Skip to content

Commit b3e8d14

Browse files
committed
Add app version to session created log entry
1 parent f779bc5 commit b3e8d14

File tree

3 files changed

+148
-5
lines changed

3 files changed

+148
-5
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { expect } from 'chai';
19+
import { LoggerProvider } from '@opentelemetry/sdk-logs';
20+
import { Logger, LogRecord, SeverityNumber } from '@opentelemetry/api-logs';
21+
import { Telemetry } from './public-types';
22+
import { startNewSession } from './helpers';
23+
import {
24+
LOG_ENTRY_ATTRIBUTE_KEYS,
25+
TELEMETRY_SESSION_ID_KEY
26+
} from './constants';
27+
import { AUTO_CONSTANTS } from './auto-constants';
28+
import { TelemetryService } from './service';
29+
30+
const MOCK_SESSION_ID = '00000000-0000-0000-0000-000000000000';
31+
32+
describe('helpers', () => {
33+
let originalSessionStorage: Storage | undefined;
34+
let originalCrypto: Crypto | undefined;
35+
let storage: Record<string, string> = {};
36+
let emittedLogs: LogRecord[] = [];
37+
38+
const fakeLoggerProvider = {
39+
getLogger: (): Logger => {
40+
return {
41+
emit: (logRecord: LogRecord) => {
42+
emittedLogs.push(logRecord);
43+
}
44+
};
45+
},
46+
forceFlush: () => Promise.resolve(),
47+
shutdown: () => Promise.resolve()
48+
} as unknown as LoggerProvider;
49+
50+
const fakeTelemetry: Telemetry = {
51+
app: {
52+
name: 'DEFAULT',
53+
automaticDataCollectionEnabled: true,
54+
options: {
55+
projectId: 'my-project',
56+
appId: 'my-appid'
57+
}
58+
},
59+
loggerProvider: fakeLoggerProvider
60+
};
61+
62+
beforeEach(() => {
63+
emittedLogs = [];
64+
storage = {};
65+
// @ts-ignore
66+
originalSessionStorage = global.sessionStorage;
67+
// @ts-ignore
68+
originalCrypto = global.crypto;
69+
70+
const sessionStorageMock: Partial<Storage> = {
71+
getItem: (key: string) => storage[key] || null,
72+
setItem: (key: string, value: string) => {
73+
storage[key] = value;
74+
}
75+
};
76+
const cryptoMock: Partial<Crypto> = {
77+
randomUUID: () => MOCK_SESSION_ID
78+
};
79+
80+
Object.defineProperty(global, 'sessionStorage', {
81+
value: sessionStorageMock,
82+
writable: true
83+
});
84+
Object.defineProperty(global, 'crypto', {
85+
value: cryptoMock,
86+
writable: true
87+
});
88+
});
89+
90+
afterEach(() => {
91+
Object.defineProperty(global, 'sessionStorage', {
92+
value: originalSessionStorage,
93+
writable: true
94+
});
95+
Object.defineProperty(global, 'crypto', {
96+
value: originalCrypto,
97+
writable: true
98+
});
99+
delete AUTO_CONSTANTS.appVersion;
100+
});
101+
102+
describe('startNewSession', () => {
103+
it('should create a new session and log it with app version (unset)', () => {
104+
startNewSession(fakeTelemetry);
105+
106+
expect(storage[TELEMETRY_SESSION_ID_KEY]).to.equal(MOCK_SESSION_ID);
107+
expect(emittedLogs.length).to.equal(1);
108+
expect(emittedLogs[0].attributes).to.deep.equal({
109+
[LOG_ENTRY_ATTRIBUTE_KEYS.SESSION_ID]: MOCK_SESSION_ID,
110+
[LOG_ENTRY_ATTRIBUTE_KEYS.APP_VERSION]: 'unset'
111+
});
112+
});
113+
114+
it('should log app version from AUTO_CONSTANTS', () => {
115+
AUTO_CONSTANTS.appVersion = '1.2.3';
116+
startNewSession(fakeTelemetry);
117+
118+
expect(emittedLogs[0].attributes).to.deep.equal({
119+
[LOG_ENTRY_ATTRIBUTE_KEYS.SESSION_ID]: MOCK_SESSION_ID,
120+
[LOG_ENTRY_ATTRIBUTE_KEYS.APP_VERSION]: '1.2.3'
121+
});
122+
});
123+
124+
it('should log app version from telemetry options', () => {
125+
const telemetryWithVersion = new TelemetryService(
126+
fakeTelemetry.app,
127+
fakeTelemetry.loggerProvider
128+
);
129+
telemetryWithVersion.options = { appVersion: '9.9.9' };
130+
131+
startNewSession(telemetryWithVersion);
132+
133+
expect(emittedLogs[0].attributes).to.deep.equal({
134+
[LOG_ENTRY_ATTRIBUTE_KEYS.SESSION_ID]: MOCK_SESSION_ID,
135+
[LOG_ENTRY_ATTRIBUTE_KEYS.APP_VERSION]: '9.9.9'
136+
});
137+
});
138+
});
139+
});

packages/telemetry/src/helpers.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { LoggerProvider, SeverityNumber } from '@opentelemetry/api-logs';
18+
import { SeverityNumber } from '@opentelemetry/api-logs';
1919
import * as constants from './auto-constants';
2020
import {
2121
LOG_ENTRY_ATTRIBUTE_KEYS,
@@ -54,7 +54,8 @@ export function getSessionId(): string | undefined {
5454
* 1. The client browser's sessionStorage (if available)
5555
* 2. In Cloud Logging as its own log entry
5656
*/
57-
export function startNewSession(loggerProvider: LoggerProvider): void {
57+
export function startNewSession(telemetry: Telemetry): void {
58+
const { loggerProvider } = telemetry;
5859
if (
5960
typeof sessionStorage !== 'undefined' &&
6061
typeof crypto?.randomUUID === 'function'
@@ -69,7 +70,8 @@ export function startNewSession(loggerProvider: LoggerProvider): void {
6970
severityNumber: SeverityNumber.DEBUG,
7071
body: 'Session created',
7172
attributes: {
72-
[LOG_ENTRY_ATTRIBUTE_KEYS.SESSION_ID]: sessionId
73+
[LOG_ENTRY_ATTRIBUTE_KEYS.SESSION_ID]: sessionId,
74+
[LOG_ENTRY_ATTRIBUTE_KEYS.APP_VERSION]: getAppVersion(telemetry)
7375
}
7476
});
7577
} catch (e) {

packages/telemetry/src/register.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,14 @@ export function registerTelemetry(): void {
5858
dynamicLogAttributeProviders
5959
);
6060

61+
const telemetryService = new TelemetryService(app, loggerProvider);
62+
6163
// Immediately track this as a new client session (if one doesn't exist yet)
6264
if (!getSessionId()) {
63-
startNewSession(loggerProvider);
65+
startNewSession(telemetryService);
6466
}
6567

66-
return new TelemetryService(app, loggerProvider);
68+
return telemetryService;
6769
},
6870
ComponentType.PUBLIC
6971
).setMultipleInstances(true)

0 commit comments

Comments
 (0)