Not another dashboard course. The code you write to emit telemetry worth having — span kinds, queues, cardinality, and the browser half nobody instruments.
Your API p99 is eighty milliseconds and a user just waited three seconds. Your dashboard is green and nobody can tell you which service was slow. This course is about the code that closes that gap, and it starts from the only useful question: what were you actually asked during your last three incidents, and what would have answered it. From there you write real instrumentation. Span kinds, because a backend builds your service map out of them and the default claims there is no remote party. Span events, because a retry has a timestamp and no duration. Span status, because whatever you mark ERROR becomes your error rate, and a 404 is usually the system working. The traceparent header byte by byte, then the step everyone forgets — extracting a context and never making it current, which silently splits every trace in two. Then the parts most services never instrument at all: carrying trace context through Kafka, SQS and Celery so the half of your system that runs later stops being dark; measuring the queue time that falls between two traces and is usually the real latency; OpenTelemetry's metrics API, where the histogram buckets you inherited are tuned for seconds and your cache answers in microseconds, and exemplars turn a p99 spike into one click to the slow trace. A whole module on cardinality, because four reasonable labels multiply into twenty million series and the ingester is OOM-killed during the incident that label was added to debug. Logs that carry a trace ID, so a hundred million lines become eleven. Then the browser, which almost nobody instruments and where the missing seconds usually are: Core Web Vitals as real-user telemetry with attribution that names the element rather than a score, error capture across all four escape hatches, source maps keyed to a release, and stitching a browser span onto the server trace so one waterfall runs from the click to the database — including the CORS header that stops you and the reason it is not a tracing bug. Then the Collector, where redaction, tail sampling and routing live as configuration you can change in minutes instead of a fleet deploy. It ends with the migration: auditing who reads your logs before you delete any, running both paths until parity is proven, and knowing when you are done — which is not a coverage percentage. Python and Node throughout, TypeScript in the browser, every technique verifiable on localhost with no vendor account.
Built by Lakshya Kumar
Paste this into any AI chat. Fill in the bracketed parts with your context — you'll get back a straight answer on whether this belongs on your plate.
We grant free access case-by-case — students, career-switchers, builders on a tight budget. Sign in to send us a note.
Sign in to applyFinished the tasks? Take the prompt to your AI and get tested on it. We copy the prompt and open the app — just paste it in.
Span kind decides how backends compute service graphs. Events record moments. Status decides what counts as an error.
One 55-character header is what turns per-service spans into one distributed trace. Read it byte by byte, then carry it correctly.
A message is a carrier. Put the context in it, link rather than parent, and stop losing the half of your system that runs later.
Six instrument types, one aggregation pipeline you can reshape without touching code, and exemplars that jump to a trace.
Series count is the product of every label's distinct values. Multiplication is why one innocuous label ends a quarter's budget.
Inject trace_id into every log record and the pivot between 'which request' and 'what the code decided' becomes one click.
Lab tools measure one fast laptop. Real users are on cheap Androids on bad networks, and only they can tell you the truth.
A minified stack trace is useless and a swallowed rejection is invisible. Capture everything, then symbolicate it.
One trace from the click to the database. The browser becomes the root span, and CORS is the thing that stops you.
Receivers, processors, exporters and the tail sampler — the one place you can change telemetry behaviour without a deploy.
A staged plan for a real codebase: audit, pick boundaries, strangle service by service, prove parity, delete the old path.
I'm taking "Instrument Your Stack: Traces from Browser to Database" — a telemetry instrumentation course in the Engineering track. Twelve modules: signal design, manual spans (kinds/events/status), context propagation, queues and async boundaries, the OTel metrics API, cardinality, logs joined to traces, browser Web Vitals as real-user telemetry, browser error capture and source maps, stitching browser spans to server traces, the Collector (pipelines, OTTL, tail sampling), and migrating a codebase off ad-hoc logging. My context: 1. My stack and languages: [describe] 2. What telemetry exists today: [nothing / logs only / a vendor APM / partial OTel] 3. Whether I have a front end I control: [yes/no, framework] 4. Whether anything crosses a queue: [describe] 5. My monthly telemetry spend, if I know it: [number] 6. The last incident I could not diagnose quickly: [describe] Given that, answer: - Which module should I start with, given what already exists? Skipping ahead is fine if the earlier ground is covered. - Which parts of this course do NOT apply to my situation, and why? - For my last undiagnosable incident, which specific signal and attribute would have answered it? - What is the smallest change that would most improve my ability to diagnose the next one?
Short and worth reading in full before module 3. The processing-model section is the part people skip.