Readme

Opentelemetry Tracing for Vals!

Enables Opentelemetry tracing for vals. Exports two functions, init and traced_handler. init will set up opentelemetry.

For how to use it see this example val.

By default, traces log to the console but if you set HONEYCOMB_API_KEY it'll also push the traces to honeycomb. In the future we can add more export targets for other services. I'm also thinking about making a val to display them.

traced_handler is a wrapper for your handler you can use on an HTTP val. Just pass it your HTTP function and export it as the default and it'll trace every request.

Here's a screenshot of what the honeycomb view of a trace from my connect playing val looks like.

CleanShot 2023-12-22 at 11.51.13@2x.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import "https://deno.land/x/xhr@0.1.0/mod.ts";
import "node:async_hooks";
import { context, propagation, SpanStatusCode, trace } from "https://cdn.skypack.dev/@opentelemetry/api";
import { OTLPTraceExporter } from "https://cdn.skypack.dev/@opentelemetry/exporter-trace-otlp-http";
import { B3Propagator } from "https://cdn.skypack.dev/@opentelemetry/propagator-b3";
import { Resource } from "https://cdn.skypack.dev/@opentelemetry/resources";
import {
ConsoleSpanExporter,
SimpleSpanProcessor,
WebTracerProvider,
} from "https://cdn.skypack.dev/@opentelemetry/sdk-trace-web";
import { SemanticResourceAttributes } from "https://cdn.skypack.dev/@opentelemetry/semantic-conventions";
import { AsyncLocalStorageContextManager } from "npm:@opentelemetry/context-async-hooks";
export function get_tracer() {
return trace.getTracer("valtown");
}
/**
* Initializes opentelemetry tracing.
*/
export function init(service_name: string, console_log: bool = false): undefined {
const provider = new WebTracerProvider({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: service_name,
}),
});
// Log traces to console if console_log is set.
if (console_log) {
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
}
// Send traces to honeycomb if HONEYCOMB_API_KEY key is set.
const honeycomb_api_key = Deno.env.get("HONEYCOMB_API_KEY");
if (honeycomb_api_key) {
provider.addSpanProcessor(
new SimpleSpanProcessor(
new OTLPTraceExporter({
url: "https://api.honeycomb.io/v1/traces",
headers: {
"x-honeycomb-team": honeycomb_api_key,
},
}),
),
);
}
provider.register({
contextManager: new AsyncLocalStorageContextManager(),
propagator: new B3Propagator(),
});
}
/**
* Wrapper for fetch that traces the request. It also passes a propagation header
* so if you are calling another val that uses traced_handler their traces will
* be tied together.
*/
export async function traced_fetch(
input: string | URL,
init?: RequestInit,
): Promise<Response> {
return await get_tracer().startActiveSpan(`fetch`, async (span) => {
const prop_output: { b3: string } = { b3: "" };
propagation.inject(context.active(), prop_output);
try {
const resp: Response = await fetch(input, {
...init,
headers: {
b3: prop_output.b3,
...(init?.headers ?? {}),
},
});
span.setAttributes({
"http.url": resp.url,
"response.status_code": resp.status,
});
if (resp.ok && resp.status >= 200 && resp.status < 400) {
span.setStatus({ code: SpanStatusCode.OK });
} else {
span.setStatus({
code: SpanStatusCode.ERROR,
message: await resp.clone().text(),
});
}
return resp;
} catch (error) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message,
});
throw new Error(error);
} finally {
span.end();
}
});
}
/**
* Wrapper to trace a function.
👆 This is a val. Vals are TypeScript snippets of code, written in the browser and run on our servers. Create scheduled functions, email yourself, and persist small pieces of data — all from the browser.