Skip to content

Commit 50a3bf4

Browse files
authored
Propagate trace context to logs (#9238)
* Propagate trace context to logs * format
1 parent 38337b8 commit 50a3bf4

File tree

4 files changed

+102
-10
lines changed

4 files changed

+102
-10
lines changed

packages/telemetry/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
},
4646
"dependencies": {
4747
"@firebase/component": "0.7.0",
48+
"@opentelemetry/api": "1.9.0",
4849
"@opentelemetry/api-logs": "0.203.0",
4950
"@opentelemetry/exporter-logs-otlp-http": "0.203.0",
5051
"@opentelemetry/resources": "2.0.1",
@@ -54,7 +55,8 @@
5455
},
5556
"license": "Apache-2.0",
5657
"devDependencies": {
57-
"@firebase/app": "0.14.1",
58+
"@firebase/app": "0.14.2",
59+
"@opentelemetry/sdk-trace-web": "2.1.0",
5860
"@rollup/plugin-json": "6.1.0",
5961
"rollup": "2.79.2",
6062
"rollup-plugin-replace": "2.2.0",

packages/telemetry/src/api.test.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,18 @@
1818
import { expect } from 'chai';
1919
import { LoggerProvider } from '@opentelemetry/sdk-logs';
2020
import { Telemetry } from './public-types';
21+
import { trace } from '@opentelemetry/api';
2122
import { Logger, LogRecord, SeverityNumber } from '@opentelemetry/api-logs';
23+
import {
24+
InMemorySpanExporter,
25+
SimpleSpanProcessor,
26+
WebTracerProvider
27+
} from '@opentelemetry/sdk-trace-web';
2228
import { captureError, flush } from './api';
2329

30+
const PROJECT_ID = 'my-project';
31+
const APP_ID = 'my-appid';
32+
2433
const emittedLogs: LogRecord[] = [];
2534

2635
const fakeLoggerProvider = {
@@ -43,8 +52,8 @@ const fakeTelemetry: Telemetry = {
4352
name: 'DEFAULT',
4453
automaticDataCollectionEnabled: true,
4554
options: {
46-
projectId: 'my-project',
47-
appId: 'my-appid'
55+
projectId: PROJECT_ID,
56+
appId: APP_ID
4857
}
4958
},
5059
loggerProvider: fakeLoggerProvider
@@ -97,7 +106,7 @@ describe('Top level API', () => {
97106
const log = emittedLogs[0];
98107
expect(log.severityNumber).to.equal(SeverityNumber.ERROR);
99108
expect(log.body).to.equal('a string error');
100-
expect(log.attributes).to.be.undefined;
109+
expect(log.attributes).to.deep.equal({});
101110
});
102111

103112
it('should capture an unknown error type correctly', () => {
@@ -107,7 +116,35 @@ describe('Top level API', () => {
107116
const log = emittedLogs[0];
108117
expect(log.severityNumber).to.equal(SeverityNumber.ERROR);
109118
expect(log.body).to.equal('Unknown error type: number');
110-
expect(log.attributes).to.be.undefined;
119+
expect(log.attributes).to.deep.equal({});
120+
});
121+
122+
it('should propagate trace context', async () => {
123+
const provider = new WebTracerProvider({
124+
spanProcessors: [new SimpleSpanProcessor(new InMemorySpanExporter())]
125+
});
126+
provider.register();
127+
128+
trace.getTracer('test-tracer').startActiveSpan('test-span', span => {
129+
const error = new Error('This is a test error');
130+
error.stack = '...stack trace...';
131+
error.name = 'TestError';
132+
133+
span.spanContext().traceId = 'my-trace';
134+
span.spanContext().spanId = 'my-span';
135+
136+
captureError(fakeTelemetry, error);
137+
span.end();
138+
});
139+
140+
await provider.shutdown();
141+
142+
expect(emittedLogs[0].attributes).to.deep.equal({
143+
'error.type': 'TestError',
144+
'error.stack': '...stack trace...',
145+
'logging.googleapis.com/trace': `projects/${PROJECT_ID}/traces/my-trace`,
146+
'logging.googleapis.com/spanId': `my-span`
147+
});
111148
});
112149
});
113150

packages/telemetry/src/api.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import { _getProvider, FirebaseApp, getApp } from '@firebase/app';
1919
import { TELEMETRY_TYPE } from './constants';
2020
import { Telemetry } from './public-types';
2121
import { Provider } from '@firebase/component';
22-
import { SeverityNumber } from '@opentelemetry/api-logs';
22+
import { AnyValueMap, SeverityNumber } from '@opentelemetry/api-logs';
23+
import { trace } from '@opentelemetry/api';
2324
import { TelemetryService } from './service';
2425

2526
declare module '@firebase/component' {
@@ -63,24 +64,44 @@ export function getTelemetry(app: FirebaseApp = getApp()): Telemetry {
6364
*/
6465
export function captureError(telemetry: Telemetry, error: unknown): void {
6566
const logger = telemetry.loggerProvider.getLogger('error-logger');
67+
68+
const activeSpanContext = trace.getActiveSpan()?.spanContext();
69+
const traceAttributes = {} as AnyValueMap;
70+
if (telemetry.app.options.projectId && activeSpanContext?.traceId) {
71+
traceAttributes[
72+
'logging.googleapis.com/trace'
73+
] = `projects/${telemetry.app.options.projectId}/traces/${activeSpanContext.traceId}`;
74+
if (activeSpanContext?.spanId) {
75+
traceAttributes['logging.googleapis.com/spanId'] =
76+
activeSpanContext.spanId;
77+
}
78+
}
79+
6680
if (error instanceof Error) {
6781
logger.emit({
6882
severityNumber: SeverityNumber.ERROR,
6983
body: error.message,
7084
attributes: {
7185
'error.type': error.name || 'Error',
72-
'error.stack': error.stack || 'No stack trace available'
86+
'error.stack': error.stack || 'No stack trace available',
87+
...traceAttributes
7388
}
7489
});
7590
} else if (typeof error === 'string') {
7691
logger.emit({
7792
severityNumber: SeverityNumber.ERROR,
78-
body: error
93+
body: error,
94+
attributes: {
95+
...traceAttributes
96+
}
7997
});
8098
} else {
8199
logger.emit({
82100
severityNumber: SeverityNumber.ERROR,
83-
body: `Unknown error type: ${typeof error}`
101+
body: `Unknown error type: ${typeof error}`,
102+
attributes: {
103+
...traceAttributes
104+
}
84105
});
85106
}
86107
}

yarn.lock

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2515,7 +2515,7 @@
25152515
dependencies:
25162516
"@opentelemetry/api" "^1.3.0"
25172517

2518-
"@opentelemetry/api@^1.3.0", "@opentelemetry/api@~1.9.0":
2518+
"@opentelemetry/api@1.9.0", "@opentelemetry/api@^1.3.0", "@opentelemetry/api@~1.9.0":
25192519
version "1.9.0"
25202520
resolved "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe"
25212521
integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==
@@ -2527,6 +2527,13 @@
25272527
dependencies:
25282528
"@opentelemetry/semantic-conventions" "^1.29.0"
25292529

2530+
"@opentelemetry/[email protected]":
2531+
version "2.1.0"
2532+
resolved "https://registry.npmjs.org/@opentelemetry/core/-/core-2.1.0.tgz#5539f04eb9e5245e000b0c3f77bdfaa07557e3a7"
2533+
integrity sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ==
2534+
dependencies:
2535+
"@opentelemetry/semantic-conventions" "^1.29.0"
2536+
25302537
"@opentelemetry/[email protected]":
25312538
version "0.203.0"
25322539
resolved "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.203.0.tgz#cdecb5c5b39561aa8520c8bb78347c6e11c91a81"
@@ -2567,6 +2574,14 @@
25672574
"@opentelemetry/core" "2.0.1"
25682575
"@opentelemetry/semantic-conventions" "^1.29.0"
25692576

2577+
"@opentelemetry/[email protected]":
2578+
version "2.1.0"
2579+
resolved "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.1.0.tgz#11772e732af4f27953cf55567a6630d8b4d8282d"
2580+
integrity sha512-1CJjf3LCvoefUOgegxi8h6r4B/wLSzInyhGP2UmIBYNlo4Qk5CZ73e1eEyWmfXvFtm1ybkmfb2DqWvspsYLrWw==
2581+
dependencies:
2582+
"@opentelemetry/core" "2.1.0"
2583+
"@opentelemetry/semantic-conventions" "^1.29.0"
2584+
25702585
"@opentelemetry/[email protected]":
25712586
version "0.203.0"
25722587
resolved "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.203.0.tgz#01bc7c0549929d2864af2ab0ba23fd5ce02b5b0a"
@@ -2593,6 +2608,23 @@
25932608
"@opentelemetry/resources" "2.0.1"
25942609
"@opentelemetry/semantic-conventions" "^1.29.0"
25952610

2611+
"@opentelemetry/[email protected]":
2612+
version "2.1.0"
2613+
resolved "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.1.0.tgz#9d31474824e9ed215f94bf71260d5321f64d402a"
2614+
integrity sha512-uTX9FBlVQm4S2gVQO1sb5qyBLq/FPjbp+tmGoxu4tIgtYGmBYB44+KX/725RFDe30yBSaA9Ml9fqphe1hbUyLQ==
2615+
dependencies:
2616+
"@opentelemetry/core" "2.1.0"
2617+
"@opentelemetry/resources" "2.1.0"
2618+
"@opentelemetry/semantic-conventions" "^1.29.0"
2619+
2620+
"@opentelemetry/[email protected]":
2621+
version "2.1.0"
2622+
resolved "https://registry.npmjs.org/@opentelemetry/sdk-trace-web/-/sdk-trace-web-2.1.0.tgz#5765729ad975a8611eb863d40778d644eda4ae54"
2623+
integrity sha512-2F6ZuZFmJg4CdhRPP8+60DkvEwGLCiU3ffAkgnnqe/ALGEBqGa0HrZaNWFGprXWVivrYHpXhr7AEfasgLZD71g==
2624+
dependencies:
2625+
"@opentelemetry/core" "2.1.0"
2626+
"@opentelemetry/sdk-trace-base" "2.1.0"
2627+
25962628
"@opentelemetry/[email protected]", "@opentelemetry/semantic-conventions@^1.29.0":
25972629
version "1.36.0"
25982630
resolved "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.36.0.tgz#149449bd4df4d0464220915ad4164121e0d75d4d"

0 commit comments

Comments
 (0)