Skip to content

Commit 2d555e9

Browse files
committed
feat: add the ability to get metrics meters
This allows apps to define custom metrics in a safe way that ensures Reliability Kit was the thing that set up OpenTelemetry.
1 parent 1d74508 commit 2d555e9

File tree

3 files changed

+99
-8
lines changed

3 files changed

+99
-8
lines changed

packages/opentelemetry/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ An [OpenTelemetry](https://opentelemetry.io/docs/what-is-opentelemetry/) client
1111
* [Automated setup with `--require`](#automated-setup-with---require)
1212
* [Automated setup with `require()`](#automated-setup-with-require)
1313
* [Manual setup](#manual-setup)
14+
* [Sending custom metrics](#sending-custom-metrics)
1415
* [Running in production](#running-in-production)
1516
* [Production metrics](#production-metrics)
1617
* [Production tracing](#production-tracing)
@@ -154,6 +155,31 @@ opentelemetry.setup({ /* ... */ });
154155

155156
This method returns any SDK instances created during setup. Calling this method a second time will return the same instances without rerunning setup.
156157

158+
### Sending custom metrics
159+
160+
Many metrics are taken care of by OpenTelemetry's auto-instrumentation (e.g. HTTP request data), but you sometimes need to send your own metrics. We expose the OpenTelemetry `getMeter` method ([documentation](https://opentelemetry.io/docs/languages/js/instrumentation/#acquiring-a-meter)) which allows you to do this.
161+
162+
In your code, load in the `getMeter` function:
163+
164+
```js
165+
import getMeter from '@dotcom-reliability-kit/opentelemetry';
166+
// or
167+
const { getMeter } = require('@dotcom-reliability-kit/opentelemetry');
168+
```
169+
170+
You can now use it in the same way as the built-in OpenTelemetry equivalent. For more information, see the [OpenTelemetry Meter documentation](https://opentelemetry.io/docs/specs/otel/metrics/api/#meter).
171+
172+
```js
173+
// Assumes that `app` is an Express application instance
174+
const meter = getMeter('my-app');
175+
const hitCounter = meter.createCounter('my-app.hits');
176+
177+
app.get('/', (request, response) => {
178+
hitCounter.add(1);
179+
response.send('Thanks for visiting');
180+
});
181+
```
182+
157183
### Running in production
158184

159185
#### Production metrics

packages/opentelemetry/lib/index.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const logger = require('@dotcom-reliability-kit/logger');
3232
/**
3333
* Stores the singleton instances that were created during OpenTelemetry setup.
3434
*
35-
* @type {Instances}
35+
* @type {Instances | undefined}
3636
*/
3737
let instances;
3838

@@ -101,4 +101,31 @@ function setupOpenTelemetry({
101101
return instances;
102102
}
103103

104+
/**
105+
* Get a metrics meter from the configured OpenTelemetry SDK.
106+
*
107+
* @param {string} name
108+
* The meter name.
109+
* @param {string} [version]
110+
* The meter version.
111+
* @param {opentelemetry.api.MeterOptions} [options]
112+
* Additional configuration options for the meter.
113+
* @returns {opentelemetry.api.Meter}
114+
* Returns a metrics meter.
115+
*/
116+
function getMeter(name, version, options) {
117+
if (!instances) {
118+
throw Object.assign(
119+
new Error(
120+
'Reliability Kit OpenTelemetry must be set up before meters can be created. See the setup guide for more information: https://github.com/Financial-Times/dotcom-reliability-kit/tree/main/packages/opentelemetry#setup'
121+
),
122+
{
123+
code: 'OTEL_MISSING_SETUP'
124+
}
125+
);
126+
}
127+
return opentelemetry.api.metrics.getMeter(name, version, options);
128+
}
129+
104130
exports.setup = setupOpenTelemetry;
131+
exports.getMeter = getMeter;

packages/opentelemetry/test/unit/lib/index.spec.js

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ describe('@dotcom-reliability-kit/opentelemetry', () => {
2020
let createMetricsConfig;
2121
let createResourceConfig;
2222
let createTracingConfig;
23-
let diag;
24-
let DiagLogLevel;
23+
let api;
2524
let logger;
2625
let NodeSDK;
2726
let opentelemetry;
@@ -40,13 +39,12 @@ describe('@dotcom-reliability-kit/opentelemetry', () => {
4039
require('../../../lib/config/resource').createResourceConfig;
4140
createTracingConfig =
4241
require('../../../lib/config/tracing').createTracingConfig;
43-
diag = require('@opentelemetry/sdk-node').api.diag;
44-
DiagLogLevel = require('@opentelemetry/sdk-node').api.DiagLogLevel;
42+
api = require('@opentelemetry/sdk-node').api;
4543
NodeSDK = require('@opentelemetry/sdk-node').NodeSDK;
4644
logger = require('@dotcom-reliability-kit/logger');
4745

4846
logger.createChildLogger.mockReturnValue('mock child logger');
49-
DiagLogLevel.INFO = 'mock info log level';
47+
api.DiagLogLevel.INFO = 'mock info log level';
5048

5149
opentelemetry = require('../../../lib/index');
5250
}
@@ -73,8 +71,8 @@ describe('@dotcom-reliability-kit/opentelemetry', () => {
7371
expect(logger.createChildLogger).toHaveBeenCalledWith({
7472
event: 'OTEL_INTERNALS'
7573
});
76-
expect(diag.setLogger).toHaveBeenCalledTimes(1);
77-
expect(diag.setLogger).toHaveBeenCalledWith(
74+
expect(api.diag.setLogger).toHaveBeenCalledTimes(1);
75+
expect(api.diag.setLogger).toHaveBeenCalledWith(
7876
'mock child logger',
7977
'mock info log level'
8078
);
@@ -254,4 +252,44 @@ describe('@dotcom-reliability-kit/opentelemetry', () => {
254252
});
255253
});
256254
});
255+
256+
describe('.getMeter(name, version, options)', () => {
257+
let meter;
258+
259+
beforeEach(() => {
260+
reloadAllModules();
261+
api.metrics.getMeter.mockReturnValue('mock-meter');
262+
opentelemetry.setup();
263+
meter = opentelemetry.getMeter(
264+
'mock-name',
265+
'mock-version',
266+
'mock-options'
267+
);
268+
});
269+
270+
it('creates and returns a global meter', () => {
271+
expect(api.metrics.getMeter).toHaveBeenCalledTimes(1);
272+
expect(api.metrics.getMeter).toHaveBeenCalledWith(
273+
'mock-name',
274+
'mock-version',
275+
'mock-options'
276+
);
277+
expect(meter).toEqual('mock-meter');
278+
});
279+
280+
describe('when OpenTelemetry has not been set up via Reliability Kit', () => {
281+
beforeEach(() => {
282+
reloadAllModules();
283+
});
284+
285+
it('throws an error', () => {
286+
expect.hasAssertions();
287+
try {
288+
opentelemetry.getMeter('mock-name', 'mock-version', 'mock-options');
289+
} catch (error) {
290+
expect(error.code).toEqual('OTEL_MISSING_SETUP');
291+
}
292+
});
293+
});
294+
});
257295
});

0 commit comments

Comments
 (0)