Skip to content

Commit 2f6d5c4

Browse files
s1gr1dcadesalaberry
authored andcommitted
feat(otel): Do not sample options and head requests (getsentry#11467)
1 parent f1e76b5 commit 2f6d5c4

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

packages/opentelemetry/src/sampler.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ import type { Client, SpanAttributes } from '@sentry/types';
88
import { logger } from '@sentry/utils';
99
import { SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING } from './constants';
1010

11+
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
1112
import { DEBUG_BUILD } from './debug-build';
1213
import { getPropagationContextFromSpan } from './propagator';
1314
import { getSamplingDecision } from './utils/getSamplingDecision';
1415
import { setIsSetup } from './utils/setupCheck';
1516

1617
/**
17-
* A custom OTEL sampler that uses Sentry sampling rates to make it's decision
18+
* A custom OTEL sampler that uses Sentry sampling rates to make its decision
1819
*/
1920
export class SentrySampler implements Sampler {
2021
private _client: Client;
@@ -72,6 +73,16 @@ export class SentrySampler implements Sampler {
7273
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: sampleRate,
7374
};
7475

76+
const method = `${spanAttributes[SemanticAttributes.HTTP_METHOD]}`.toUpperCase();
77+
if (method === 'OPTIONS' || method === 'HEAD') {
78+
DEBUG_BUILD && logger.log(`[Tracing] Not sampling span because HTTP method is '${method}' for ${spanName}`);
79+
return {
80+
decision: SamplingDecision.NOT_RECORD,
81+
attributes,
82+
traceState: traceState.set(SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING, '1'),
83+
};
84+
}
85+
7586
if (!sampled) {
7687
return {
7788
decision: SamplingDecision.NOT_RECORD,

packages/opentelemetry/test/trace.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
import type { Event, Scope } from '@sentry/types';
2121
import { makeTraceState } from '../src/propagator';
2222

23+
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
2324
import { continueTrace, startInactiveSpan, startSpan, startSpanManual } from '../src/trace';
2425
import type { AbstractSpan } from '../src/types';
2526
import { getDynamicSamplingContextFromSpan } from '../src/utils/dynamicSamplingContext';
@@ -1358,6 +1359,76 @@ describe('trace (sampling)', () => {
13581359
});
13591360
});
13601361

1362+
describe('HTTP methods (sampling)', () => {
1363+
beforeEach(() => {
1364+
mockSdkInit({ enableTracing: true });
1365+
});
1366+
1367+
afterEach(() => {
1368+
cleanupOtel();
1369+
});
1370+
1371+
it('does sample when HTTP method is other than OPTIONS or HEAD', () => {
1372+
const spanGET = startSpanManual(
1373+
{ name: 'test span', attributes: { [SemanticAttributes.HTTP_METHOD]: 'GET' } },
1374+
span => {
1375+
return span;
1376+
},
1377+
);
1378+
expect(spanIsSampled(spanGET)).toBe(true);
1379+
expect(getSamplingDecision(spanGET.spanContext())).toBe(true);
1380+
1381+
const spanPOST = startSpanManual(
1382+
{ name: 'test span', attributes: { [SemanticAttributes.HTTP_METHOD]: 'POST' } },
1383+
span => {
1384+
return span;
1385+
},
1386+
);
1387+
expect(spanIsSampled(spanPOST)).toBe(true);
1388+
expect(getSamplingDecision(spanPOST.spanContext())).toBe(true);
1389+
1390+
const spanPUT = startSpanManual(
1391+
{ name: 'test span', attributes: { [SemanticAttributes.HTTP_METHOD]: 'PUT' } },
1392+
span => {
1393+
return span;
1394+
},
1395+
);
1396+
expect(spanIsSampled(spanPUT)).toBe(true);
1397+
expect(getSamplingDecision(spanPUT.spanContext())).toBe(true);
1398+
1399+
const spanDELETE = startSpanManual(
1400+
{ name: 'test span', attributes: { [SemanticAttributes.HTTP_METHOD]: 'DELETE' } },
1401+
span => {
1402+
return span;
1403+
},
1404+
);
1405+
expect(spanIsSampled(spanDELETE)).toBe(true);
1406+
expect(getSamplingDecision(spanDELETE.spanContext())).toBe(true);
1407+
});
1408+
1409+
it('does not sample when HTTP method is OPTIONS', () => {
1410+
const span = startSpanManual(
1411+
{ name: 'test span', attributes: { [SemanticAttributes.HTTP_METHOD]: 'OPTIONS' } },
1412+
span => {
1413+
return span;
1414+
},
1415+
);
1416+
expect(spanIsSampled(span)).toBe(false);
1417+
expect(getSamplingDecision(span.spanContext())).toBe(false);
1418+
});
1419+
1420+
it('does not sample when HTTP method is HEAD', () => {
1421+
const span = startSpanManual(
1422+
{ name: 'test span', attributes: { [SemanticAttributes.HTTP_METHOD]: 'HEAD' } },
1423+
span => {
1424+
return span;
1425+
},
1426+
);
1427+
expect(spanIsSampled(span)).toBe(false);
1428+
expect(getSamplingDecision(span.spanContext())).toBe(false);
1429+
});
1430+
});
1431+
13611432
describe('continueTrace', () => {
13621433
beforeEach(() => {
13631434
mockSdkInit({ enableTracing: true });

0 commit comments

Comments
 (0)