Skip to content

Commit 66d69bb

Browse files
committed
feat: add a service ID property
This is going to be required to send OpenTelemetry metrics as our systems will only accept metrics with the corresponding semantic resource attribute.
1 parent b24c3f6 commit 66d69bb

File tree

4 files changed

+67
-3
lines changed

4 files changed

+67
-3
lines changed

packages/app-info/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ A utility to get application information (e.g. the system code) in a consistent
1414
* [`appInfo.cloudProvider`](#appinfocloudprovider)
1515
* [`appInfo.herokuAppId`](#appinfoherokuappid)
1616
* [`appInfo.herokuDynoId`](#appinfoherokudynoid)
17+
* [`appInfo.instanceId`](#appinfoinstanceid)
1718
* [`appInfo.semanticConventions`](#appinfosemanticconventions)
1819
* [Migrating](#migrating)
1920
* [Contributing](#contributing)
@@ -100,6 +101,10 @@ Get the `process.env.HEROKU_DYNO_ID` which is the dyno identifier
100101

101102
This is derived from the dyno metadata
102103

104+
### `appInfo.instanceId`
105+
106+
Get the ID of the instance that's running the application. This is derived from `process.env.HEROKU_DYNO_ID` if present, otherwise it will be set to a random UUID that identifies the currently running process.
107+
103108
### `appInfo.semanticConventions`
104109

105110
This object contains aliases for the main `appInfo` properties that correspond to OpenTelemetry's [Semantic Conventions](https://opentelemetry.io/docs/concepts/semantic-conventions/). We use the following mapping:
@@ -109,6 +114,7 @@ This object contains aliases for the main `appInfo` properties that correspond t
109114
* `appInfo.semanticConventions.deployment.environment` aliases `appInfo.environment`
110115
* `appInfo.semanticConventions.service.name` aliases `appInfo.systemCode`
111116
* `appInfo.semanticConventions.service.version` aliases `appInfo.releaseVersion`
117+
* `appInfo.semanticConventions.service.instance.id` aliases `appInfo.instanceId`
112118

113119

114120
## Migrating

packages/app-info/lib/index.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const path = require('node:path');
2+
const { randomUUID } = require('node:crypto');
23

34
// This package relies on Heroku and AWS Lambda environment variables.
45
// Documentation for these variables is available here:
@@ -149,6 +150,14 @@ exports.herokuAppId = process.env.HEROKU_APP_ID || null;
149150
*/
150151
exports.herokuDynoId = process.env.HEROKU_DYNO_ID || null;
151152

153+
/**
154+
* The ID of the running instance of the service.
155+
*
156+
* @readonly
157+
* @type {string}
158+
*/
159+
exports.instanceId = process.env.HEROKU_DYNO_ID || randomUUID();
160+
152161
/**
153162
* @type {import('@dotcom-reliability-kit/app-info').SemanticConventions}
154163
*/
@@ -162,7 +171,10 @@ exports.semanticConventions = {
162171
},
163172
service: {
164173
name: exports.systemCode,
165-
version: exports.releaseVersion
174+
version: exports.releaseVersion,
175+
instance: {
176+
id: exports.instanceId
177+
}
166178
}
167179
};
168180

packages/app-info/test/unit/lib/index.spec.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
jest.mock('node:crypto');
2+
13
describe('@dotcom-reliability-kit/app-info', () => {
24
let appInfo;
35

@@ -375,6 +377,35 @@ describe('@dotcom-reliability-kit/app-info', () => {
375377
});
376378
});
377379

380+
describe('.instanceId', () => {
381+
let randomUUID;
382+
383+
beforeEach(() => {
384+
jest.resetModules();
385+
process.env.HEROKU_DYNO_ID = 'mock-heroku-dyno-id';
386+
appInfo = require('../../../lib');
387+
});
388+
389+
it('is set to `process.env.HEROKU_DYNO_ID`', () => {
390+
expect(appInfo.instanceId).toBe('mock-heroku-dyno-id');
391+
});
392+
393+
describe('when `process.env.HEROKU_DYNO_ID` is not defined', () => {
394+
beforeEach(() => {
395+
jest.resetModules();
396+
randomUUID = require('node:crypto').randomUUID;
397+
randomUUID.mockReturnValue('mock-generated-uuid');
398+
delete process.env.HEROKU_DYNO_ID;
399+
appInfo = require('../../../lib');
400+
});
401+
402+
it('is set to a random UUID', () => {
403+
expect(randomUUID).toHaveBeenCalledTimes(1);
404+
expect(appInfo.instanceId).toBe('mock-generated-uuid');
405+
});
406+
});
407+
});
408+
378409
describe('.semanticConventions', () => {
379410
describe('.cloud', () => {
380411
describe('.provider', () => {
@@ -418,6 +449,16 @@ describe('@dotcom-reliability-kit/app-info', () => {
418449
);
419450
});
420451
});
452+
453+
describe('.instance', () => {
454+
describe('.id', () => {
455+
it('is an alias of `instanceId`', () => {
456+
expect(appInfo.semanticConventions.service.instance.id).toBe(
457+
appInfo.instanceId
458+
);
459+
});
460+
});
461+
});
421462
});
422463
});
423464
});

packages/app-info/types/index.d.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,22 @@ declare module '@dotcom-reliability-kit/app-info' {
99
export const cloudProvider: string | null;
1010
export const herokuAppId: string | null;
1111
export const herokuDynoId: string | null;
12+
export const instanceId: string;
1213

1314
export type SemanticConventions = {
1415
cloud: {
1516
provider: string | null,
1617
region: string | null
1718
},
1819
deployment: {
19-
environment: string | null
20+
environment: string
2021
},
2122
service: {
2223
name: string | null
23-
version: string | null
24+
version: string | null,
25+
instance: {
26+
id: string
27+
}
2428
}
2529
};
2630

@@ -35,6 +39,7 @@ declare module '@dotcom-reliability-kit/app-info' {
3539
cloudProvider: typeof cloudProvider,
3640
herokuAppId: typeof herokuAppId,
3741
herokuDynoId: typeof herokuDynoId,
42+
instanceId: typeof instanceId,
3843
semanticConventions: SemanticConventions
3944
};
4045

0 commit comments

Comments
 (0)