Skip to content
22 changes: 22 additions & 0 deletions docs/latest/advanced/opentelemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,25 @@ deno task start

Open `http://localhost:16686` to browse traces. You'll see each request broken
down into its middleware, handler, and rendering spans.

## Client-side trace correlation

When an OpenTelemetry exporter is active, Fresh automatically injects a
[W3C Trace Context](https://www.w3.org/TR/trace-context/) `<meta>` tag into the
`<head>` of every rendered page:

```html
<head>
<meta
name="traceparent"
content="00-ab42124a3c573678d4d8b21ba52df3bf-d21f7bc17caa5aba-01"
>
<!-- ... -->
</head>
```

This allows client-side OpenTelemetry instrumentation (such as
[`@opentelemetry/instrumentation-document-load`](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/c9d62be989802534c01373e8ab41e13747d7ee3e/packages/instrumentation-document-load))
to link browser performance traces back to the server-side span that rendered
the page, giving you end-to-end visibility from server rendering through page
load.
17 changes: 17 additions & 0 deletions packages/fresh/src/runtime/server/preact_hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { getCodeFrame } from "../../dev/middlewares/error_overlay/code_frame.ts"
import { escapeScript } from "../../utils.ts";
import { HeadContext } from "../head.ts";
import { useContext } from "preact/hooks";
import { isSpanContextValid, trace } from "@opentelemetry/api";

interface InternalPreactOptions extends PreactOptions {
[OptionsType.ATTR](name: string, value: unknown): string | void;
Expand Down Expand Up @@ -282,6 +283,22 @@ options[OptionsType.DIFF] = (vnode) => {
}
}

// Inject W3C traceparent meta tag when OpenTelemetry is active,
// enabling client-side tracing to connect to the server span.
const activeSpan = trace.getActiveSpan();
if (activeSpan) {
const spanCtx = activeSpan.spanContext();
if (isSpanContextValid(spanCtx)) {
const flags = (spanCtx.traceFlags & 1) ? "01" : "00";
const traceparent =
`00-${spanCtx.traceId}-${spanCtx.spanId}-${flags}`;
items.push(
// deno-lint-ignore no-explicit-any
h("meta", { name: "traceparent", content: traceparent }) as any,
);
}
}

// deno-lint-ignore no-explicit-any
items.push(h(RemainingHead, null) as VNode<any>);

Expand Down
Loading