Skip to content
Merged
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
96 changes: 60 additions & 36 deletions packages/node/src/integrations/context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
/* eslint-disable max-lines */

import {
APP_START_TIME,
CLOUD_ACCOUNT_ID,
CLOUD_AVAILABILITY_ZONE,
CLOUD_PLATFORM,
CLOUD_PROVIDER,
CLOUD_REGION,
CULTURE_LOCALE,
CULTURE_TIMEZONE,
DEVICE_ARCHS,
DEVICE_BOOT_TIME,
DEVICE_CPU_DESCRIPTION,
DEVICE_FREE_MEMORY,
DEVICE_MEMORY_SIZE,
DEVICE_PROCESSOR_COUNT,
DEVICE_PROCESSOR_FREQUENCY,
OS_BUILD,
OS_KERNEL_VERSION,
OS_NAME,
OS_VERSION,
PROCESS_RUNTIME_ENGINE_NAME,
PROCESS_RUNTIME_ENGINE_VERSION,
} from '@sentry/conventions/attributes';
import { execFile } from 'node:child_process';
import { readdir, readFile } from 'node:fs';
import * as os from 'node:os';
Expand Down Expand Up @@ -54,8 +77,8 @@ const _nodeContextIntegration = ((options: ContextOptions = {}) => {

// Map static context data to span attributes
const cachedSpanAttributes: Record<string, unknown> = {
'process.runtime.engine.name': 'v8',
'process.runtime.engine.version': process.versions.v8,
[PROCESS_RUNTIME_ENGINE_NAME]: 'v8',
[PROCESS_RUNTIME_ENGINE_VERSION]: process.versions.v8,
...contextsToSpanAttributes({
app: appContext,
device: deviceContext,
Expand Down Expand Up @@ -156,52 +179,53 @@ export function contextsToSpanAttributes(contexts: Contexts): Record<string, unk

if (app) {
if (app.app_start_time) {
attrs['app.start_time'] = app.app_start_time;
attrs[APP_START_TIME] = app.app_start_time;
}
}

if (device) {
if (device.arch) {
attrs['device.archs'] = [device.arch];
attrs[DEVICE_ARCHS] = [device.arch];
}
if (device.boot_time) {
attrs['device.boot_time'] = device.boot_time;
attrs[DEVICE_BOOT_TIME] = device.boot_time;
}
if (device.memory_size != null) {
attrs['device.memory_size'] = device.memory_size;
attrs[DEVICE_MEMORY_SIZE] = device.memory_size;
}
if (device.processor_count != null) {
attrs['device.processor_count'] = device.processor_count;
attrs[DEVICE_PROCESSOR_COUNT] = device.processor_count;
}
if (device.cpu_description) {
attrs['device.cpu_description'] = device.cpu_description;
attrs[DEVICE_CPU_DESCRIPTION] = device.cpu_description;
}
if (device.processor_frequency != null) {
attrs['device.processor_frequency'] = device.processor_frequency;
attrs[DEVICE_PROCESSOR_FREQUENCY] = device.processor_frequency;
}
}

if (osCtx) {
if (osCtx.name) {
attrs['os.name'] = osCtx.name;
attrs[OS_NAME] = osCtx.name;
}
if (osCtx.version) {
attrs['os.version'] = osCtx.version;
attrs[OS_VERSION] = osCtx.version;
}
if (osCtx.kernel_version) {
attrs['os.kernel_version'] = osCtx.kernel_version;
attrs[OS_KERNEL_VERSION] = osCtx.kernel_version;
}
if (osCtx.build) {
attrs['os.build'] = osCtx.build;
// oxlint-disable-next-line typescript/no-deprecated -- Preserve the existing attribute name.
attrs[OS_BUILD] = osCtx.build;
}
}

if (culture) {
if (culture.locale) {
attrs['culture.locale'] = culture.locale;
attrs[CULTURE_LOCALE] = culture.locale;
}
if (culture.timezone) {
attrs['culture.timezone'] = culture.timezone;
attrs[CULTURE_TIMEZONE] = culture.timezone;
}
}

Expand Down Expand Up @@ -235,7 +259,7 @@ export function getDynamicSpanAttributes(

// Only include if memory tracking was initially enabled (indicated by free_memory being set)
if (deviceContext?.free_memory != null) {
attrs['device.free_memory'] = os.freemem();
attrs[DEVICE_FREE_MEMORY] = os.freemem();
}

return attrs;
Expand Down Expand Up @@ -519,62 +543,62 @@ function getCloudResourceContext(): CloudResourceContext | undefined {
if (process.env.VERCEL) {
// https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#system-environment-variables
return {
'cloud.provider': 'vercel',
'cloud.region': process.env.VERCEL_REGION,
[CLOUD_PROVIDER]: 'vercel',
[CLOUD_REGION]: process.env.VERCEL_REGION,
};
} else if (process.env.AWS_REGION) {
// https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html
return {
'cloud.provider': 'aws',
'cloud.region': process.env.AWS_REGION,
'cloud.platform': process.env.AWS_EXECUTION_ENV,
[CLOUD_PROVIDER]: 'aws',
[CLOUD_REGION]: process.env.AWS_REGION,
[CLOUD_PLATFORM]: process.env.AWS_EXECUTION_ENV,
};
} else if (process.env.GCP_PROJECT) {
// https://cloud.google.com/composer/docs/how-to/managing/environment-variables#reserved_variables
return {
'cloud.provider': 'gcp',
[CLOUD_PROVIDER]: 'gcp',
};
} else if (process.env.ALIYUN_REGION_ID) {
// TODO: find where I found these environment variables - at least gc.github.com returns something
return {
'cloud.provider': 'alibaba_cloud',
'cloud.region': process.env.ALIYUN_REGION_ID,
[CLOUD_PROVIDER]: 'alibaba_cloud',
[CLOUD_REGION]: process.env.ALIYUN_REGION_ID,
};
} else if (process.env.WEBSITE_SITE_NAME && process.env.REGION_NAME) {
// https://learn.microsoft.com/en-us/azure/app-service/reference-app-settings?tabs=kudu%2Cdotnet#app-environment
return {
'cloud.provider': 'azure',
'cloud.region': process.env.REGION_NAME,
[CLOUD_PROVIDER]: 'azure',
[CLOUD_REGION]: process.env.REGION_NAME,
};
} else if (process.env.IBM_CLOUD_REGION) {
// TODO: find where I found these environment variables - at least gc.github.com returns something
return {
'cloud.provider': 'ibm_cloud',
'cloud.region': process.env.IBM_CLOUD_REGION,
[CLOUD_PROVIDER]: 'ibm_cloud',
[CLOUD_REGION]: process.env.IBM_CLOUD_REGION,
};
} else if (process.env.TENCENTCLOUD_REGION) {
// https://www.tencentcloud.com/document/product/583/32748
return {
'cloud.provider': 'tencent_cloud',
'cloud.region': process.env.TENCENTCLOUD_REGION,
'cloud.account.id': process.env.TENCENTCLOUD_APPID,
'cloud.availability_zone': process.env.TENCENTCLOUD_ZONE,
[CLOUD_PROVIDER]: 'tencent_cloud',
[CLOUD_REGION]: process.env.TENCENTCLOUD_REGION,
[CLOUD_ACCOUNT_ID]: process.env.TENCENTCLOUD_APPID,
[CLOUD_AVAILABILITY_ZONE]: process.env.TENCENTCLOUD_ZONE,
};
} else if (process.env.NETLIFY) {
// https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables
return {
'cloud.provider': 'netlify',
[CLOUD_PROVIDER]: 'netlify',
};
} else if (process.env.FLY_REGION) {
// https://fly.io/docs/reference/runtime-environment/
return {
'cloud.provider': 'fly.io',
'cloud.region': process.env.FLY_REGION,
[CLOUD_PROVIDER]: 'fly.io',
[CLOUD_REGION]: process.env.FLY_REGION,
};
} else if (process.env.DYNO) {
// https://devcenter.heroku.com/articles/dynos#local-environment-variables
return {
'cloud.provider': 'heroku',
[CLOUD_PROVIDER]: 'heroku',
};
} else {
return undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {
URL_SCHEME,
USER_AGENT_ORIGINAL,
SENTRY_ORIGIN,
HTTP_REQUEST_BODY_SIZE,
HTTP_RESPONSE_STATUS_TEXT,
} from '@sentry/conventions/attributes';
import { HTTP_SERVER } from '@sentry/conventions/op';
import type { Event, Integration, IntegrationFn, Span, SpanAttributes, SpanStatus } from '@sentry/core';
Expand Down Expand Up @@ -188,7 +190,7 @@ const _httpServerSpansIntegration = ((options: HttpServerSpansIntegrationOptions
[NETWORK_PROTOCOL_NAME]: 'http',
[NETWORK_PROTOCOL_VERSION]: httpVersion,
[NETWORK_TRANSPORT]: httpVersion?.toUpperCase() === 'QUIC' ? 'udp' : 'tcp',
'http.request.body.size': getContentLengthFromHeaders(request.headers),
[HTTP_REQUEST_BODY_SIZE]: getContentLengthFromHeaders(request.headers),
...httpHeadersToSpanAttributes(normalizedRequest.headers || {}, client.getDataCollectionOptions()),
},
});
Expand Down Expand Up @@ -362,7 +364,7 @@ function getIncomingRequestAttributesOnResponse(

const newAttributes: SpanAttributes = {
[HTTP_RESPONSE_STATUS_CODE]: statusCode,
'http.response.status_text': statusMessage?.toUpperCase(),
[HTTP_RESPONSE_STATUS_TEXT]: statusMessage?.toUpperCase(),
};

// `client.address` is the originating client, so a forwarding header wins over the socket, which
Expand Down
7 changes: 4 additions & 3 deletions packages/node/src/integrations/nodeRuntimeMetrics.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SENTRY_ORIGIN } from '@sentry/conventions/attributes';
import { monitorEventLoopDelay, performance } from 'perf_hooks';
import { _INTERNAL_safeDateNow, defineIntegration, metrics } from '@sentry/core';
import { _INTERNAL_safeUnref } from '@sentry/core/server';
Expand Down Expand Up @@ -138,9 +139,9 @@ export const nodeRuntimeMetricsIntegration = defineIntegration((options: NodeRun
const resolutionNs = EVENT_LOOP_DELAY_RESOLUTION_MS * 1e6;
const nsToS = (ns: number): number => Math.max(0, (ns - resolutionNs) / 1e9);

const METRIC_ATTRIBUTES = { attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };
const METRIC_ATTRIBUTES_BYTE = { unit: 'byte', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };
const METRIC_ATTRIBUTES_SECOND = { unit: 'second', attributes: { 'sentry.origin': 'auto.node.runtime_metrics' } };
const METRIC_ATTRIBUTES = { attributes: { [SENTRY_ORIGIN]: 'auto.node.runtime_metrics' } };
const METRIC_ATTRIBUTES_BYTE = { unit: 'byte', attributes: { [SENTRY_ORIGIN]: 'auto.node.runtime_metrics' } };
const METRIC_ATTRIBUTES_SECOND = { unit: 'second', attributes: { [SENTRY_ORIGIN]: 'auto.node.runtime_metrics' } };

function collectMetrics(): void {
const now = _INTERNAL_safeDateNow();
Expand Down
3 changes: 2 additions & 1 deletion packages/node/src/integrations/pino.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SENTRY_ORIGIN } from '@sentry/conventions/attributes';
import type { IntegrationFn, LogSeverityLevel } from '@sentry/core';
import {
_INTERNAL_captureLog,
Expand Down Expand Up @@ -142,7 +143,7 @@ const _pinoIntegration = defineIntegration((userOptions: DeepPartial<PinoOptions
if (options.log.levels.includes(level)) {
const attributes: Record<string, unknown> = {
...resultObj,
'sentry.origin': 'auto.log.pino',
[SENTRY_ORIGIN]: 'auto.log.pino',
'pino.logger.level': levelNumber,
};

Expand Down
3 changes: 2 additions & 1 deletion packages/node/src/integrations/winston.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SENTRY_ORIGIN } from '@sentry/conventions/attributes';
/* eslint-disable @typescript-eslint/ban-ts-comment */
import type { LogSeverityLevel } from '@sentry/core';
import { debug, isObjectLike } from '@sentry/core';
Expand Down Expand Up @@ -109,7 +110,7 @@ export function createSentryWinstonTransport<TransportStreamInstance extends obj
if (this._levels.has(logSeverityLevel)) {
captureLog(logSeverityLevel, message as string, {
...attributes,
'sentry.origin': 'auto.log.winston',
[SENTRY_ORIGIN]: 'auto.log.winston',
});
} else if (!customLevel && !winstonLogLevel) {
DEBUG_BUILD &&
Expand Down
3 changes: 2 additions & 1 deletion packages/node/src/logs/capture.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SENTRY_MESSAGE_TEMPLATE } from '@sentry/conventions/attributes';
import { format } from 'node:util';
import type { Log, LogSeverityLevel, ParameterizedString, Scope } from '@sentry/core';
import { _INTERNAL_captureLog } from '@sentry/core';
Expand Down Expand Up @@ -36,7 +37,7 @@ export function captureLog(level: LogSeverityLevel, ...args: CaptureLogArgs): vo
if (Array.isArray(paramsOrAttributes)) {
// type-casting here because from the type definitions we know that `maybeAttributesOrMetadata` is an attributes object (or undefined)
const attributes = { ...(maybeAttributesOrMetadata as Log['attributes'] | undefined) };
attributes['sentry.message.template'] = messageOrMessageTemplate;
attributes[SENTRY_MESSAGE_TEMPLATE] = messageOrMessageTemplate;
paramsOrAttributes.forEach((param, index) => {
attributes[`sentry.message.parameter.${index}`] = param;
});
Expand Down
3 changes: 2 additions & 1 deletion packages/node/src/sdk/client.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SERVER_ADDRESS } from '@sentry/conventions/attributes';
import * as os from 'node:os';
import type { Tracer } from '@opentelemetry/api';
import { trace } from '@opentelemetry/api';
Expand Down Expand Up @@ -68,7 +69,7 @@ export class NodeClient extends ServerRuntimeClient<NodeClientOptions> {
this.on('beforeCaptureLog', log => {
log.attributes = {
...log.attributes,
'server.address': serverName,
[SERVER_ADDRESS]: serverName,
};
});
}
Expand Down
Loading