Skip to content

Commit

Permalink
Remap resource and operation.name for DataDog (#28)
Browse files Browse the repository at this point in the history
* Remap resource and operation.name for DataDog

* remove .internal

* additional tests
  • Loading branch information
dvoytenko authored Feb 7, 2024
1 parent 54b98ba commit 965fce6
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/silver-points-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@vercel/otel": minor
---

DataDog resource/operation mapping
2 changes: 2 additions & 0 deletions packages/otel/src/instrumentations/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ export class FetchInstrumentation implements Instrumentation {
[SemanticAttributes.HTTP_SCHEME]: url.protocol.replace(":", ""),
[SemanticAttributes.NET_PEER_NAME]: url.hostname,
[SemanticAttributes.NET_PEER_PORT]: url.port,
"operation.name": "fetch",
"resource.name": `${req.method} ${req.url}`,
},
},
async (span) => {
Expand Down
76 changes: 74 additions & 2 deletions packages/otel/src/processor/composite-span-processor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Context } from "@opentelemetry/api";
import { TraceFlags, diag } from "@opentelemetry/api";
import type { Attributes, Context } from "@opentelemetry/api";
import { TraceFlags, diag, SpanKind } from "@opentelemetry/api";
import type {
Span,
ReadableSpan,
Expand Down Expand Up @@ -81,6 +81,11 @@ export class CompositeSpanProcessor implements SpanProcessor {
const { traceId, spanId } = span.spanContext();
const isRoot = this.rootSpanIds.get(traceId) === spanId;

const resourceAttributes = getResourceAttributes(span);
if (resourceAttributes) {
Object.assign(span.attributes, resourceAttributes);
}

for (const spanProcessor of this.processors) {
spanProcessor.onEnd(span);
}
Expand All @@ -100,3 +105,70 @@ function isSampled(traceFlags: number): boolean {
// eslint-disable-next-line no-bitwise
return (traceFlags & TraceFlags.SAMPLED) !== 0;
}

const SPAN_KIND_NAME: { [key in SpanKind]: string } = {
[SpanKind.INTERNAL]: "internal",
[SpanKind.SERVER]: "server",
[SpanKind.CLIENT]: "client",
[SpanKind.PRODUCER]: "producer",
[SpanKind.CONSUMER]: "consumer",
};

function getResourceAttributes(span: ReadableSpan): Attributes | undefined {
const { kind, attributes } = span;
const {
"operation.name": operationName,
"resouce.name": resourceName,
"span.type": spanTypeAttr,
"next.span_type": nextSpanType,
"http.method": httpMethod,
"http.route": httpRoute,
} = attributes;
if (operationName) {
return undefined;
}

// Per https://github.com/DataDog/datadog-agent/blob/main/pkg/config/config_template.yaml,
// the default operation.name is "library name + span kind".
const libraryName = span.instrumentationLibrary.name;

const spanType = nextSpanType ?? spanTypeAttr;
if (spanType && typeof spanType === "string") {
return {
"operation.name": toOperationName(libraryName, spanType),
};
}

if (
httpMethod &&
httpRoute &&
typeof httpMethod === "string" &&
typeof httpRoute === "string"
) {
return {
"operation.name": toOperationName(
libraryName,
`http.${SPAN_KIND_NAME[kind] || "internal"}.request`
),
"resource.name": resourceName ?? `${httpMethod} ${httpRoute}`,
};
}

return {
"operation.name": toOperationName(
libraryName,
kind === SpanKind.INTERNAL ? "" : SPAN_KIND_NAME[kind]
),
};
}

function toOperationName(libraryName: string, name: string): string {
if (!libraryName) {
return name;
}
let cleanLibraryName = libraryName.replace(/[ @./]/g, "_");
if (cleanLibraryName.startsWith("_")) {
cleanLibraryName = cleanLibraryName.slice(1);
}
return name ? `${cleanLibraryName}.${name}` : cleanLibraryName;
}
2 changes: 2 additions & 0 deletions tests/e2e/test/vercel-deployment/render-outbound.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ describe("vercel deployment: outbound", {}, (props) => {
"net.peer.name": "localhost",
"net.peer.port": `${bridge.port}`,
"http.status_code": 200,
"operation.name": "fetch",
"resource.name": `POST http://localhost:${bridge.port}/`,
},
},
],
Expand Down
8 changes: 7 additions & 1 deletion tests/e2e/test/vercel-deployment/render.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ describe("vercel deployment: render", {}, (props) => {
"http.status_code": 200,
"next.route": "/slugs/[slug]",
"http.route": "/slugs/[slug]",
"operation.name": "next_js.BaseServer.handleRequest",
},
spans: [
{
Expand All @@ -49,6 +50,7 @@ describe("vercel deployment: render", {}, (props) => {
"next.span_name": "render route (app) /slugs/[slug]",
"next.span_type": "AppRender.getBodyResult",
"next.route": "/slugs/[slug]",
"operation.name": "next_js.AppRender.getBodyResult",
},
spans: [
{
Expand All @@ -72,7 +74,11 @@ describe("vercel deployment: render", {}, (props) => {
{
name: "sample-span",
kind: SpanKind.INTERNAL,
attributes: { scope: "sample", foo: "bar" },
attributes: {
scope: "sample",
foo: "bar",
"operation.name": "sample",
},
spans: [],
},
],
Expand Down

0 comments on commit 965fce6

Please sign in to comment.