Skip to content

Commit 214618d

Browse files
committed
wip: APM
1 parent 22a2a4e commit 214618d

File tree

11 files changed

+318
-61
lines changed

11 files changed

+318
-61
lines changed

packages/hub/src/hub.ts

+48
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ import {
88
Integration,
99
IntegrationClass,
1010
Severity,
11+
SpanDetails,
1112
User,
1213
} from '@sentry/types';
1314
import { consoleSandbox, dynamicRequire, getGlobalObject, isNodeEnv, logger, uuid4 } from '@sentry/utils';
1415

1516
import { Carrier, Layer } from './interfaces';
1617
import { Scope } from './scope';
18+
import { Span } from './span';
1719

1820
declare module 'domain' {
1921
export let active: Domain;
@@ -383,6 +385,52 @@ export class Hub implements HubInterface {
383385
}
384386
return {};
385387
}
388+
389+
/**
390+
* @inheritDoc
391+
*/
392+
public startSpan(spanDetails?: SpanDetails): Span {
393+
const scope = this.getScope();
394+
395+
if (scope) {
396+
const span = scope.getSpan();
397+
if (span) {
398+
return span.newSpan(spanDetails);
399+
}
400+
}
401+
402+
return new Span(spanDetails);
403+
}
404+
405+
/**
406+
* @inheritDoc
407+
*/
408+
public finishSpan(span: Span): string | undefined {
409+
if (!span.timestamp) {
410+
span.finish();
411+
}
412+
413+
if (!span.transaction) {
414+
return undefined;
415+
}
416+
417+
if (span.sampled) {
418+
return undefined;
419+
}
420+
421+
if (!this.getClient()) {
422+
return undefined;
423+
}
424+
425+
return this.captureEvent({
426+
contexts: { trace: span.getTraceContext() },
427+
spans: span.finishedSpans.filter(s => s !== span),
428+
start_timestamp: span.startTimestamp,
429+
timestamp: span.timestamp,
430+
transaction: span.transaction,
431+
type: 'transaction',
432+
});
433+
}
386434
}
387435

388436
/** Returns the global shim registry. */

packages/hub/src/scope.ts

-10
Original file line numberDiff line numberDiff line change
@@ -200,16 +200,6 @@ export class Scope implements ScopeInterface {
200200
return this;
201201
}
202202

203-
/**
204-
* @inheritDoc
205-
*/
206-
public startSpan(parentSpan?: Span): Span {
207-
const span = new Span();
208-
span.setParent(parentSpan);
209-
this.setSpan(span);
210-
return span;
211-
}
212-
213203
/**
214204
* Internal getter for Span, used in Hub.
215205
* @hidden

packages/hub/src/span.ts

+130-33
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Span as SpanInterface } from '@sentry/types';
1+
import { Span as SpanInterface, SpanDetails } from '@sentry/types';
22
import { uuid4 } from '@sentry/utils';
33

44
export const TRACEPARENT_REGEXP = /^[ \t]*([0-9a-f]{32})?-?([0-9a-f]{16})?-?([01])?[ \t]*$/;
@@ -7,71 +7,168 @@ export const TRACEPARENT_REGEXP = /^[ \t]*([0-9a-f]{32})?-?([0-9a-f]{16})?-?([01
77
* Span containg all data about a span
88
*/
99
export class Span implements SpanInterface {
10-
public constructor(
11-
private readonly _traceId: string = uuid4(),
12-
private readonly _spanId: string = uuid4().substring(16),
13-
private _sampled?: boolean,
14-
private _parent?: Span,
15-
) {}
10+
/**
11+
* Trace ID
12+
*/
13+
private readonly _traceId: string = uuid4();
1614

1715
/**
18-
* Setter for parent
16+
* Span ID
1917
*/
20-
public setParent(parent: Span | undefined): this {
21-
this._parent = parent;
22-
return this;
18+
private readonly _spanId: string = uuid4().substring(16);
19+
20+
/**
21+
* Parent Span ID
22+
*/
23+
private readonly _parentSpanId?: string;
24+
25+
/**
26+
* Has the sampling decision been made?
27+
*/
28+
public readonly sampled?: string;
29+
30+
/**
31+
* Timestamp when the span was created.
32+
*/
33+
public readonly startTimestamp: number = new Date().getTime();
34+
35+
/**
36+
* Finish timestamp of the span.
37+
*/
38+
public timestamp?: number;
39+
40+
/**
41+
* Set the transaction of the Span.
42+
*/
43+
public transaction?: string;
44+
45+
/**
46+
* Set the operation of the Span.
47+
*/
48+
public op?: string;
49+
50+
/**
51+
* Set the description of the Span.
52+
*/
53+
public description?: string;
54+
55+
/**
56+
* List of spans that were finalized
57+
*/
58+
public finishedSpans: Span[] = [];
59+
60+
public constructor(spanDetails?: SpanDetails) {
61+
if (!spanDetails) {
62+
return this;
63+
}
64+
65+
if (spanDetails.traceId) {
66+
this._traceId = spanDetails.traceId;
67+
}
68+
if (spanDetails.spanId) {
69+
this._spanId = spanDetails.spanId;
70+
}
71+
if (spanDetails.parentSpanId) {
72+
this._parentSpanId = spanDetails.parentSpanId;
73+
}
74+
if (spanDetails.sampled) {
75+
this.sampled = spanDetails.sampled;
76+
}
77+
if (spanDetails.transaction) {
78+
this.transaction = spanDetails.transaction;
79+
}
80+
if (spanDetails.op) {
81+
this.op = spanDetails.op;
82+
}
83+
if (spanDetails.description) {
84+
this.description = spanDetails.description;
85+
}
86+
}
87+
88+
/** JSDoc */
89+
public newSpan(spanDetails?: Pick<SpanDetails, Exclude<keyof SpanDetails, 'spanId'>>): Span {
90+
const span = new Span({
91+
...spanDetails,
92+
parentSpanId: this._parentSpanId,
93+
sampled: this.sampled,
94+
traceId: this._traceId,
95+
});
96+
97+
span.finishedSpans = this.finishedSpans;
98+
99+
return span;
23100
}
24101

25102
/**
26-
* Setter for sampled
103+
* Setter for transaction.
27104
*/
28-
public setSampled(sampled: boolean | undefined): this {
29-
this._sampled = sampled;
105+
public setTransaction(transaction: string | undefined): this {
106+
this.transaction = transaction;
30107
return this;
31108
}
32109

33110
/**
34111
* Continues a trace
35112
* @param traceparent Traceparent string
36113
*/
37-
public static fromTraceparent(traceparent: string): Span | undefined {
114+
public static fromTraceparent(
115+
traceparent: string,
116+
spanDetails?: Pick<SpanDetails, Exclude<keyof SpanDetails, 'spanId' | 'sampled' | 'traceid'>>,
117+
): Span | undefined {
38118
const matches = traceparent.match(TRACEPARENT_REGEXP);
39119
if (matches) {
40-
let sampled;
41-
if (matches[3] === '1') {
42-
sampled = true;
43-
} else if (matches[3] === '0') {
44-
sampled = false;
45-
}
46-
const parent = new Span(matches[1], matches[2], sampled);
47-
return new Span(matches[1], undefined, sampled, parent);
120+
const [traceId, spanId, sampled] = matches;
121+
return new Span({
122+
...spanDetails,
123+
sampled,
124+
spanId,
125+
traceId,
126+
});
48127
}
49128
return undefined;
50129
}
51130

131+
/**
132+
* Sets the finish timestamp on the current span
133+
*/
134+
public finish(): void {
135+
this.timestamp = new Date().getTime();
136+
this.finishedSpans.push(this);
137+
}
138+
52139
/**
53140
* @inheritDoc
54141
*/
55142
public toTraceparent(): string {
56-
let sampled = '';
57-
if (this._sampled === true) {
58-
sampled = '-1';
59-
} else if (this._sampled === false) {
60-
sampled = '-0';
61-
}
62-
63-
return `${this._traceId}-${this._spanId}${sampled}`;
143+
return `${this._traceId}-${this._spanId}${this.sampled ? '-1' : '0'}`;
144+
}
145+
/**
146+
* @inheritDoc
147+
*/
148+
public getTraceContext(): object {
149+
return {
150+
description: this.description,
151+
op: this.op,
152+
parent_span_id: this._parentSpanId,
153+
span_id: this._spanId,
154+
trace_id: this._traceId,
155+
};
64156
}
65157

66158
/**
67159
* @inheritDoc
68160
*/
69161
public toJSON(): object {
70162
return {
71-
parent: (this._parent && this._parent.toJSON()) || undefined,
72-
sampled: this._sampled,
163+
description: this.description,
164+
op: this.op,
165+
parent_span_id: this._parentSpanId,
166+
sampled: this.sampled,
73167
span_id: this._spanId,
168+
start_timestamp: this.startTimestamp,
169+
timestamp: this.timestamp,
74170
trace_id: this._traceId,
171+
transaction: this.transaction,
75172
};
76173
}
77174
}

packages/hub/test/span.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ describe('Span', () => {
1212
expect(from._parent._spanId).toEqual('bbbbbbbbbbbbbbbb');
1313
expect(from._traceId).toEqual('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
1414
expect(from._spanId).not.toEqual('bbbbbbbbbbbbbbbb');
15-
expect(from._sampled).toBeUndefined();
15+
expect(from.sampled).toBeUndefined();
1616
});
1717
test('sample true', () => {
1818
const from = Span.fromTraceparent('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-1') as any;
19-
expect(from._sampled).toBeTruthy();
19+
expect(from.sampled).toBeTruthy();
2020
});
2121

2222
test('sample false', () => {
2323
const from = Span.fromTraceparent('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-0') as any;
24-
expect(from._sampled).toBeFalsy();
24+
expect(from.sampled).toBeFalsy();
2525
});
2626
});
2727

packages/integrations/src/tracing.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export class Tracing implements Integration {
7575
*/
7676
public static startTrace(hub: Hub, transaction?: string): void {
7777
hub.configureScope(scope => {
78-
scope.startSpan();
78+
// scope.startSpan();
7979
scope.setTransaction(transaction);
8080
});
8181
}

packages/node/src/handlers.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { captureException, getCurrentHub, withScope } from '@sentry/core';
2-
import { Span } from '@sentry/hub';
1+
import { captureException, getCurrentHub } from '@sentry/core';
32
import { Event } from '@sentry/types';
43
import { forget, isString, logger, normalize } from '@sentry/utils';
54
import * as cookie from 'cookie';
@@ -321,20 +320,20 @@ export function errorHandler(options?: {
321320
) => void {
322321
return function sentryErrorMiddleware(
323322
error: MiddlewareError,
324-
_req: http.IncomingMessage,
325-
_res: http.ServerResponse,
323+
req: http.IncomingMessage,
324+
res: http.ServerResponse,
326325
next: (error: MiddlewareError) => void,
327326
): void {
328327
const shouldHandleError = (options && options.shouldHandleError) || defaultShouldHandleError;
329328

330329
if (shouldHandleError(error)) {
331330
withScope(scope => {
332-
if (_req.headers && isString(_req.headers['sentry-trace'])) {
333-
const span = Span.fromTraceparent(_req.headers['sentry-trace'] as string);
331+
if (req.headers && isString(req.headers['sentry-trace'])) {
332+
const span = Span.fromTraceparent(req.headers['sentry-trace'] as string);
334333
scope.setSpan(span);
335334
}
336335
const eventId = captureException(error);
337-
(_res as any).sentry = eventId;
336+
(res as any).sentry = eventId;
338337
next(error);
339338
});
340339

0 commit comments

Comments
 (0)