@twin.org/telemetry-connector-opentelemetry 0.0.3-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,170 @@
1
+ # Telemetry Connector OpenTelemetry Examples
2
+
3
+ These examples show how to wire the connector to an OpenTelemetry SDK provider, record metrics, and connect to a Grafana/Prometheus stack.
4
+
5
+ ## Prometheus Metric Naming
6
+
7
+ When using the Prometheus exporter, the OTEL SDK appends the `unit` field to the metric
8
+ name following the OpenMetrics convention. If you set `unit: "requests"` on a counter
9
+ named `api_requests`, Prometheus will expose it as `api_requests_requests_total` — which
10
+ may not be what you want. Omit the `unit` field (or leave it empty) to get clean names:
11
+
12
+ - `api_requests` (Counter, no unit) → `api_requests_total`
13
+ - `active_connections` (UpDownCounter, no unit) → `active_connections`
14
+ - `cpu_temperature` (Gauge, no unit) → `cpu_temperature`
15
+
16
+ > This only applies to the Prometheus exporter. OTLP exporters (e.g. Grafana Agent,
17
+ > Tempo, OTEL Collector) preserve the unit as metadata and are not affected.
18
+
19
+ ## Basic Setup with Prometheus Exporter
20
+
21
+ Install the optional Prometheus exporter peer dependency alongside the connector:
22
+
23
+ ```shell
24
+ npm install @opentelemetry/exporter-prometheus
25
+ ```
26
+
27
+ Pass a `readers` config map to the connector constructor. The connector instantiates the
28
+ exporter internally when `start()` is called — no manual `MeterProvider` wiring is needed.
29
+
30
+ ```typescript
31
+ import { EntityStorageConnectorFactory } from '@twin.org/entity-storage-models';
32
+ import { MemoryEntityStorageConnector } from '@twin.org/entity-storage-connector-memory';
33
+ import { TelemetryConnectorFactory } from '@twin.org/telemetry-models';
34
+ import {
35
+ OpenTelemetryTelemetryConnector,
36
+ initSchema,
37
+ type TelemetryMetric,
38
+ type TelemetryMetricValue
39
+ } from '@twin.org/telemetry-connector-opentelemetry';
40
+
41
+ // Register entity storage so the connector can persist metric definitions and history.
42
+ initSchema();
43
+ EntityStorageConnectorFactory.register(
44
+ 'telemetry-metric',
45
+ () => new MemoryEntityStorageConnector<TelemetryMetric>({ entitySchema: 'TelemetryMetric' })
46
+ );
47
+ EntityStorageConnectorFactory.register(
48
+ 'telemetry-metric-value',
49
+ () =>
50
+ new MemoryEntityStorageConnector<TelemetryMetricValue>({ entitySchema: 'TelemetryMetricValue' })
51
+ );
52
+
53
+ const connector = new OpenTelemetryTelemetryConnector({
54
+ meterName: 'my-service',
55
+ meterVersion: '1.0.0',
56
+ readers: {
57
+ prometheus: { type: 'prometheus', port: 9464 }
58
+ }
59
+ });
60
+
61
+ await connector.start();
62
+
63
+ TelemetryConnectorFactory.register('telemetry', () => connector);
64
+ ```
65
+
66
+ ## Creating and Recording Metrics
67
+
68
+ ```typescript
69
+ import { MetricType } from '@twin.org/telemetry-models';
70
+ import { OpenTelemetryTelemetryConnector } from '@twin.org/telemetry-connector-opentelemetry';
71
+
72
+ const connector = new OpenTelemetryTelemetryConnector();
73
+
74
+ console.log(connector.className()); // OpenTelemetryTelemetryConnector
75
+
76
+ await connector.createMetric({
77
+ id: 'api-requests',
78
+ label: 'API Requests',
79
+ description: 'Total number of API requests received',
80
+ unit: 'requests',
81
+ type: MetricType.Counter
82
+ });
83
+
84
+ await connector.createMetric({
85
+ id: 'active-connections',
86
+ label: 'Active Connections',
87
+ description: 'Number of currently active WebSocket connections',
88
+ unit: 'connections',
89
+ type: MetricType.IncDecCounter
90
+ });
91
+
92
+ await connector.createMetric({
93
+ id: 'cpu-temperature',
94
+ label: 'CPU Temperature',
95
+ description: 'Current CPU temperature reading',
96
+ unit: 'celsius',
97
+ type: MetricType.Gauge
98
+ });
99
+
100
+ // Counter: increment only
101
+ await connector.addMetricValue('api-requests', 'inc');
102
+ await connector.addMetricValue('api-requests', 10, { route: '/api/health', statusCode: 200 });
103
+
104
+ // IncDecCounter: increment and decrement
105
+ await connector.addMetricValue('active-connections', 'inc');
106
+ await connector.addMetricValue('active-connections', 'dec');
107
+
108
+ // Gauge: absolute value
109
+ await connector.addMetricValue('cpu-temperature', 72.4);
110
+ await connector.addMetricValue('cpu-temperature', 68.1, { node: 'edge-1' });
111
+
112
+ const latest = await connector.getMetric('cpu-temperature');
113
+ console.log(latest.value.value); // 68.1
114
+ ```
115
+
116
+ ## Querying the Local Registry
117
+
118
+ The connector keeps an in-memory mirror of all recorded values so you can read back metrics without a separate query backend.
119
+
120
+ ```typescript
121
+ import { MetricType } from '@twin.org/telemetry-models';
122
+ import { OpenTelemetryTelemetryConnector } from '@twin.org/telemetry-connector-opentelemetry';
123
+
124
+ const connector = new OpenTelemetryTelemetryConnector();
125
+
126
+ // List all metrics of a given type
127
+ const counters = await connector.query(MetricType.Counter, undefined, 25);
128
+ console.log(counters.entities.length);
129
+
130
+ // Paginate through value history
131
+ const page1 = await connector.queryValues(
132
+ 'api-requests',
133
+ Date.now() - 3_600_000,
134
+ Date.now(),
135
+ undefined,
136
+ 20
137
+ );
138
+ console.log(page1.entities.length);
139
+
140
+ if (page1.cursor) {
141
+ const page2 = await connector.queryValues('api-requests', undefined, undefined, page1.cursor, 20);
142
+ console.log(page2.entities.length);
143
+ }
144
+ ```
145
+
146
+ ## Updating and Removing Metrics
147
+
148
+ ```typescript
149
+ import { OpenTelemetryTelemetryConnector } from '@twin.org/telemetry-connector-opentelemetry';
150
+
151
+ const connector = new OpenTelemetryTelemetryConnector();
152
+
153
+ await connector.updateMetric({
154
+ id: 'api-requests',
155
+ label: 'API Requests Total',
156
+ description: 'Cumulative count of all API requests',
157
+ unit: 'requests'
158
+ });
159
+
160
+ await connector.removeMetric('api-requests');
161
+
162
+ const result = await connector.query(undefined, undefined, 10);
163
+ console.log(result.entities.length); // 0
164
+ ```
165
+
166
+ ## Connecting to Grafana via OTLP
167
+
168
+ > OTLP exporter support is planned for a future release. The `readers` config map currently
169
+ > supports `type: "prometheus"` only. Additional reader types (OTLP HTTP, OTLP gRPC, etc.)
170
+ > will be added as further discriminated-union members of `IOpenTelemetryReaderConfig`.
@@ -0,0 +1,365 @@
1
+ # Class: OpenTelemetryTelemetryConnector
2
+
3
+ Class for performing telemetry operations using OpenTelemetry instruments.
4
+ Metric definitions and value history are persisted via an internal
5
+ EntityStorageTelemetryConnector instance created at construction time.
6
+ Call `start()` to initialise the MeterProvider and exporters; metrics can be
7
+ created and queried before start() — OTEL forwarding is simply skipped until
8
+ the MeterProvider is running.
9
+
10
+ ## Implements
11
+
12
+ - `ITelemetryConnector`
13
+
14
+ ## Constructors
15
+
16
+ ### Constructor
17
+
18
+ > **new OpenTelemetryTelemetryConnector**(`options?`): `OpenTelemetryTelemetryConnector`
19
+
20
+ Create a new instance of OpenTelemetryTelemetryConnector.
21
+ Eagerly constructs the inner EntityStorageTelemetryConnector — if the required
22
+ entity storage types are not registered this constructor will throw (fail fast).
23
+
24
+ #### Parameters
25
+
26
+ ##### options?
27
+
28
+ [`IOpenTelemetryTelemetryConnectorConstructorOptions`](../interfaces/IOpenTelemetryTelemetryConnectorConstructorOptions.md)
29
+
30
+ The options for the connector.
31
+
32
+ #### Returns
33
+
34
+ `OpenTelemetryTelemetryConnector`
35
+
36
+ ## Properties
37
+
38
+ ### NAMESPACE {#namespace}
39
+
40
+ > `readonly` `static` **NAMESPACE**: `string` = `"opentelemetry"`
41
+
42
+ The namespace supported by the telemetry connector.
43
+
44
+ ***
45
+
46
+ ### CLASS\_NAME {#class_name}
47
+
48
+ > `readonly` `static` **CLASS\_NAME**: `string`
49
+
50
+ Runtime name for the class.
51
+
52
+ ## Methods
53
+
54
+ ### className() {#classname}
55
+
56
+ > **className**(): `string`
57
+
58
+ Returns the class name of the component.
59
+
60
+ #### Returns
61
+
62
+ `string`
63
+
64
+ The class name of the component.
65
+
66
+ #### Implementation of
67
+
68
+ `ITelemetryConnector.className`
69
+
70
+ ***
71
+
72
+ ### start() {#start}
73
+
74
+ > **start**(`nodeLoggingComponentType?`): `Promise`\<`void`\>
75
+
76
+ Initialise the MeterProvider and configured exporters.
77
+ Calling start() on an already-started connector is a no-op.
78
+
79
+ #### Parameters
80
+
81
+ ##### nodeLoggingComponentType?
82
+
83
+ `string`
84
+
85
+ The node logging component type.
86
+
87
+ #### Returns
88
+
89
+ `Promise`\<`void`\>
90
+
91
+ Nothing.
92
+
93
+ #### Implementation of
94
+
95
+ `ITelemetryConnector.start`
96
+
97
+ ***
98
+
99
+ ### stop() {#stop}
100
+
101
+ > **stop**(`nodeLoggingComponentType?`): `Promise`\<`void`\>
102
+
103
+ Shut down the MeterProvider and release resources.
104
+ Calling stop() on a connector that has not been started is a no-op.
105
+
106
+ #### Parameters
107
+
108
+ ##### nodeLoggingComponentType?
109
+
110
+ `string`
111
+
112
+ The node logging component type.
113
+
114
+ #### Returns
115
+
116
+ `Promise`\<`void`\>
117
+
118
+ Nothing.
119
+
120
+ #### Implementation of
121
+
122
+ `ITelemetryConnector.stop`
123
+
124
+ ***
125
+
126
+ ### createMetric() {#createmetric}
127
+
128
+ > **createMetric**(`metric`): `Promise`\<`void`\>
129
+
130
+ Create a new metric.
131
+ The definition is always persisted via the inner entity-storage connector.
132
+ If the MeterProvider is running the corresponding OTEL instrument is also registered.
133
+
134
+ #### Parameters
135
+
136
+ ##### metric
137
+
138
+ `ITelemetryMetric`
139
+
140
+ The metric details.
141
+
142
+ #### Returns
143
+
144
+ `Promise`\<`void`\>
145
+
146
+ Nothing.
147
+
148
+ #### Implementation of
149
+
150
+ `ITelemetryConnector.createMetric`
151
+
152
+ ***
153
+
154
+ ### getMetric() {#getmetric}
155
+
156
+ > **getMetric**(`id`): `Promise`\<\{ `metric`: `ITelemetryMetric`; `value`: `ITelemetryMetricValue`; \}\>
157
+
158
+ Get the metric details and its most recent value.
159
+
160
+ #### Parameters
161
+
162
+ ##### id
163
+
164
+ `string`
165
+
166
+ The metric id.
167
+
168
+ #### Returns
169
+
170
+ `Promise`\<\{ `metric`: `ITelemetryMetric`; `value`: `ITelemetryMetricValue`; \}\>
171
+
172
+ The metric details and its most recent value.
173
+
174
+ #### Implementation of
175
+
176
+ `ITelemetryConnector.getMetric`
177
+
178
+ ***
179
+
180
+ ### updateMetric() {#updatemetric}
181
+
182
+ > **updateMetric**(`metric`): `Promise`\<`void`\>
183
+
184
+ Update the metric metadata.
185
+ Note: OpenTelemetry instrument descriptors are immutable once created.
186
+ This method updates the persisted metadata mirror; the description/unit changes
187
+ are NOT propagated to the registered MeterProvider and will not appear at the
188
+ OTEL backend (Prometheus, OTLP, etc.).
189
+
190
+ #### Parameters
191
+
192
+ ##### metric
193
+
194
+ `Omit`\<`ITelemetryMetric`, `"type"`\>
195
+
196
+ The metric details (type cannot be changed).
197
+
198
+ #### Returns
199
+
200
+ `Promise`\<`void`\>
201
+
202
+ Nothing.
203
+
204
+ #### Implementation of
205
+
206
+ `ITelemetryConnector.updateMetric`
207
+
208
+ ***
209
+
210
+ ### addMetricValue() {#addmetricvalue}
211
+
212
+ > **addMetricValue**(`id`, `value`, `customData?`): `Promise`\<`string`\>
213
+
214
+ Record a metric value.
215
+ Entity storage always receives the value first and performs all validation.
216
+ If the MeterProvider is running the measurement is also forwarded to the OTEL instrument.
217
+ Counter accepts positive integers or "inc".
218
+ UpDownCounter accepts integers (positive or negative) or "inc"/"dec".
219
+ Gauge accepts any number.
220
+
221
+ #### Parameters
222
+
223
+ ##### id
224
+
225
+ `string`
226
+
227
+ The id of the metric.
228
+
229
+ ##### value
230
+
231
+ `number` \| `"inc"` \| `"dec"`
232
+
233
+ The value for the operation.
234
+
235
+ ##### customData?
236
+
237
+ Optional custom data forwarded as OTEL attributes.
238
+
239
+ #### Returns
240
+
241
+ `Promise`\<`string`\>
242
+
243
+ The id of the new metric value entry.
244
+
245
+ #### Implementation of
246
+
247
+ `ITelemetryConnector.addMetricValue`
248
+
249
+ ***
250
+
251
+ ### removeMetric() {#removemetric}
252
+
253
+ > **removeMetric**(`id`): `Promise`\<`void`\>
254
+
255
+ Remove a metric and its persisted value history.
256
+ Note: OpenTelemetry exposes no API to deregister an instrument from a Meter,
257
+ so the underlying Counter/UpDownCounter/Gauge remains resident for the lifetime
258
+ of the process. Re-creating a metric with the same id but a different MetricType
259
+ is therefore not safe.
260
+
261
+ #### Parameters
262
+
263
+ ##### id
264
+
265
+ `string`
266
+
267
+ The id of the metric.
268
+
269
+ #### Returns
270
+
271
+ `Promise`\<`void`\>
272
+
273
+ Nothing.
274
+
275
+ #### Implementation of
276
+
277
+ `ITelemetryConnector.removeMetric`
278
+
279
+ ***
280
+
281
+ ### query() {#query}
282
+
283
+ > **query**(`type?`, `cursor?`, `limit?`): `Promise`\<\{ `entities`: `ITelemetryMetric`[]; `cursor?`: `string`; \}\>
284
+
285
+ Query the registered metrics, optionally filtered by type.
286
+
287
+ #### Parameters
288
+
289
+ ##### type?
290
+
291
+ `MetricType`
292
+
293
+ The type of the metric.
294
+
295
+ ##### cursor?
296
+
297
+ `string`
298
+
299
+ The cursor to request the next page.
300
+
301
+ ##### limit?
302
+
303
+ `number`
304
+
305
+ Limit the number of entities to return.
306
+
307
+ #### Returns
308
+
309
+ `Promise`\<\{ `entities`: `ITelemetryMetric`[]; `cursor?`: `string`; \}\>
310
+
311
+ The matching metrics and an optional cursor for the next page.
312
+
313
+ #### Implementation of
314
+
315
+ `ITelemetryConnector.query`
316
+
317
+ ***
318
+
319
+ ### queryValues() {#queryvalues}
320
+
321
+ > **queryValues**(`id`, `timeStart?`, `timeEnd?`, `cursor?`, `limit?`): `Promise`\<\{ `metric`: `ITelemetryMetric`; `entities`: `ITelemetryMetricValue`[]; `cursor?`: `string`; \}\>
322
+
323
+ Query the recorded values for a metric, ordered by most recent first.
324
+
325
+ #### Parameters
326
+
327
+ ##### id
328
+
329
+ `string`
330
+
331
+ The id of the metric.
332
+
333
+ ##### timeStart?
334
+
335
+ `number`
336
+
337
+ The inclusive start time (epoch ms).
338
+
339
+ ##### timeEnd?
340
+
341
+ `number`
342
+
343
+ The inclusive end time (epoch ms).
344
+
345
+ ##### cursor?
346
+
347
+ `string`
348
+
349
+ The cursor returned by the previous call.
350
+
351
+ ##### limit?
352
+
353
+ `number`
354
+
355
+ Limit the number of values to return.
356
+
357
+ #### Returns
358
+
359
+ `Promise`\<\{ `metric`: `ITelemetryMetric`; `entities`: `ITelemetryMetricValue`[]; `cursor?`: `string`; \}\>
360
+
361
+ The metric details, matching values, and an optional cursor for the next page.
362
+
363
+ #### Implementation of
364
+
365
+ `ITelemetryConnector.queryValues`
@@ -0,0 +1,14 @@
1
+ # @twin.org/telemetry-connector-opentelemetry
2
+
3
+ ## Classes
4
+
5
+ - [OpenTelemetryTelemetryConnector](classes/OpenTelemetryTelemetryConnector.md)
6
+
7
+ ## Interfaces
8
+
9
+ - [IOpenTelemetryPrometheusReaderConfig](interfaces/IOpenTelemetryPrometheusReaderConfig.md)
10
+ - [IOpenTelemetryTelemetryConnectorConstructorOptions](interfaces/IOpenTelemetryTelemetryConnectorConstructorOptions.md)
11
+
12
+ ## Type Aliases
13
+
14
+ - [IOpenTelemetryReaderConfig](type-aliases/IOpenTelemetryReaderConfig.md)
@@ -0,0 +1,63 @@
1
+ # Interface: IOpenTelemetryPrometheusReaderConfig
2
+
3
+ Configuration for a Prometheus scrape-endpoint reader.
4
+ The connector instantiates a PrometheusExporter from these options in start().
5
+
6
+ ## Properties
7
+
8
+ ### type {#type}
9
+
10
+ > **type**: `"prometheus"`
11
+
12
+ Discriminator — must be "prometheus".
13
+
14
+ ***
15
+
16
+ ### port? {#port}
17
+
18
+ > `optional` **port?**: `number`
19
+
20
+ TCP port the Prometheus HTTP server listens on.
21
+
22
+ #### Default
23
+
24
+ ```ts
25
+ 9464
26
+ ```
27
+
28
+ ***
29
+
30
+ ### endpoint? {#endpoint}
31
+
32
+ > `optional` **endpoint?**: `string`
33
+
34
+ HTTP path that Prometheus scrapes.
35
+
36
+ #### Default
37
+
38
+ ```ts
39
+ /metrics
40
+ ```
41
+
42
+ ***
43
+
44
+ ### startServer? {#startserver}
45
+
46
+ > `optional` **startServer?**: `boolean`
47
+
48
+ Whether to start the built-in HTTP server automatically.
49
+ Set to false if you manage the server externally.
50
+
51
+ #### Default
52
+
53
+ ```ts
54
+ true
55
+ ```
56
+
57
+ ***
58
+
59
+ ### prefix? {#prefix}
60
+
61
+ > `optional` **prefix?**: `string`
62
+
63
+ Optional string prepended to every exported metric name.
@@ -0,0 +1,85 @@
1
+ # Interface: IOpenTelemetryTelemetryConnectorConstructorOptions
2
+
3
+ The options for the OpenTelemetry telemetry connector constructor.
4
+
5
+ ## Properties
6
+
7
+ ### meterName? {#metername}
8
+
9
+ > `optional` **meterName?**: `string`
10
+
11
+ The name of the OpenTelemetry meter used to create instruments.
12
+
13
+ #### Default
14
+
15
+ ```ts
16
+ twin-telemetry
17
+ ```
18
+
19
+ ***
20
+
21
+ ### meterVersion? {#meterversion}
22
+
23
+ > `optional` **meterVersion?**: `string`
24
+
25
+ The version reported by the OpenTelemetry meter.
26
+
27
+ #### Default
28
+
29
+ ```ts
30
+ 0.0.1
31
+ ```
32
+
33
+ ***
34
+
35
+ ### readers? {#readers}
36
+
37
+ > `optional` **readers?**: `object`
38
+
39
+ Named metric-reader configurations keyed by an arbitrary id.
40
+ Each entry's `type` field determines which exporter the connector instantiates
41
+ in start(). Omit or pass an empty object for a no-op provider (useful for tests).
42
+ Example: { "main": { type: "prometheus", port: 9464 } }
43
+
44
+ #### Index Signature
45
+
46
+ \[`id`: `string`\]: [`IOpenTelemetryPrometheusReaderConfig`](IOpenTelemetryPrometheusReaderConfig.md)
47
+
48
+ ***
49
+
50
+ ### loggingComponentType? {#loggingcomponenttype}
51
+
52
+ > `optional` **loggingComponentType?**: `string`
53
+
54
+ The component type to use for logging inside the connector and the inner
55
+ entity-storage connector. When omitted logging is disabled.
56
+
57
+ ***
58
+
59
+ ### telemetryMetricStorageConnectorType? {#telemetrymetricstorageconnectortype}
60
+
61
+ > `optional` **telemetryMetricStorageConnectorType?**: `string`
62
+
63
+ The entity storage connector type to use for storing metric definitions.
64
+ Must be registered in `EntityStorageConnectorFactory` before calling `start()`.
65
+
66
+ #### Default
67
+
68
+ ```ts
69
+ telemetry-metric
70
+ ```
71
+
72
+ ***
73
+
74
+ ### telemetryMetricValueStorageConnectorType? {#telemetrymetricvaluestorageconnectortype}
75
+
76
+ > `optional` **telemetryMetricValueStorageConnectorType?**: `string`
77
+
78
+ The entity storage connector type to use for storing metric values.
79
+ Must be registered in `EntityStorageConnectorFactory` before calling `start()`.
80
+
81
+ #### Default
82
+
83
+ ```ts
84
+ telemetry-metric-value
85
+ ```
@@ -0,0 +1,6 @@
1
+ # Type Alias: IOpenTelemetryReaderConfig
2
+
3
+ > **IOpenTelemetryReaderConfig** = [`IOpenTelemetryPrometheusReaderConfig`](../interfaces/IOpenTelemetryPrometheusReaderConfig.md)
4
+
5
+ Discriminated union of all supported metric-reader configurations.
6
+ Add new members here when additional exporter types are implemented.
@@ -0,0 +1,13 @@
1
+ {
2
+ "error": {
3
+ "openTelemetryTelemetryConnector": {
4
+ "unknownReaderType": "The reader type \"{type}\" is not supported, valid values are: prometheus"
5
+ }
6
+ },
7
+ "info": {
8
+ "openTelemetryTelemetryConnector": {
9
+ "connectorStarted": "The connector was started with \"{readerCount}\" metric reader(s)",
10
+ "connectorStopped": "The connector was stopped"
11
+ }
12
+ }
13
+ }