Skip to content

Commit 836a427

Browse files
authored
Add option to explicitly set app version for telemetry (#9388)
* Add option to explicitly set app version for telemetry * update deps * misc cleanup
1 parent 073672e commit 836a427

File tree

10 files changed

+104
-19
lines changed

10 files changed

+104
-19
lines changed

common/api-review/telemetry-angular.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface Telemetry {
2323

2424
// @public
2525
export interface TelemetryOptions {
26+
appVersion?: string;
2627
endpointUrl?: string;
2728
}
2829

common/api-review/telemetry-react.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface Telemetry {
2222

2323
// @public
2424
export interface TelemetryOptions {
25+
appVersion?: string;
2526
endpointUrl?: string;
2627
}
2728

common/api-review/telemetry.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export interface Telemetry {
3131

3232
// @public
3333
export interface TelemetryOptions {
34+
appVersion?: string;
3435
endpointUrl?: string;
3536
}
3637

docs-devsite/telemetry_.telemetryoptions.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,19 @@ export interface TelemetryOptions
2222

2323
| Property | Type | Description |
2424
| --- | --- | --- |
25+
| [appVersion](./telemetry_.telemetryoptions.md#telemetryoptionsappversion) | string | The version of the application. This should be a unique string that identifies the snapshot of code to be deployed, such as "1.0.2". If not specified, other default locations will be checked for an identifier. Setting a value here takes precedent over any other values. |
2526
| [endpointUrl](./telemetry_.telemetryoptions.md#telemetryoptionsendpointurl) | string | The URL for the endpoint to which telemetry data should be sent, in the Open Telemetry format. By default, data will be sent to Firebase. |
2627

28+
## TelemetryOptions.appVersion
29+
30+
The version of the application. This should be a unique string that identifies the snapshot of code to be deployed, such as "1.0.2". If not specified, other default locations will be checked for an identifier. Setting a value here takes precedent over any other values.
31+
32+
<b>Signature:</b>
33+
34+
```typescript
35+
appVersion?: string;
36+
```
37+
2738
## TelemetryOptions.endpointUrl
2839

2940
The URL for the endpoint to which telemetry data should be sent, in the Open Telemetry format. By default, data will be sent to Firebase.

docs-devsite/telemetry_angular.telemetryoptions.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,19 @@ export interface TelemetryOptions
2222

2323
| Property | Type | Description |
2424
| --- | --- | --- |
25+
| [appVersion](./telemetry_angular.telemetryoptions.md#telemetryoptionsappversion) | string | The version of the application. This should be a unique string that identifies the snapshot of code to be deployed, such as "1.0.2". If not specified, other default locations will be checked for an identifier. Setting a value here takes precedent over any other values. |
2526
| [endpointUrl](./telemetry_angular.telemetryoptions.md#telemetryoptionsendpointurl) | string | The URL for the endpoint to which telemetry data should be sent, in the Open Telemetry format. By default, data will be sent to Firebase. |
2627

28+
## TelemetryOptions.appVersion
29+
30+
The version of the application. This should be a unique string that identifies the snapshot of code to be deployed, such as "1.0.2". If not specified, other default locations will be checked for an identifier. Setting a value here takes precedent over any other values.
31+
32+
<b>Signature:</b>
33+
34+
```typescript
35+
appVersion?: string;
36+
```
37+
2738
## TelemetryOptions.endpointUrl
2839

2940
The URL for the endpoint to which telemetry data should be sent, in the Open Telemetry format. By default, data will be sent to Firebase.

docs-devsite/telemetry_react.telemetryoptions.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,19 @@ export interface TelemetryOptions
2222

2323
| Property | Type | Description |
2424
| --- | --- | --- |
25+
| [appVersion](./telemetry_react.telemetryoptions.md#telemetryoptionsappversion) | string | The version of the application. This should be a unique string that identifies the snapshot of code to be deployed, such as "1.0.2". If not specified, other default locations will be checked for an identifier. Setting a value here takes precedent over any other values. |
2526
| [endpointUrl](./telemetry_react.telemetryoptions.md#telemetryoptionsendpointurl) | string | The URL for the endpoint to which telemetry data should be sent, in the Open Telemetry format. By default, data will be sent to Firebase. |
2627

28+
## TelemetryOptions.appVersion
29+
30+
The version of the application. This should be a unique string that identifies the snapshot of code to be deployed, such as "1.0.2". If not specified, other default locations will be checked for an identifier. Setting a value here takes precedent over any other values.
31+
32+
<b>Signature:</b>
33+
34+
```typescript
35+
appVersion?: string;
36+
```
37+
2738
## TelemetryOptions.endpointUrl
2839

2940
The URL for the endpoint to which telemetry data should be sent, in the Open Telemetry format. By default, data will be sent to Firebase.

packages/telemetry/src/api.test.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ describe('Top level API', () => {
124124
expect(log.body).to.equal('This is a test error');
125125
expect(log.attributes).to.deep.equal({
126126
'error.type': 'TestError',
127-
'error.stack': '...stack trace...'
127+
'error.stack': '...stack trace...',
128+
'app.version': 'unset'
128129
});
129130
});
130131

@@ -140,7 +141,8 @@ describe('Top level API', () => {
140141
expect(log.body).to.equal('error with no stack');
141142
expect(log.attributes).to.deep.equal({
142143
'error.type': 'Error',
143-
'error.stack': 'No stack trace available'
144+
'error.stack': 'No stack trace available',
145+
'app.version': 'unset'
144146
});
145147
});
146148

@@ -151,7 +153,9 @@ describe('Top level API', () => {
151153
const log = emittedLogs[0];
152154
expect(log.severityNumber).to.equal(SeverityNumber.ERROR);
153155
expect(log.body).to.equal('a string error');
154-
expect(log.attributes).to.deep.equal({});
156+
expect(log.attributes).to.deep.equal({
157+
'app.version': 'unset'
158+
});
155159
});
156160

157161
it('should capture an unknown error type correctly', () => {
@@ -161,7 +165,9 @@ describe('Top level API', () => {
161165
const log = emittedLogs[0];
162166
expect(log.severityNumber).to.equal(SeverityNumber.ERROR);
163167
expect(log.body).to.equal('Unknown error type: number');
164-
expect(log.attributes).to.deep.equal({});
168+
expect(log.attributes).to.deep.equal({
169+
'app.version': 'unset'
170+
});
165171
});
166172

167173
it('should propagate trace context', async () => {
@@ -187,6 +193,7 @@ describe('Top level API', () => {
187193
expect(emittedLogs[0].attributes).to.deep.equal({
188194
'error.type': 'TestError',
189195
'error.stack': '...stack trace...',
196+
'app.version': 'unset',
190197
'logging.googleapis.com/trace': `projects/${PROJECT_ID}/traces/my-trace`,
191198
'logging.googleapis.com/spanId': `my-span`
192199
});
@@ -211,6 +218,7 @@ describe('Top level API', () => {
211218
expect(log.attributes).to.deep.equal({
212219
'error.type': 'TestError',
213220
'error.stack': '...stack trace...',
221+
'app.version': 'unset',
214222
strAttr: 'string attribute',
215223
mapAttr: {
216224
boolAttr: true,
@@ -219,6 +227,24 @@ describe('Top level API', () => {
219227
arrAttr: [1, 2, 3]
220228
});
221229
});
230+
231+
it('should use explicit app version when provided', () => {
232+
const telemetry = new TelemetryService(
233+
fakeTelemetry.app,
234+
fakeTelemetry.loggerProvider
235+
);
236+
telemetry.options = {
237+
appVersion: '1.0.0'
238+
};
239+
240+
captureError(telemetry, 'a string error');
241+
242+
expect(emittedLogs.length).to.equal(1);
243+
const log = emittedLogs[0];
244+
expect(log.attributes).to.deep.equal({
245+
'app.version': '1.0.0'
246+
});
247+
});
222248
});
223249

224250
describe('flush()', () => {

packages/telemetry/src/api.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,13 @@ export function getTelemetry(
5454
TELEMETRY_TYPE
5555
);
5656
const identifier = options?.endpointUrl || '';
57-
return telemetryProvider.getImmediate({ identifier });
57+
const telemetry: TelemetryService = telemetryProvider.getImmediate({
58+
identifier
59+
});
60+
if (options) {
61+
telemetry.options = options;
62+
}
63+
return telemetry;
5864
}
5965

6066
/**
@@ -72,20 +78,27 @@ export function captureError(
7278
attributes?: AnyValueMap
7379
): void {
7480
const logger = telemetry.loggerProvider.getLogger('error-logger');
81+
const customAttributes = attributes || {};
7582

83+
// Add trace metadata
7684
const activeSpanContext = trace.getActiveSpan()?.spanContext();
77-
const traceAttributes = {} as AnyValueMap;
7885
if (telemetry.app.options.projectId && activeSpanContext?.traceId) {
79-
traceAttributes[
86+
customAttributes[
8087
'logging.googleapis.com/trace'
8188
] = `projects/${telemetry.app.options.projectId}/traces/${activeSpanContext.traceId}`;
8289
if (activeSpanContext?.spanId) {
83-
traceAttributes['logging.googleapis.com/spanId'] =
90+
customAttributes['logging.googleapis.com/spanId'] =
8491
activeSpanContext.spanId;
8592
}
8693
}
8794

88-
const customAttributes = attributes || {};
95+
// Add app version metadata
96+
let appVersion = 'unset';
97+
// TODO: implement app version fallback logic
98+
if ((telemetry as TelemetryService).options?.appVersion) {
99+
appVersion = (telemetry as TelemetryService).options!.appVersion!;
100+
}
101+
customAttributes['app.version'] = appVersion;
89102

90103
if (error instanceof Error) {
91104
logger.emit({
@@ -94,27 +107,20 @@ export function captureError(
94107
attributes: {
95108
'error.type': error.name || 'Error',
96109
'error.stack': error.stack || 'No stack trace available',
97-
...traceAttributes,
98110
...customAttributes
99111
}
100112
});
101113
} else if (typeof error === 'string') {
102114
logger.emit({
103115
severityNumber: SeverityNumber.ERROR,
104116
body: error,
105-
attributes: {
106-
...traceAttributes,
107-
...customAttributes
108-
}
117+
attributes: customAttributes
109118
});
110119
} else {
111120
logger.emit({
112121
severityNumber: SeverityNumber.ERROR,
113122
body: `Unknown error type: ${typeof error}`,
114-
attributes: {
115-
...traceAttributes,
116-
...customAttributes
117-
}
123+
attributes: customAttributes
118124
});
119125
}
120126
}

packages/telemetry/src/public-types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,11 @@ export interface TelemetryOptions {
4646
* By default, data will be sent to Firebase.
4747
*/
4848
endpointUrl?: string;
49+
50+
/**
51+
* The version of the application. This should be a unique string that identifies the snapshot of
52+
* code to be deployed, such as "1.0.2". If not specified, other default locations will be checked
53+
* for an identifier. Setting a value here takes precedent over any other values.
54+
*/
55+
appVersion?: string;
4956
}

packages/telemetry/src/service.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,23 @@
1616
*/
1717

1818
import { _FirebaseService, FirebaseApp } from '@firebase/app';
19-
import { Telemetry } from './public-types';
19+
import { Telemetry, TelemetryOptions } from './public-types';
2020
import { LoggerProvider } from '@opentelemetry/sdk-logs';
2121

2222
export class TelemetryService implements Telemetry, _FirebaseService {
23+
private _options?: TelemetryOptions;
24+
2325
constructor(public app: FirebaseApp, public loggerProvider: LoggerProvider) {}
2426

2527
_delete(): Promise<void> {
2628
return Promise.resolve();
2729
}
30+
31+
set options(optionsToSet: TelemetryOptions) {
32+
this._options = optionsToSet;
33+
}
34+
35+
get options(): TelemetryOptions | undefined {
36+
return this._options;
37+
}
2838
}

0 commit comments

Comments
 (0)