Skip to content

Commit a18b637

Browse files
Setup OTEL with Sentry for AvaTax (#1801)
1 parent ac32ce6 commit a18b637

File tree

7 files changed

+105
-22
lines changed

7 files changed

+105
-22
lines changed

.changeset/fuzzy-cougars-grin.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"saleor-app-avatax": patch
3+
---
4+
5+
Setup Sentry integration with our OTEL setup.

apps/avatax/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"@saleor/webhook-utils": "workspace:*",
4848
"@sentry/cli": "catalog:",
4949
"@sentry/nextjs": "catalog:",
50+
"@sentry/opentelemetry": "9.8.0",
5051
"@t3-oss/env-nextjs": "0.11.1",
5152
"@trpc/client": "catalog:",
5253
"@trpc/next": "catalog:",

apps/avatax/src/env.ts

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const env = createEnv({
4040
OTEL_ACCESS_TOKEN: z.string().optional(),
4141
VERCEL_ENV: z.string().optional(),
4242
REPOSITORY_URL: z.string().optional(),
43+
OTEL_TRACES_SAMPLER_ARG: z.coerce.number().min(0).max(1).optional().default(1),
4344
},
4445
shared: {
4546
NODE_ENV: z.enum(["development", "production", "test"]),
@@ -78,6 +79,7 @@ export const env = createEnv({
7879
OTEL_ACCESS_TOKEN: process.env.OTEL_ACCESS_TOKEN,
7980
VERCEL_ENV: process.env.VERCEL_ENV,
8081
REPOSITORY_URL: process.env.REPOSITORY_URL,
82+
OTEL_TRACES_SAMPLER_ARG: process.env.OTEL_TRACES_SAMPLER_ARG,
8183
},
8284
isServer: typeof window === "undefined" || process.env.NODE_ENV === "test",
8385
});

apps/avatax/src/instrumentation.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// Use `process.env` here to avoid broken Next.js build
22

33
export async function register() {
4-
if (process.env.NEXT_RUNTIME === "nodejs" && process.env.OTEL_ENABLED === "true") {
4+
if (
5+
process.env.NEXT_RUNTIME === "nodejs" &&
6+
process.env.OTEL_ENABLED === "true" &&
7+
process.env.NEXT_PUBLIC_SENTRY_DSN
8+
) {
59
await import("./instrumentations/otel-node");
610
}
711

8-
if (process.env.NEXT_RUNTIME === "nodejs" && process.env.NEXT_PUBLIC_SENTRY_DSN) {
9-
await import("./instrumentations/sentry-node");
10-
}
11-
1212
if (process.env.NEXT_RUNTIME === "edge" && process.env.NEXT_PUBLIC_SENTRY_DSN) {
1313
await import("./instrumentations/sentry-edge");
1414
}

apps/avatax/src/instrumentations/otel-node.ts

+60
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,70 @@
1+
/* eslint-disable max-params */
2+
import { Context, Link, SpanAttributes, SpanKind } from "@opentelemetry/api";
3+
import { ParentBasedSampler, TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-node";
14
import { ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
25
import { ATTR_DEPLOYMENT_ENVIRONMENT_NAME } from "@opentelemetry/semantic-conventions/incubating";
36
import { createAwsInstrumentation } from "@saleor/apps-otel/src/aws-instrumentation-factory";
47
import { createBatchSpanProcessor } from "@saleor/apps-otel/src/batch-span-processor-factory";
58
import { createHttpInstrumentation } from "@saleor/apps-otel/src/http-instrumentation-factory";
69
import { ObservabilityAttributes } from "@saleor/apps-otel/src/observability-attributes";
10+
import * as Sentry from "@sentry/nextjs";
11+
import { SentryPropagator, wrapSamplingDecision } from "@sentry/opentelemetry";
712
import { registerOTel } from "@vercel/otel";
813

914
import { env } from "@/env";
15+
import { createLogger } from "@/logger";
1016

1117
import pkg from "../../package.json";
1218

19+
const logger = createLogger("instrumentations/otel-node");
20+
21+
// Use ParentBasedSampler to make decision and then wrap it with Sentry wrapSamplingDecision
22+
class AppSampler extends ParentBasedSampler {
23+
shouldSample(
24+
context: Context,
25+
traceId: string,
26+
spanName: string,
27+
spanKind: SpanKind,
28+
attributes: SpanAttributes,
29+
links: Link[],
30+
) {
31+
const { decision } = super.shouldSample(
32+
context,
33+
traceId,
34+
spanName,
35+
spanKind,
36+
attributes,
37+
links,
38+
);
39+
40+
logger.info("AppSampler.shouldSample.decision", { decision });
41+
42+
return wrapSamplingDecision({
43+
decision,
44+
context,
45+
spanAttributes: attributes,
46+
});
47+
}
48+
toString() {
49+
return "AppSampler";
50+
}
51+
}
52+
53+
Sentry.init({
54+
dsn: env.NEXT_PUBLIC_SENTRY_DSN,
55+
environment: env.ENV,
56+
includeLocalVariables: true,
57+
skipOpenTelemetrySetup: true,
58+
registerEsmLoaderHooks: false,
59+
integrations: [
60+
Sentry.localVariablesIntegration({
61+
captureAllExceptions: true,
62+
}),
63+
Sentry.extraErrorDataIntegration(),
64+
Sentry.httpIntegration({ spans: false }),
65+
],
66+
});
67+
1368
registerOTel({
1469
serviceName: env.OTEL_SERVICE_NAME,
1570
attributes: {
@@ -27,4 +82,9 @@ registerOTel({
2782
}),
2883
],
2984
instrumentations: [createAwsInstrumentation(), createHttpInstrumentation()],
85+
traceSampler: new AppSampler({
86+
root: new TraceIdRatioBasedSampler(env.OTEL_TRACES_SAMPLER_ARG),
87+
}),
88+
propagators: [new SentryPropagator()],
89+
contextManager: new Sentry.SentryContextManager(),
3090
});

apps/avatax/src/instrumentations/sentry-node.ts

-17
This file was deleted.

pnpm-lock.yaml

+32
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)