Skip to content

Commit 1c529e9

Browse files
committed
feat(ourlogs): Add replay id to logs when available
Logs can't lookup replays via traces since traces may be sampled whereas logs are (currently) unsampled, with their own traceids.
1 parent 3b06e0b commit 1c529e9

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

packages/core/src/logs/exports.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { timestampInSeconds } from '../utils-hoist/time';
1010
import { GLOBAL_OBJ } from '../utils-hoist/worldwide';
1111
import { SEVERITY_TEXT_TO_SEVERITY_NUMBER } from './constants';
1212
import { createLogEnvelope } from './envelope';
13+
import { Integration } from '../types-hoist/integration';
1314

1415
const MAX_LOG_BUFFER_SIZE = 100;
1516

@@ -111,6 +112,9 @@ export function _INTERNAL_captureLog(
111112
return;
112113
}
113114

115+
116+
const replay = client.getIntegrationByName<Integration & { getReplayId: () => string }>('Replay');
117+
const replayId = replay?.getReplayId();
114118
const [, traceContext] = _getTraceInfoFromScope(client, scope);
115119

116120
const processedLogAttributes = {
@@ -125,6 +129,10 @@ export function _INTERNAL_captureLog(
125129
processedLogAttributes['sentry.environment'] = environment;
126130
}
127131

132+
if (replayId) {
133+
processedLogAttributes['sentry.replay_id'] = replayId;
134+
}
135+
128136
const { sdk } = client.getSdkMetadata() ?? {};
129137
if (sdk) {
130138
processedLogAttributes['sentry.sdk.name'] = sdk.name;

packages/core/test/lib/logs/exports.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import type { Log } from '../../../src/types-hoist/log';
1010
import * as loggerModule from '../../../src/utils-hoist/logger';
1111
import { getDefaultTestClientOptions, TestClient } from '../../mocks/client';
12+
import { Replay } from '../../../../replay-internal/src/integration';
1213

1314
const PUBLIC_DSN = 'https://username@domain/123';
1415

@@ -155,6 +156,50 @@ describe('_INTERNAL_captureLog', () => {
155156
});
156157
});
157158

159+
it('includes replay id in log attributes when available', async () => {
160+
161+
let _initialized = false;
162+
class TestReplayIntegration extends Replay {
163+
protected get _isInitialized(): boolean {
164+
return _initialized;
165+
}
166+
protected set _isInitialized(value: boolean) {
167+
_initialized = value;
168+
}
169+
170+
public afterAllSetup(): void {
171+
// do nothing, we need to manually initialize this
172+
}
173+
}
174+
const replayIntegration = new TestReplayIntegration();
175+
const options = getDefaultTestClientOptions({
176+
dsn: PUBLIC_DSN,
177+
_experiments: { enableLogs: true },
178+
integrations: [replayIntegration],
179+
});
180+
181+
const client = new TestClient(options);
182+
183+
replayIntegration['_setup'](client as any);
184+
replayIntegration['_initialize'](client as any);
185+
186+
const scope = new Scope();
187+
scope.setPropagationContext({
188+
traceId: '3d9355f71e9c444b81161599adac6e29',
189+
sampleRand: 1,
190+
});
191+
192+
_INTERNAL_captureLog({ level: 'info', message: 'test log with replay id' }, client, scope);
193+
194+
const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes;
195+
expect(logAttributes).toEqual({
196+
'sentry.replay_id': {
197+
value: '123',
198+
type: 'string',
199+
},
200+
});
201+
});
202+
158203
it('includes SDK metadata in log attributes when available', () => {
159204
const options = getDefaultTestClientOptions({
160205
dsn: PUBLIC_DSN,

0 commit comments

Comments
 (0)