opentelemetry-js
opentelemetry-js copied to clipboard
Deno Support
Are there any plans for supporting Deno runtime? Perhaps it is a bit early to speak of it, however it seems rather possible due to low number of dependencies for opentelemetry implementation. Deno tries to support browser APIs so providing support might be rather easy.
It's already possible to import some opentelemetry-js libs via skypack, however using require (for instance, for require('lodash.merge')
in opentelemetry/tracing and opentelemetry/metrics, opentelemetry/metrics 2 makes it impossible.
Overall seems to be not so many requires:
> grep -r 'require(' packages/**/src/*
packages/opentelemetry-exporter-collector-grpc/src/CollectorExporterNodeBase.ts: const { onInit } = require('./util');
packages/opentelemetry-exporter-collector-grpc/src/CollectorExporterNodeBase.ts: const { send } = require('./util');
packages/opentelemetry-exporter-collector-proto/src/CollectorExporterNodeBase.ts: const { onInit } = require('./util');
packages/opentelemetry-exporter-collector-proto/src/CollectorExporterNodeBase.ts: const { send } = require('./util');
packages/opentelemetry-exporter-jaeger/src/types.ts:export const UDPSender = require('jaeger-client/dist/src/reporters/udp_sender')
packages/opentelemetry-exporter-jaeger/src/types.ts:export const Utils = require('jaeger-client/dist/src/util').default;
packages/opentelemetry-exporter-jaeger/src/types.ts:export const ThriftUtils = require('jaeger-client/dist/src/thrift').default;
packages/opentelemetry-exporter-jaeger/src/types.ts:export const HTTPSender = require('jaeger-client/dist/src/reporters/http_sender')
packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts: const version = require(path.join(baseDir, 'package.json')).version;
packages/opentelemetry-metrics/src/Meter.ts:const merge = require('lodash.merge');
packages/opentelemetry-metrics/src/MeterProvider.ts:const merge = require('lodash.merge');
packages/opentelemetry-semantic-conventions/build/src/index.js:__exportStar(require("./trace"), exports);
packages/opentelemetry-semantic-conventions/build/src/index.js:__exportStar(require("./resource"), exports);
packages/opentelemetry-semantic-conventions/build/src/resource/index.js:__exportStar(require("./ResourceAttributes"), exports);
packages/opentelemetry-semantic-conventions/build/src/trace/index.js:__exportStar(require("./SemanticAttributes"), exports);
However, it demands more effort due to testing in Deno runtime and validating not using any Node-specific APIs.
Do you have any plans or thoughts at the moment?
Thanks!
Another thing to be aware of for deno support is the use of XHR. Since deno does not support XHR, when trying to use opentelemetry/metrics via jspm, an error is thrown when creating the request
error: Uncaught (in promise) ReferenceError: XMLHttpRequest is not defined
const xhr = new XMLHttpRequest();
This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 14 days.
Assigning myself as I'm working on non-main-frame-context support on browsers.
Fwiw I tried taking a stab at adapting one of the browser console exporter examples for Deno Deploy to see how far I could get, and I was actually able to make it through to the end with a minimum of monkey patching.
Here's the Deno Deploy code I used. All of the hacks are just below the imports (the last one was also to get around this Deno Deploy issue)
import { serve } from "https://deno.land/[email protected]/http/server.ts";
import { context, trace } from 'https://cdn.skypack.dev/@opentelemetry/api';
import { ConsoleSpanExporter, SimpleSpanProcessor } from 'https://cdn.skypack.dev/@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from 'https://cdn.skypack.dev/@opentelemetry/exporter-trace-otlp-http';
import { WebTracerProvider } from 'https://cdn.skypack.dev/@opentelemetry/sdk-trace-web';
import { FetchInstrumentation } from 'https://cdn.skypack.dev/@opentelemetry/instrumentation-fetch';
import { ZoneContextManager } from 'https://cdn.skypack.dev/@opentelemetry/context-zone';
import { B3Propagator } from 'https://cdn.skypack.dev/@opentelemetry/propagator-b3';
import { registerInstrumentations } from 'https://cdn.skypack.dev/@opentelemetry/instrumentation';
//Hacks - start
globalThis.document = {
createElement: function() { //For this line - https://github.com/open-telemetry/opentelemetry-js/blob/bdb61f7e56b7fbe7d281262e69e5bc8683a52014/packages/opentelemetry-sdk-trace-web/src/utils.ts#L33
return {
protocol: ":", //For this line - https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-xml-http-request/src/xhr.ts#L170
};
}
};
globalThis.location = {}; //For this line - https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-sdk-trace-web/src/utils.ts#L424
performance.clearResourceTimings = function() {}; //For this line and Deno Deploy bug - https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts#L181
//Hacks - end
const provider = new WebTracerProvider();
// Note: For production consider using the "BatchSpanProcessor" to reduce the number of requests
// to your exporter. Using the SimpleSpanProcessor here as it sends the spans immediately to the
// exporter without delay
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
provider.addSpanProcessor(new SimpleSpanProcessor(new OTLPTraceExporter()));
provider.register({
contextManager: new ZoneContextManager(),
propagator: new B3Propagator(),
});
registerInstrumentations({
instrumentations: [
new FetchInstrumentation({
ignoreUrls: [/localhost:8090\/sockjs-node/],
propagateTraceHeaderCorsUrls: [
'https://cors-test.appspot.com/test',
'https://httpbin.org/get',
],
clearTimingResources: true,
}),
],
});
const webTracerWithZone = provider.getTracer('example-tracer-web');
async function handler(req: Request): Response {
const singleSpan = webTracerWithZone.startSpan('files-series-info');
context.with(trace.setSpan(context.active(), singleSpan), () => {
fetch("https://httpbin.org/get").then((_data) => {
trace.getSpan(context.active()).addEvent('fetching-single-span-completed');
singleSpan.end();
});
});
// await fetch("https://httpbin.org/get");
return new Response("Hello world");
}
console.log("Listening on http://localhost:8000");
await serve(handler);
And here was the console output
{
traceId: "f720f78470dbc13cd78b90187533bbec",
parentId: "9244d1764e81faf1",
name: "HTTP GET",
id: "5649f9695859d613",
kind: 2,
timestamp: NaN,
duration: NaN,
attributes: {
component: "fetch",
"http.method": "GET",
"http.url": "https://httpbin.org/get",
"http.status_code": 200,
"http.status_text": "OK",
"http.scheme": ""
},
status: { code: 0 },
events: []
}
And I haven't tried this (yet), but I wonder if a polyfill for XmlHttpRequest for Deno (like this one from deno.land) could be enough to support a normal exporter before standard support arrives.
I think the main thing that will be missing for correct deno support is a correct context manager, the zone start to fail as soon as you use async/await (in your example you use promises so it works fine). For this someone will need to take a shot at https://github.com/denoland/deno/issues/7010
I think the main thing that will be missing for correct deno support is a correct context manager, the zone start to fail as soon as you use async/await (in your example you use promises so it works fine). For this someone will need to take a shot at denoland/deno#7010
(Been trying to read around and unpack your insight after confirming it didn't work with async/await...trying to summarize my understanding below)
Am I correct in understanding that browsers today (and maybe for the foreseeable future) don't offer a way to maintain context across native async/await, and so there's no way to update zone.js (or any userland approximation of zones/other constructs that can keep track of timings/contexts across async operations/ticks) to take them into account, so no usage of otel-js (or any similar tooling) will be able to work with them?
So the workaround for browsers and Deno for now is to avoid native async/await (either by not using it or transpilation). Is that accurate? (or is there some nuance for browsers I'm maybe missing?)
(Fwiw it was this github issue for zone.js that helped crystallize things for me a bit more)
Am I correct in understanding that browsers today (and maybe for the foreseeable future) don't offer a way to maintain context across native async/await, and so there's no way to update zone.js (or any userland approximation of zones/other constructs that can keep track of timings/contexts across async operations/ticks) to take them into account, so no usage of otel-js (or any similar tooling) will be able to work with them?
Exactly, this is a known issue and i believe the general agreement is to have a system added into EMCAScript but no one had the time/resource to push it through. The last one was actually @legendecas (https://github.com/legendecas/proposal-async-context)
So the workaround for browsers and Deno for now is to avoid native async/await (either by not using it or transpilation). Is that accurate? (or is there some nuance for browsers I'm maybe missing?)
Currently yes, but from the few discussions i have seen in Deno there is interested from the core team for diagostics tooling and context management is the most important thing in event loop runtimes (there is more info in a PR that someone from MS pulled up there: https://github.com/denoland/deno/pull/8209)
It doesn't address the main context management blocker, but in case it's helpful I made a small polyfill for navigator.sendBeacon in Deno, as I noticed that was missing and it was either going to be polyfilling that or XmlHttpRequest in order to get the browser HTTP/JSON exporter to work there.
With that I was able to get a full flow through Deno working, but in a very simple case avoiding the native async/await and zone issues discussed above.
Worth noting that a second attempt at promise hooks might be landing soon: https://github.com/denoland/deno/pull/15475
promise hooks did land in deno 1.26