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

A holiday bundle! #4364

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 1 addition & 9 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
{
"editor.formatOnType": true,
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.rulers": [120],
"editor.tabSize": 2,
"files.autoSave": "onWindowChange",
Expand All @@ -12,10 +9,5 @@
"**/build/": true,
"**/dist/": true
},
"typescript.tsdk": "./node_modules/typescript/lib",
"[json]": {
"editor.formatOnType": false,
"editor.formatOnPaste": false,
"editor.formatOnSave": false
}
"typescript.tsdk": "./node_modules/typescript/lib"
}
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"packages/angular",
"packages/browser",
"packages/core",
"packages/ember",
"packages/eslint-config-sdk",
"packages/eslint-plugin-sdk",
"packages/gatsby",
Expand Down
2 changes: 1 addition & 1 deletion packages/hub/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sentry/hub",
"version": "6.16.1",
"version": "6.17.0-0",
"description": "Sentry hub which handles global state managment.",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/hub",
Expand Down
Binary file added packages/hub/sentry-hub-6.17.0-0.tgz
Binary file not shown.
8 changes: 6 additions & 2 deletions packages/hub/src/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface Layer {
*/
export interface Carrier {
__SENTRY__?: {
enableHubBranching?: boolean;
hub?: Hub;
/**
* Extra Hub properties injected by various SDKs
Expand Down Expand Up @@ -575,7 +576,8 @@ export function getActiveDomain(): DomainAsCarrier | undefined {
*/
function getHubFromActiveDomain(registry: Carrier): Hub {
try {
const activeDomain = getMainCarrier().__SENTRY__?.extensions?.domain?.active;
const domain = getMainCarrier().__SENTRY__?.extensions?.domain;
const activeDomain = domain?.active;

// If there's no active domain, just return global hub
if (!activeDomain) {
Expand All @@ -584,7 +586,9 @@ function getHubFromActiveDomain(registry: Carrier): Hub {

// If there's no hub on current domain, or it's an old API, assign a new one
if (!hasHubOnCarrier(activeDomain) || getHubFromCarrier(activeDomain).isOlderThan(API_VERSION)) {
const registryHubTopStack = getHubFromCarrier(registry).getStackTop();
// @ts-ignore using non-standard reference to parent domain
const parentCarrier = (activeDomain as unknown).parent || registry
const registryHubTopStack = getHubFromCarrier(parentCarrier).getStackTop();
setHubOnCarrier(activeDomain, new Hub(registryHubTopStack.client, Scope.clone(registryHubTopStack.scope)));
}

Expand Down
2 changes: 1 addition & 1 deletion packages/node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sentry/node",
"version": "6.16.1",
"version": "6.17.0-0",
"description": "Official Sentry SDK for Node.js",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node",
Expand Down
Binary file added packages/node/sentry-node-6.17.0-0.tgz
Binary file not shown.
2 changes: 1 addition & 1 deletion packages/node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export { NodeOptions } from './types';
export { NodeBackend } from './backend';
export { NodeClient } from './client';
export { defaultIntegrations, init, lastEventId, flush, close, getSentryRelease } from './sdk';
export { deepReadDirSync } from './utils';
export { deepReadDirSync, trace } from './utils';
export { SDK_NAME } from './version';

import { Integrations as CoreIntegrations } from '@sentry/core';
Expand Down
82 changes: 55 additions & 27 deletions packages/node/src/integrations/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Integration, Span } from '@sentry/types';
import { fill, logger, parseSemver } from '@sentry/utils';
import * as http from 'http';
import * as https from 'https';
import * as net from 'net';

import {
cleanSpanDescription,
Expand Down Expand Up @@ -111,12 +112,14 @@ function _createWrappedRequestMethodFactory(
const scope = getCurrentHub().getScope();
if (scope && tracingEnabled) {
parentSpan = scope.getSpan();
if (parentSpan) {
if (parentSpan && parentSpan.op !== 'aws.request') {
span = parentSpan.startChild({
description: `${requestOptions.method || 'GET'} ${requestUrl}`,
op: 'http.client',
});
}

if (span && !requestOptions.stripTracingHeader) {
const sentryTraceHeader = span.toTraceparent();
logger.log(
`[Tracing] Adding sentry-trace header ${sentryTraceHeader} to outgoing request to ${requestUrl}: `,
Expand All @@ -126,35 +129,60 @@ function _createWrappedRequestMethodFactory(
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return originalRequestMethod
.apply(httpModule, requestArgs)
.once('response', function(this: http.ClientRequest, res: http.IncomingMessage): void {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const req = this;
if (breadcrumbsEnabled) {
addRequestBreadcrumb('response', requestUrl, req, res);
}
if (tracingEnabled && span) {
if (res.statusCode) {
span.setHttpStatus(res.statusCode);
}
span.description = cleanSpanDescription(span.description, requestOptions, req);
span.finish();
}
const start = Date.now()
const req: ReturnType<OriginalRequestMethod> = originalRequestMethod.apply(httpModule, requestArgs)

req.once('socket', (socket: net.Socket) => {
socket.on('lookup', () => {
span?.setData('net.time_to_lookup', Date.now() - start)
})
.once('error', function(this: http.ClientRequest): void {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const req = this;
socket.on('connect', () => {
span?.setData('net.time_to_connect', Date.now() - start)
})
})

if (breadcrumbsEnabled) {
addRequestBreadcrumb('error', requestUrl, req);
}
if (tracingEnabled && span) {
span.setHttpStatus(500);
span.description = cleanSpanDescription(span.description, requestOptions, req);
span.finish();
req.once('response', function(this: http.ClientRequest, res: http.IncomingMessage): void {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const req = this;
if (breadcrumbsEnabled) {
addRequestBreadcrumb('response', requestUrl, req, res);
}
if (tracingEnabled && span) {
if (res.statusCode) {
span.setHttpStatus(res.statusCode);
}
});
// @ts-ignore reusedSocket missing from @types/node
span.setTag('net.reused_socket', (req as unknown).reusedSocket)
span.description = cleanSpanDescription(span.description, requestOptions, req);

res.once('end', () => {
span?.finish()
})

res.once('error', () => {
if (breadcrumbsEnabled) {
addRequestBreadcrumb('response_error', requestUrl, req);
}
span?.finish()
})
}
})

req.once('error', function(this: http.ClientRequest): void {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const req = this;

if (breadcrumbsEnabled) {
addRequestBreadcrumb('error', requestUrl, req);
}
if (tracingEnabled && span) {
span.setHttpStatus(500);
span.description = cleanSpanDescription(span.description, requestOptions, req);
span.finish();
}
});

return req
};
};
}
Expand Down
4 changes: 3 additions & 1 deletion packages/node/src/integrations/utils/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ export function cleanSpanDescription(
}

// the node types are missing a few properties which node's `urlToOptions` function spits out
export type RequestOptions = http.RequestOptions & { hash?: string; search?: string; pathname?: string; href?: string };
export type RequestOptions = http.RequestOptions
& { hash?: string; search?: string; pathname?: string; href?: string }
& { stripTracingHeader?: boolean };
type RequestCallback = (response: http.IncomingMessage) => void;
export type RequestMethodArgs =
| [RequestOptions | string | URL, RequestCallback?]
Expand Down
4 changes: 4 additions & 0 deletions packages/node/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ export function init(options: NodeOptions = {}): void {
const carrier = getMainCarrier();
const autoloadedIntegrations = carrier.__SENTRY__?.integrations || [];

if (carrier.__SENTRY__) {
carrier.__SENTRY__.enableHubBranching = options.enableHubBranching
}

options.defaultIntegrations =
options.defaultIntegrations === false
? []
Expand Down
2 changes: 2 additions & 0 deletions packages/node/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Options } from '@sentry/types';
* @see NodeClient for more information.
*/
export interface NodeOptions extends Options {
enableHubBranching?: boolean;

/** Sets an optional server name (device name) */
serverName?: string;

Expand Down
50 changes: 50 additions & 0 deletions packages/node/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { getCurrentHub } from '@sentry/core';
import { getMainCarrier } from '@sentry/hub';
import { SpanContext } from '@sentry/types';
import * as domain from 'domain';
import * as fs from 'fs';
import * as path from 'path';

Expand Down Expand Up @@ -36,3 +40,49 @@ export function deepReadDirSync(targetDir: string): string[] {

return deepReadCurrentDir(targetDirAbsPath).map(absPath => path.relative(targetDirAbsPath, absPath));
}

type Trace<T> = (spanContext: SpanContext, fn: () => T) => Promise<T>
type DomainExt = ReturnType<typeof domain.create> & { parent?: DomainExt }

const branchHubAndTrace: Trace<unknown> = async (spanContext, fn) => new Promise((resolve, reject) => {
const d = domain.create() as DomainExt
// @ts-ignore
d.parent = (domain as unknown).active
d.on('error', reject)
d.run(async () => {
const parent = getCurrentHub().getScope()
const span = parent?.getSpan()?.startChild(spanContext)
parent?.setSpan(span)
try {
resolve(await fn())
} catch(e) {
span?.setStatus('unknown_error')
reject(e)
} finally {
span?.finish()
delete d.parent
}
})
})

const useScopeAndTrace: Trace<unknown> = async (spanContext, fn) => {
const span = getCurrentHub().getScope()?.getSpan()?.startChild(spanContext)
try {
return await fn()
} catch(e) {
span?.setStatus('unknown_error')
throw e
} finally {
span?.finish()
}
}

export const trace: Trace<unknown> = async (spanContext, fn) => {
const carrier = getMainCarrier()
const branchStrategy = carrier.__SENTRY__?.enableHubBranching
if (branchStrategy) {
return branchHubAndTrace(spanContext, fn)
} else {
return useScopeAndTrace(spanContext, fn)
}
}
2 changes: 1 addition & 1 deletion packages/serverless/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sentry/serverless",
"version": "6.16.1",
"version": "6.17.0-0",
"description": "Official Sentry SDK for various serverless solutions",
"repository": "git://github.com/getsentry/sentry-javascript.git",
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless",
Expand Down
Binary file not shown.
83 changes: 45 additions & 38 deletions packages/serverless/src/awslambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { isString, logger } from '@sentry/utils';
// NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil
// eslint-disable-next-line import/no-unresolved
import { Context, Handler } from 'aws-lambda';
import * as domain from 'domain'
import { existsSync } from 'fs';
import { hostname } from 'os';
import { basename, resolve } from 'path';
Expand Down Expand Up @@ -280,45 +281,51 @@ export function wrapHandler<TEvent, TResult>(
}, timeoutWarningDelay);
}

// Applying `sentry-trace` to context
let traceparentData;
const eventWithHeaders = event as { headers?: { [key: string]: string } };
if (eventWithHeaders.headers && isString(eventWithHeaders.headers['sentry-trace'])) {
traceparentData = extractTraceparentData(eventWithHeaders.headers['sentry-trace'] as string);
}
const transaction = startTransaction({
name: context.functionName,
op: 'awslambda.handler',
...traceparentData,
});

const hub = getCurrentHub();
const scope = hub.pushScope();
let rv: TResult | undefined;
try {
enhanceScopeWithEnvironmentData(scope, context, START_TIME);
// We put the transaction on the scope so users can attach children to it
scope.setSpan(transaction);
rv = await asyncHandler(event, context);

// We manage lambdas that use Promise.allSettled by capturing the errors of failed promises
if (options.captureAllSettledReasons && Array.isArray(rv) && isPromiseAllSettledResult(rv)) {
const reasons = getRejectedReasons(rv);
reasons.forEach(exception => {
captureException(exception);
});
const local = domain.create()
return local.run(async () => {
// Applying `sentry-trace` to context
let traceparentData;
const eventWithHeaders = event as { headers?: { [key: string]: string } };
if (eventWithHeaders.headers && isString(eventWithHeaders.headers['sentry-trace'])) {
traceparentData = extractTraceparentData(eventWithHeaders.headers['sentry-trace'] as string);
}
} catch (e) {
captureException(e);
if (options.rethrowAfterCapture) {
throw e;

const transaction = startTransaction({
name: context.functionName,
op: 'awslambda.handler',
...traceparentData,
});

const hub = getCurrentHub();
const scope = hub.pushScope();

let rv: TResult | undefined;
try {
enhanceScopeWithEnvironmentData(scope, context, START_TIME);
// We put the transaction on the scope so users can attach children to it
scope.setSpan(transaction);
rv = await asyncHandler(event, context);

// We manage lambdas that use Promise.allSettled by capturing the errors of failed promises
if (options.captureAllSettledReasons && Array.isArray(rv) && isPromiseAllSettledResult(rv)) {
const reasons = getRejectedReasons(rv);
reasons.forEach(exception => {
captureException(exception);
});
}
} catch (e) {
captureException(e);
if (options.rethrowAfterCapture) {
throw e;
}
} finally {
clearTimeout(timeoutWarningTimer);
transaction.finish();
hub.popScope();
await flush(options.flushTimeout);
}
} finally {
clearTimeout(timeoutWarningTimer);
transaction.finish();
hub.popScope();
await flush(options.flushTimeout);
}
return rv;
return rv;
})

};
}
Loading