Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 80d5ff2

Browse files
feat(logs): Add internal replay_is_buffering flag (#17752)
1 parent 8424fdc commit 80d5ff2

File tree

2 files changed

+211
-4
lines changed

2 files changed

+211
-4
lines changed

‎packages/core/src/logs/internal.ts‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,20 @@ export function _INTERNAL_captureLog(
151151
setLogAttribute(processedLogAttributes, 'sentry.sdk.name', name);
152152
setLogAttribute(processedLogAttributes, 'sentry.sdk.version', version);
153153

154-
const replay = client.getIntegrationByName<Integration & { getReplayId: (onlyIfSampled?: boolean) => string }>(
155-
'Replay',
156-
);
157-
setLogAttribute(processedLogAttributes, 'sentry.replay_id', replay?.getReplayId(true));
154+
const replay = client.getIntegrationByName<
155+
Integration & {
156+
getReplayId: (onlyIfSampled?: boolean) => string;
157+
getRecordingMode: () => 'session' | 'buffer' | undefined;
158+
}
159+
>('Replay');
160+
161+
const replayId = replay?.getReplayId(true);
162+
setLogAttribute(processedLogAttributes, 'sentry.replay_id', replayId);
163+
164+
if (replayId && replay?.getRecordingMode() === 'buffer') {
165+
// We send this so we can identify cases where the replayId is attached but the replay itself might not have been sent to Sentry
166+
setLogAttribute(processedLogAttributes, 'sentry._internal.replay_is_buffering', true);
167+
}
158168

159169
const beforeLogMessage = beforeLog.message;
160170
if (isParameterizedString(beforeLogMessage)) {

‎packages/core/test/lib/logs/internal.test.ts‎

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ describe('_INTERNAL_captureLog', () => {
424424
// Simulate behavior: return ID for sampled sessions
425425
return onlyIfSampled ? 'sampled-replay-id' : 'any-replay-id';
426426
}),
427+
getRecordingMode: vi.fn(() => 'session'),
427428
};
428429

429430
vi.spyOn(client, 'getIntegrationByName').mockReturnValue(mockReplayIntegration as any);
@@ -480,6 +481,7 @@ describe('_INTERNAL_captureLog', () => {
480481
// Buffer mode should still return ID even with onlyIfSampled=true
481482
return 'buffer-replay-id';
482483
}),
484+
getRecordingMode: vi.fn(() => 'buffer'),
483485
};
484486

485487
vi.spyOn(client, 'getIntegrationByName').mockReturnValue(mockReplayIntegration as any);
@@ -494,6 +496,10 @@ describe('_INTERNAL_captureLog', () => {
494496
value: 'buffer-replay-id',
495497
type: 'string',
496498
},
499+
'sentry._internal.replay_is_buffering': {
500+
value: true,
501+
type: 'boolean',
502+
},
497503
});
498504
});
499505

@@ -527,6 +533,7 @@ describe('_INTERNAL_captureLog', () => {
527533
// Mock replay integration
528534
const mockReplayIntegration = {
529535
getReplayId: vi.fn(() => 'test-replay-id'),
536+
getRecordingMode: vi.fn(() => 'session'),
530537
};
531538

532539
vi.spyOn(client, 'getIntegrationByName').mockReturnValue(mockReplayIntegration as any);
@@ -590,6 +597,196 @@ describe('_INTERNAL_captureLog', () => {
590597
expect(logAttributes).not.toHaveProperty('sentry.replay_id');
591598
});
592599
});
600+
601+
it('sets replay_is_buffering attribute when replay is in buffer mode', () => {
602+
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableLogs: true });
603+
const client = new TestClient(options);
604+
const scope = new Scope();
605+
scope.setClient(client);
606+
607+
// Mock replay integration with buffer mode
608+
const mockReplayIntegration = {
609+
getReplayId: vi.fn(() => 'buffer-replay-id'),
610+
getRecordingMode: vi.fn(() => 'buffer'),
611+
};
612+
613+
vi.spyOn(client, 'getIntegrationByName').mockReturnValue(mockReplayIntegration as any);
614+
615+
_INTERNAL_captureLog({ level: 'info', message: 'test log with buffered replay' }, scope);
616+
617+
expect(mockReplayIntegration.getReplayId).toHaveBeenCalledWith(true);
618+
expect(mockReplayIntegration.getRecordingMode).toHaveBeenCalled();
619+
620+
const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes;
621+
expect(logAttributes).toEqual({
622+
'sentry.replay_id': {
623+
value: 'buffer-replay-id',
624+
type: 'string',
625+
},
626+
'sentry._internal.replay_is_buffering': {
627+
value: true,
628+
type: 'boolean',
629+
},
630+
});
631+
});
632+
633+
it('does not set replay_is_buffering attribute when replay is in session mode', () => {
634+
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableLogs: true });
635+
const client = new TestClient(options);
636+
const scope = new Scope();
637+
scope.setClient(client);
638+
639+
// Mock replay integration with session mode
640+
const mockReplayIntegration = {
641+
getReplayId: vi.fn(() => 'session-replay-id'),
642+
getRecordingMode: vi.fn(() => 'session'),
643+
};
644+
645+
vi.spyOn(client, 'getIntegrationByName').mockReturnValue(mockReplayIntegration as any);
646+
647+
_INTERNAL_captureLog({ level: 'info', message: 'test log with session replay' }, scope);
648+
649+
expect(mockReplayIntegration.getReplayId).toHaveBeenCalledWith(true);
650+
expect(mockReplayIntegration.getRecordingMode).toHaveBeenCalled();
651+
652+
const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes;
653+
expect(logAttributes).toEqual({
654+
'sentry.replay_id': {
655+
value: 'session-replay-id',
656+
type: 'string',
657+
},
658+
});
659+
expect(logAttributes).not.toHaveProperty('sentry._internal.replay_is_buffering');
660+
});
661+
662+
it('does not set replay_is_buffering attribute when replay is undefined mode', () => {
663+
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableLogs: true });
664+
const client = new TestClient(options);
665+
const scope = new Scope();
666+
scope.setClient(client);
667+
668+
// Mock replay integration with undefined mode (replay stopped/disabled)
669+
const mockReplayIntegration = {
670+
getReplayId: vi.fn(() => 'stopped-replay-id'),
671+
getRecordingMode: vi.fn(() => undefined),
672+
};
673+
674+
vi.spyOn(client, 'getIntegrationByName').mockReturnValue(mockReplayIntegration as any);
675+
676+
_INTERNAL_captureLog({ level: 'info', message: 'test log with stopped replay' }, scope);
677+
678+
expect(mockReplayIntegration.getReplayId).toHaveBeenCalledWith(true);
679+
expect(mockReplayIntegration.getRecordingMode).toHaveBeenCalled();
680+
681+
const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes;
682+
expect(logAttributes).toEqual({
683+
'sentry.replay_id': {
684+
value: 'stopped-replay-id',
685+
type: 'string',
686+
},
687+
});
688+
expect(logAttributes).not.toHaveProperty('sentry._internal.replay_is_buffering');
689+
});
690+
691+
it('does not set replay_is_buffering attribute when no replay ID is available', () => {
692+
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableLogs: true });
693+
const client = new TestClient(options);
694+
const scope = new Scope();
695+
scope.setClient(client);
696+
697+
// Mock replay integration that returns no replay ID but has buffer mode
698+
const mockReplayIntegration = {
699+
getReplayId: vi.fn(() => undefined),
700+
getRecordingMode: vi.fn(() => 'buffer'),
701+
};
702+
703+
vi.spyOn(client, 'getIntegrationByName').mockReturnValue(mockReplayIntegration as any);
704+
705+
_INTERNAL_captureLog({ level: 'info', message: 'test log with buffer mode but no replay ID' }, scope);
706+
707+
expect(mockReplayIntegration.getReplayId).toHaveBeenCalledWith(true);
708+
// getRecordingMode should not be called if there's no replay ID
709+
expect(mockReplayIntegration.getRecordingMode).not.toHaveBeenCalled();
710+
711+
const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes;
712+
expect(logAttributes).toEqual({});
713+
expect(logAttributes).not.toHaveProperty('sentry.replay_id');
714+
expect(logAttributes).not.toHaveProperty('sentry.internal.replay_is_buffering');
715+
});
716+
717+
it('does not set replay_is_buffering attribute when replay integration is missing', () => {
718+
const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, enableLogs: true });
719+
const client = new TestClient(options);
720+
const scope = new Scope();
721+
scope.setClient(client);
722+
723+
// Mock no replay integration found
724+
vi.spyOn(client, 'getIntegrationByName').mockReturnValue(undefined);
725+
726+
_INTERNAL_captureLog({ level: 'info', message: 'test log without replay integration' }, scope);
727+
728+
const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes;
729+
expect(logAttributes).toEqual({});
730+
expect(logAttributes).not.toHaveProperty('sentry.replay_id');
731+
expect(logAttributes).not.toHaveProperty('sentry._internal.replay_is_buffering');
732+
});
733+
734+
it('combines replay_is_buffering with other replay attributes', () => {
735+
const options = getDefaultTestClientOptions({
736+
dsn: PUBLIC_DSN,
737+
enableLogs: true,
738+
release: '1.0.0',
739+
environment: 'test',
740+
});
741+
const client = new TestClient(options);
742+
const scope = new Scope();
743+
scope.setClient(client);
744+
745+
// Mock replay integration with buffer mode
746+
const mockReplayIntegration = {
747+
getReplayId: vi.fn(() => 'buffer-replay-id'),
748+
getRecordingMode: vi.fn(() => 'buffer'),
749+
};
750+
751+
vi.spyOn(client, 'getIntegrationByName').mockReturnValue(mockReplayIntegration as any);
752+
753+
_INTERNAL_captureLog(
754+
{
755+
level: 'info',
756+
message: 'test log with buffer replay and other attributes',
757+
attributes: { component: 'auth', action: 'login' },
758+
},
759+
scope,
760+
);
761+
762+
const logAttributes = _INTERNAL_getLogBuffer(client)?.[0]?.attributes;
763+
expect(logAttributes).toEqual({
764+
component: {
765+
value: 'auth',
766+
type: 'string',
767+
},
768+
action: {
769+
value: 'login',
770+
type: 'string',
771+
},
772+
'sentry.release': {
773+
value: '1.0.0',
774+
type: 'string',
775+
},
776+
'sentry.environment': {
777+
value: 'test',
778+
type: 'string',
779+
},
780+
'sentry.replay_id': {
781+
value: 'buffer-replay-id',
782+
type: 'string',
783+
},
784+
'sentry._internal.replay_is_buffering': {
785+
value: true,
786+
type: 'boolean',
787+
},
788+
});
789+
});
593790
});
594791

595792
describe('user functionality', () => {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /