How do I integrate NextJS' OpenTelemetry support with .Net Aspire? #5304
-
I've tried implementing support for OpenTelemetry w/ NextJS using .Net Aspire. I spent a fair number of hours yesterday looking through NextJS' docs (https://nextjs.org/docs/app/building-your-application/optimizing/open-telemetry) as well as yours (https://github.com/dotnet/aspire-samples/tree/b013f64e063dce2a4c72b840eab83d43991a5d22/samples/AspireWithNode), but no matter what I do, I am unable to see anything pop up in the There are so many moving pieces that it's near-impossible to figure out what is going from; All the environment variables are there, and according to NextJS' docs, this is all you should have to do to follow the standard systems: import { registerOTel } from '@vercel/otel';
// The following function does get called by NextJS, according to the console.
export function register() {
console.log('service name:', process.env.OTEL_SERVICE_NAME); // This is the right string
console.log('endpoint:', process.env.OTEL_EXPORTER_OTLP_ENDPOINT); // This seems to be the right string.
registerOTel(); // This is supposed to use these env. variables, according to NextJS' docs.
console.log('Registered OTel'); // This gets called.
} This is my Aspire code: var builder = DistributedApplication.CreateBuilder(args);
builder
.AddNpmApp(
"Frontend",
"<PATH>",
"dev"
)
.WithOtlpExporter()
.WithHttpEndpoint(env: "PORT", port: 3000)
.WithExternalHttpEndpoints();
var app = builder.Build();
await app.RunAsync().ConfigureAwait(false); As you can see in this image, the traces selector doesn't even include the service. ![]() |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 34 replies
-
You might need to make the default OTLP endpoint http based to make these scenarios work more seamlessly. |
Beta Was this translation helpful? Give feedback.
-
I have the same issue was @oising, when using the javascript exporter you get a import { BatchSpanProcessor, WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request/build/src/xhr';
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction';
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import { resourceFromAttributes, type Resource } from '@opentelemetry/resources';
const endpoint = import.meta.env.OTEL_EXPORTER_OTLP_ENDPOINT;
const serviceName = import.meta.env.OTEL_SERVICE_NAME;
const attributes = {
[ATTR_SERVICE_NAME]: serviceName,
...parseDelimitedValues(import.meta.env.OTEL_RESOURCE_ATTRIBUTES)
}
const headers = {
"Content-Type": 'application/json',
...parseDelimitedValues(import.meta.env.OTEL_EXPORTER_OTLP_HEADERS)
};
const exporter = new OTLPTraceExporter({
url: `${endpoint}/v1/traces`,
headers: headers,
concurrencyLimit: 10,
timeoutMillis: 30000,
});
const provider = new WebTracerProvider({
resource: resourceFromAttributes(attributes),
spanProcessors: [
new BatchSpanProcessor(exporter, {
maxQueueSize: 100,
maxExportBatchSize: 10,
scheduledDelayMillis: 500,
exportTimeoutMillis: 30000,
})]
});
provider.register({
// Changing default contextManager to use ZoneContextManager - supports asynchronous operations - optional
contextManager: new ZoneContextManager(),
});
registerInstrumentations({
instrumentations: [
new UserInteractionInstrumentation(),
new DocumentLoadInstrumentation(),
new XMLHttpRequestInstrumentation()
],
});
function parseDelimitedValues(s:string) {
const headers:string[] = s.split(','); // Split by comma
const o:Record<string, string> = {};
headers.forEach(header => {
const [key, value] = header.split('='); // Split by equal sign
o[key.trim()] = value.trim(); // Add to the object, trimming spaces
});
return o;
} |
Beta Was this translation helpful? Give feedback.
This is my actual code: