Skip to content

Commit f700855

Browse files
committed
fix(stdlib,server): correct Sentry sampled checks and dropping of trace events
1 parent 20ccf3a commit f700855

File tree

3 files changed

+46
-20
lines changed

3 files changed

+46
-20
lines changed

packages/server/src/middleware/log.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,8 @@ export function logMiddleware(app, options) {
111111

112112
if (span) {
113113
if (!isMatchedRoute) {
114-
// @ts-expect-error Private property?
115-
//
116114
// Discard sampled spans which don't match a route.
117-
span._sampled = false;
115+
span.setAttribute("_compas.skip-event", true);
118116
}
119117

120118
span.setStatus(

packages/stdlib/src/logger.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { environment, isProduction } from "./env.js";
33
import { AppError } from "./error.js";
44
import { isNil, isPlainObject, merge } from "./lodash.js";
55
import { loggerWriteGithubActions, loggerWritePretty } from "./log-writers.js";
6-
import { _compasSentryExport } from "./sentry.js";
6+
import { _compasSentryExport, sentrySpanIsSampled } from "./sentry.js";
77

88
/**
99
* @typedef {object} Logger
@@ -131,13 +131,14 @@ export function newLogger(options) {
131131
info: (message) => {
132132
childLogger.info({ message });
133133

134-
if (!_compasSentryExport?.getActiveSpan?.()) {
135-
// Don't add breadcrumbs if we don't have a span. This prevents unmatched logs from showing up in a random span.
134+
if (!sentrySpanIsSampled(_compasSentryExport?.getActiveSpan?.())) {
135+
// Don't add breadcrumbs if we don't have a span. This prevents unmatched logs
136+
// from showing up in a random span.
136137
return;
137138
}
138139

139140
if (!addedContextAsBreadcrumb) {
140-
_compasSentryExport.addBreadcrumb({
141+
_compasSentryExport?.addBreadcrumb({
141142
category: context.type,
142143
data: {
143144
...context,
@@ -148,7 +149,7 @@ export function newLogger(options) {
148149
addedContextAsBreadcrumb = true;
149150
}
150151

151-
_compasSentryExport.addBreadcrumb({
152+
_compasSentryExport?.addBreadcrumb({
152153
category: context.type,
153154
data: typeof message === "string" ? undefined : message,
154155
message: typeof message === "string" ? message : undefined,
@@ -159,13 +160,14 @@ export function newLogger(options) {
159160
error: (message) => {
160161
childLogger.error({ message });
161162

162-
if (!_compasSentryExport?.getActiveSpan?.()) {
163-
// Don't add breadcrumbs if we don't have a span. This prevents unmatched logs from showing up in a random span.
163+
if (!sentrySpanIsSampled(_compasSentryExport?.getActiveSpan?.())) {
164+
// Don't add breadcrumbs if we don't have a span. This prevents unmatched logs
165+
// from showing up in a random span.
164166
return;
165167
}
166168

167169
if (!addedContextAsBreadcrumb) {
168-
_compasSentryExport.addBreadcrumb({
170+
_compasSentryExport?.addBreadcrumb({
169171
category: "log",
170172
data: {
171173
...context,
@@ -176,7 +178,7 @@ export function newLogger(options) {
176178
addedContextAsBreadcrumb = true;
177179
}
178180

179-
_compasSentryExport.addBreadcrumb({
181+
_compasSentryExport?.addBreadcrumb({
180182
category: "log",
181183
data: typeof message === "string" ? undefined : message,
182184
message: typeof message === "string" ? message : undefined,

packages/stdlib/src/sentry.js

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* The Sentry version that all Compas packages use if set via {@link compasWithSentry}.
33
*
4-
* @type {undefined|import("@sentry/node")}
4+
* @type {undefined|typeof import("@sentry/node")}
55
*/
66
export let _compasSentryExport = undefined;
77

@@ -16,35 +16,61 @@ export let _compasSentryEnableQuerySpans = false;
1616
* Enable Sentry support. This comes with the following changes:
1717
*
1818
* Stdlib:
19-
* - Logger: both info and error logs are added as breadcrumbs to the current active span.
19+
* - Logger: both info and error logs are added as breadcrumbs to the current active
20+
* span.
2021
* - Event: Events are propagated to Sentry as (inactive) spans.
2122
* Meaning that further logs are not necessarily correlated to the correct event.
2223
* The final event callstack is not logged.
2324
*
2425
* Server:
2526
* - Starts a new root span for each incoming request.
2627
* - Tries to name it based on the finalized name of `ctx.event`.
27-
* This is most likely in the format `router.foo.bar` for matched routes by the generated router.
28+
* This is most likely in the format `router.foo.bar` for matched routes by the
29+
* generated router.
2830
* - Uses the sentry-trace header when provided.
2931
* Note that if a custom list of `allowHeaders` is provided in the CORS options,
3032
* 'sentry-trace' and 'baggage' should be allowed as well.
31-
* - If the error handler retrieves an unknown or AppError.serverError, it is reported as an uncaught exception.
32-
* It is advised to set 'normalizeDepth' to '0' in your Sentry config, and to enable the 'extraErrorDataIntegration' integration.
33+
* - If the error handler retrieves an unknown or AppError.serverError, it is reported as
34+
* an uncaught exception. It is advised to set 'normalizeDepth' to '0' in your Sentry
35+
* config, and to enable the 'extraErrorDataIntegration' integration.
3336
*
3437
* Store:
3538
* - Starts a new root span for each handled Job in the QueueWorker
36-
* The span name is based on the job name. Unhandled errors are captured as exceptions.
37-
* - Supports passing queries to Sentry as spans. Requires {@link opts.sendQueriesAsSpans} to be set.
39+
* The span name is based on the job name. Unhandled errors are captured as
40+
* exceptions.
41+
* - Supports passing queries to Sentry as spans. Requires {@link
42+
* opts.sendQueriesAsSpans} to be set.
3843
*
3944
* All:
4045
* - All error logs in Compas package code are captured as exceptions.
4146
*
42-
* @param {import("@sentry/node")} instance
47+
* @param {typeof import("@sentry/node")} instance
4348
* @param {{
4449
* sendQueriesAsSpans?: boolean
4550
* }} [opts]
4651
*/
4752
export function compasWithSentry(instance, { sendQueriesAsSpans } = {}) {
4853
_compasSentryExport = instance;
4954
_compasSentryEnableQuerySpans = sendQueriesAsSpans ?? false;
55+
56+
_compasSentryExport.addEventProcessor((event) => {
57+
if (event.spans?.some?.((it) => it.data?.["_compas.skip-event"])) {
58+
return null;
59+
}
60+
61+
return event;
62+
});
63+
}
64+
65+
/**
66+
* @see https://github.com/getsentry/sentry-javascript/blob/8bec42e0285ee301e8fc9bcaf02046daf48e0495/packages/core/src/utils/spanUtils.ts#L103
67+
*/
68+
export function sentrySpanIsSampled(span) {
69+
if (!span) {
70+
return false;
71+
}
72+
73+
const { traceFlags } = span.spanContext();
74+
75+
return Boolean(traceFlags & 0x1);
5076
}

0 commit comments

Comments
 (0)