Punk::OpenTelemetry example: an app, and something to watch it

Two servers. One is an ordinary Punk application with the plugin turned on; the other is a collector that accepts what the exporter sends and prints it to STDERR as a trace tree, so you can watch telemetry arrive rather than take a vendor's word for it.

Run it

Two terminals, from the distribution root.

The collector, on the port the OTLP spec uses for HTTP:

plackup -s Hyperman -p 4318 example/collector.psgi

The application, pointed at it:

OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318 \
OTEL_EXPORTER_OTLP_PROTOCOL=http/json \
OTEL_BSP_SCHEDULE_DELAY=1000 \
  plackup -s Hyperman -p 5000 example/app.psgi

Then make some requests:

curl localhost:5000/hello/world
curl localhost:5000/work          # calls out, so you get a client span too
curl localhost:5000/slow
curl localhost:5000/boom          # dies on purpose; the span still arrives

Within a second or so the collector prints what it received:

=== punk-otel-example  instance=c89d9f58  schema=https://opentelemetry.io/schemas/1.30.0
  scope: Punk::OpenTelemetry 0.02
    GET /hello/:name             server      4.17ms  trace=0726f412 span=52eb67ff
        client.address = 127.0.0.1
        http.request.method = GET
        http.response.status_code = 200
        http.route = /hello/:name
        url.path = /hello/world
      greet                      internal    0.04ms  trace=0726f412 span=6c4d6ed5
        greeting.name = world
        * composed

The three environment variables

OTEL_EXPORTER_OTLP_PROTOCOL=http/json is what makes the collector readable. JSON is three to five times larger on the wire than the protobuf default, so it is the demo's choice and not the deployment's - which is the reason the JSON transport exists at all: you can read it in a bug report.

OTEL_BSP_SCHEDULE_DELAY=1000 drops the batch delay to a second, so the demo does not look broken while you wait out the five-second default.

OTEL_EXPORTER_OTLP_ENDPOINT is left to the environment on purpose. The application declares its service name in code, where it belongs, and takes its destination from the deployment, where that belongs. Point it at a real collector or a vendor and nothing in app.psgi changes.

What the application does not contain

Any export code. The plugin builds the tracer and the exporter and schedules the drain between them; the application says plugin 'OpenTelemetry' and then writes routes. That is the point of the plugin, and the part most easily got wrong by hand - a drain on the tail of a response ships every server span one request late, because the server span is ended by the response observer that runs after after_dispatch.

Turning it off

OTEL_SDK_DISABLED=true plackup -s Hyperman -p 5000 example/app.psgi

No tracer, no exporter, nothing installed into the request path. The boot line says so, which is the one thing you want to see when you have just switched telemetry off at three in the morning and need to know whether it took.

This is a demonstration

The collector accepts everything, stores nothing and authenticates nobody. It exists to make the wire format visible.