Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(am): Introduce startTransaction #2600

Merged
merged 30 commits into from
May 27, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2685577
feat: Introduce startTransaction
HazAT May 19, 2020
59586bb
fix: Correctly export interfaces
HazAT May 19, 2020
1409700
fix: Additional undefined check
HazAT May 19, 2020
95680f9
Merge branch 'master' into hazat/am-changes
HazAT May 20, 2020
5192e55
Apply suggestions from code review
HazAT May 20, 2020
0d5781a
fix: Log wording
HazAT May 20, 2020
5dcc194
fix: Don't log trace context with non transactions
HazAT May 20, 2020
e416400
fix: Doc Strings
HazAT May 20, 2020
b8dc363
feat: Added new option `startTraceForUserSession`
HazAT May 20, 2020
d44c123
fix: Typing docs
HazAT May 20, 2020
c6338cf
ref: Remove trace option
HazAT May 20, 2020
6556ae7
Update packages/types/src/span.ts
HazAT May 25, 2020
0631c5a
fix: Node, Add startChild
HazAT May 25, 2020
20ec671
fix: Tests
HazAT May 25, 2020
da67297
fix: Remove trace from apply to event
HazAT May 25, 2020
b5f16d9
fix: Lint errors + Vue Integration
HazAT May 25, 2020
0fd1fab
meta: Add changelog
HazAT May 25, 2020
78ecad8
fix: Vue integration
HazAT May 25, 2020
f088aad
fix: Transaction interface
HazAT May 25, 2020
46d2526
ref: CodeReview
HazAT May 26, 2020
de78c96
feat: Safeguard for name prop in transaction
HazAT May 26, 2020
6269bfd
fix: SampleRate and BeforeSend for Transaction
HazAT May 26, 2020
19840c3
ref: StartSpan creates a child of the span on scope
HazAT May 26, 2020
171e2a1
ref: Set unlabled transaction name
HazAT May 26, 2020
39312e8
ref: Slight refactor of startSpan
HazAT May 26, 2020
c288ad4
ref: Also fix traceHeaders
HazAT May 26, 2020
b077373
feat: Add additional tests
HazAT May 26, 2020
43940d7
fix: Small typo in tests
HazAT May 26, 2020
5446d82
fix: One off tests
HazAT May 26, 2020
0f0f051
Merge branch 'master' into hazat/am-changes
HazAT May 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 32 additions & 23 deletions packages/apm/src/hubextensions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { getMainCarrier, Hub } from '@sentry/hub';
import { SpanContext } from '@sentry/types';
import { SpanContext, TransactionContext } from '@sentry/types';

import { Span } from './span';
import { Transaction } from './transaction';

/** Returns all trace headers that are currently on the top scope. */
function traceHeaders(): { [key: string]: string } {
Expand All @@ -26,43 +27,51 @@ function traceHeaders(): { [key: string]: string } {
*
* @param spanContext Properties with which the span should be created
*/
function startSpan(spanContext?: SpanContext): Span {
function startSpan(spanOrTransactionContext: SpanContext | TransactionContext): Span {
// @ts-ignore
const hub = this as Hub;
const scope = hub.getScope();
HazAT marked this conversation as resolved.
Show resolved Hide resolved
const client = hub.getClient();
let span;

// This flag determines if we already added the span as a child to the span that currently lives on the scope
// If we do not have this, we will add it later on twice to the span recorder and therefore have too many spans
let addedAsChild = false;
let newSpanContext = spanOrTransactionContext;

// We create an empty Span always in case there is nothing on the scope
HazAT marked this conversation as resolved.
Show resolved Hide resolved
let parentSpan = new Span();
HazAT marked this conversation as resolved.
Show resolved Hide resolved
if (scope) {
const parentSpan = scope.getSpan() as Span;
// If there is a Span on the Scope we use the span_id / trace_id
// To define the parent <-> child relationship
parentSpan = scope.getSpan() as Span;
HazAT marked this conversation as resolved.
Show resolved Hide resolved
if (parentSpan) {
span = parentSpan.child(spanContext);
addedAsChild = true;
const { span_id, trace_id } = parentSpan.getTraceContext();
newSpanContext = {
parentSpanId: span_id,
traceId: trace_id,
...spanOrTransactionContext,
};
}
}

if (!span) {
span = new Span(spanContext, hub);
}
// We are dealing with a Transaction
if ((newSpanContext as TransactionContext).name) {
const transaction = new Transaction(newSpanContext as TransactionContext, hub);

// We only roll the dice on sampling for "root" spans (transactions) because the childs inherit this state
if (span.sampled === undefined && span.isRootSpan()) {
const sampleRate = (client && client.getOptions().tracesSampleRate) || 0;
span.sampled = Math.random() < sampleRate;
}
// We only roll the dice on sampling for "root" spans (transactions) because the childs inherit this state
HazAT marked this conversation as resolved.
Show resolved Hide resolved
if (transaction.sampled === undefined) {
const sampleRate = (client && client.getOptions().tracesSampleRate) || 0;
transaction.sampled = Math.random() < sampleRate;
}

// We only want to create a span list if we sampled the transaction
// in case we will discard the span anyway because sampled == false, we safe memory and do not store child spans
HazAT marked this conversation as resolved.
Show resolved Hide resolved
if (transaction.sampled) {
const experimentsOptions = (client && client.getOptions()._experiments) || {};
transaction.initSpanRecorder(experimentsOptions.maxSpans as number);
}

// We only want to create a span list if we sampled the transaction
// in case we will discard the span anyway because sampled == false, we safe memory and do not store child spans
if (span.sampled && !addedAsChild) {
const experimentsOptions = (client && client.getOptions()._experiments) || {};
span.initSpanRecorder(experimentsOptions.maxSpans as number);
return transaction;
}

return span;
return new Span(newSpanContext);
}

/**
Expand Down
60 changes: 38 additions & 22 deletions packages/apm/src/integrations/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {

import { Span as SpanClass } from '../span';
import { SpanStatus } from '../spanstatus';
import { Transaction } from '../transaction';

/**
* Options for Tracing integration
Expand Down Expand Up @@ -132,7 +133,7 @@ export class Tracing implements Integration {
*/
private static _getCurrentHub?: () => Hub;

private static _activeTransaction?: Span;
private static _activeTransaction?: Transaction;

private static _currentIndex: number = 1;

Expand Down Expand Up @@ -202,6 +203,8 @@ export class Tracing implements Integration {
logger.warn(`[Tracing] We added a reasonable default for you: ${defaultTracingOrigins}`);
}

Tracing._startTrace();

// Starting pageload transaction
if (global.location && global.location.href) {
// Use `${global.location.href}` as transaction name
Expand Down Expand Up @@ -407,6 +410,26 @@ export class Tracing implements Integration {
logger.log(args);
}

/**
* TODO
*/
private static _startTrace(): void {
const _getCurrentHub = Tracing._getCurrentHub;
if (!_getCurrentHub) {
return;
}

const hub = _getCurrentHub();
if (!hub) {
return;
}

const trace = hub.startSpan({});
hub.configureScope((scope: Scope) => {
scope.setSpan(trace);
});
}

/**
* Starts a Transaction waiting for activity idle to finish
*/
Expand All @@ -430,15 +453,8 @@ export class Tracing implements Integration {

Tracing._activeTransaction = hub.startSpan({
...spanContext,
transaction: name,
});

// We set the transaction on the scope so if there are any other spans started outside of this integration
// we also add them to this transaction.
// Once the idle transaction is finished, we make sure to remove it again.
hub.configureScope((scope: Scope) => {
scope.setSpan(Tracing._activeTransaction);
});
name,
}) as Transaction;

// The reason we do this here is because of cached responses
// If we start and transaction without an activity it would never finish since there is no activity
Expand All @@ -454,11 +470,11 @@ export class Tracing implements Integration {
* Finshes the current active transaction
*/
public static finishIdleTransaction(): void {
const active = Tracing._activeTransaction as SpanClass;
const active = Tracing._activeTransaction;
if (active) {
Tracing._addPerformanceEntries(active);
Tracing._log('[Tracing] finishIdleTransaction', active.transaction);
active.finish(/*trimEnd*/ true);
Tracing._log('[Tracing] finishIdleTransaction', active.name);
active.finish(undefined, /*trimEnd*/ true);
HazAT marked this conversation as resolved.
Show resolved Hide resolved
Tracing._resetActiveTransaction();
}
}
Expand Down Expand Up @@ -487,7 +503,7 @@ export class Tracing implements Integration {
op: 'browser',
});
span.startTimestamp = timeOrigin + Tracing._msToSec(entry[`${event}Start`]);
span.timestamp = timeOrigin + Tracing._msToSec(entry[`${event}End`]);
span.endTimestamp = timeOrigin + Tracing._msToSec(entry[`${event}End`]);
}

// tslint:disable-next-line: completed-docs
Expand All @@ -497,14 +513,14 @@ export class Tracing implements Integration {
op: 'browser',
});
request.startTimestamp = timeOrigin + Tracing._msToSec(entry.requestStart);
request.timestamp = timeOrigin + Tracing._msToSec(entry.responseEnd);
request.endTimestamp = timeOrigin + Tracing._msToSec(entry.responseEnd);

const response = parent.child({
description: 'response',
op: 'browser',
});
response.startTimestamp = timeOrigin + Tracing._msToSec(entry.responseStart);
response.timestamp = timeOrigin + Tracing._msToSec(entry.responseEnd);
response.endTimestamp = timeOrigin + Tracing._msToSec(entry.responseEnd);
}

let entryScriptSrc: string | undefined;
Expand Down Expand Up @@ -554,7 +570,7 @@ export class Tracing implements Integration {
op: entry.entryType,
});
mark.startTimestamp = timeOrigin + startTime;
mark.timestamp = mark.startTimestamp + duration;
mark.endTimestamp = mark.startTimestamp + duration;
if (tracingInitMarkStartTime === undefined && entry.name === 'sentry-tracing-init') {
tracingInitMarkStartTime = mark.startTimestamp;
}
Expand All @@ -567,7 +583,7 @@ export class Tracing implements Integration {
transactionSpan.spanRecorder.spans.map((finishedSpan: SpanClass) => {
if (finishedSpan.description && finishedSpan.description.indexOf(resourceName) !== -1) {
finishedSpan.startTimestamp = timeOrigin + startTime;
finishedSpan.timestamp = finishedSpan.startTimestamp + duration;
finishedSpan.endTimestamp = finishedSpan.startTimestamp + duration;
}
});
}
Expand All @@ -577,10 +593,10 @@ export class Tracing implements Integration {
op: `resource`,
});
resource.startTimestamp = timeOrigin + startTime;
resource.timestamp = resource.startTimestamp + duration;
resource.endTimestamp = resource.startTimestamp + duration;
// We remember the entry script end time to calculate the difference to the first init mark
if (entryScriptStartEndTime === undefined && (entryScriptSrc || '').includes(resourceName)) {
entryScriptStartEndTime = resource.timestamp;
entryScriptStartEndTime = resource.endTimestamp;
}
}
break;
Expand All @@ -595,7 +611,7 @@ export class Tracing implements Integration {
op: `script`,
});
evaluation.startTimestamp = entryScriptStartEndTime;
evaluation.timestamp = tracingInitMarkStartTime;
evaluation.endTimestamp = tracingInitMarkStartTime;
}

Tracing._performanceCursor = Math.max(performance.getEntries().length - 1, 0);
Expand Down Expand Up @@ -727,7 +743,7 @@ export class Tracing implements Integration {
}
});
}
if (Tracing.options.debug && Tracing.options.debug.spanDebugTimingInfo) {
if (Tracing.options && Tracing.options.debug && Tracing.options.debug.spanDebugTimingInfo) {
Tracing._addSpanDebugInfo(span);
}
span.finish();
Expand Down
Loading