@twin.org/telemetry-connector-opentelemetry 0.9.2-next.7 → 0.9.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.
- package/dist/es/models/IOpenTelemetryTelemetryConnectorConfig.js.map +1 -1
- package/dist/es/models/IOpenTelemetryTelemetryConnectorConstructorOptions.js.map +1 -1
- package/dist/es/openTelemetryTelemetryConnector.js +71 -111
- package/dist/es/openTelemetryTelemetryConnector.js.map +1 -1
- package/dist/types/models/IOpenTelemetryTelemetryConnectorConfig.d.ts +0 -19
- package/dist/types/models/IOpenTelemetryTelemetryConnectorConstructorOptions.d.ts +0 -17
- package/dist/types/openTelemetryTelemetryConnector.d.ts +16 -60
- package/docs/changelog.md +25 -0
- package/docs/reference/classes/OpenTelemetryTelemetryConnector.md +15 -164
- package/docs/reference/interfaces/IOpenTelemetryTelemetryConnectorConfig.md +0 -50
- package/docs/reference/interfaces/IOpenTelemetryTelemetryConnectorConstructorOptions.md +0 -39
- package/locales/en.json +7 -0
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IOpenTelemetryTelemetryConnectorConfig.js","sourceRoot":"","sources":["../../../src/models/IOpenTelemetryTelemetryConnectorConfig.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IOpenTelemetryReaderConfig } from \"./IOpenTelemetryReaderConfig.js\";\n\n/**\n * The options for the OpenTelemetry telemetry connector constructor.\n */\nexport interface IOpenTelemetryTelemetryConnectorConfig {\n\t/**\n\t *
|
|
1
|
+
{"version":3,"file":"IOpenTelemetryTelemetryConnectorConfig.js","sourceRoot":"","sources":["../../../src/models/IOpenTelemetryTelemetryConnectorConfig.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IOpenTelemetryReaderConfig } from \"./IOpenTelemetryReaderConfig.js\";\n\n/**\n * The options for the OpenTelemetry telemetry connector constructor.\n */\nexport interface IOpenTelemetryTelemetryConnectorConfig {\n\t/**\n\t * The name of the OpenTelemetry meter used to create instruments.\n\t * @default twin-telemetry\n\t */\n\tmeterName?: string;\n\n\t/**\n\t * The version reported by the OpenTelemetry meter.\n\t * @default 1.0.0\n\t */\n\tmeterVersion?: string;\n\n\t/**\n\t * Named metric-reader configurations keyed by an arbitrary id.\n\t * Each entry's `type` field determines which exporter the connector instantiates\n\t * in start(). Omit or pass an empty object for a no-op provider (useful for tests).\n\t */\n\treaders?: { [id: string]: IOpenTelemetryReaderConfig };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IOpenTelemetryTelemetryConnectorConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IOpenTelemetryTelemetryConnectorConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IOpenTelemetryTelemetryConnectorConfig } from \"./IOpenTelemetryTelemetryConnectorConfig.js\";\n\n/**\n * The options for the OpenTelemetry telemetry connector constructor.\n */\nexport interface IOpenTelemetryTelemetryConnectorConstructorOptions {\n\t/**\n\t * The
|
|
1
|
+
{"version":3,"file":"IOpenTelemetryTelemetryConnectorConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IOpenTelemetryTelemetryConnectorConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IOpenTelemetryTelemetryConnectorConfig } from \"./IOpenTelemetryTelemetryConnectorConfig.js\";\n\n/**\n * The options for the OpenTelemetry telemetry connector constructor.\n */\nexport interface IOpenTelemetryTelemetryConnectorConstructorOptions {\n\t/**\n\t * The config for the telemetry connector.\n\t */\n\tconfig?: IOpenTelemetryTelemetryConnectorConfig;\n}\n"]}
|
|
@@ -3,14 +3,13 @@ import { PrometheusExporter } from "@opentelemetry/exporter-prometheus";
|
|
|
3
3
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
4
4
|
import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
|
5
5
|
import { ContextIdKeys, ContextIdStore } from "@twin.org/context";
|
|
6
|
-
import {
|
|
7
|
-
import { EntityStorageTelemetryConnector } from "@twin.org/telemetry-connector-entity-storage";
|
|
6
|
+
import { ComponentFactory, Converter, GeneralError, Guards, Is, NotFoundError, RandomHelper } from "@twin.org/core";
|
|
8
7
|
import { MetricCounterOperation, MetricType } from "@twin.org/telemetry-models";
|
|
9
8
|
import { OpenTelemetryReaderTypes } from "./models/openTelemetryReaderTypes.js";
|
|
10
9
|
/**
|
|
11
10
|
* Class for performing telemetry operations using OpenTelemetry instruments.
|
|
12
|
-
* Metric definitions
|
|
13
|
-
*
|
|
11
|
+
* Metric definitions are held in memory. Persistence and querying are not supported;
|
|
12
|
+
* use a multi-connector with an EntityStorageTelemetryConnector for those capabilities.
|
|
14
13
|
*/
|
|
15
14
|
export class OpenTelemetryTelemetryConnector {
|
|
16
15
|
/**
|
|
@@ -26,11 +25,6 @@ export class OpenTelemetryTelemetryConnector {
|
|
|
26
25
|
* @internal
|
|
27
26
|
*/
|
|
28
27
|
_config;
|
|
29
|
-
/**
|
|
30
|
-
* Internal entity-storage connector that owns all metric metadata and value history.
|
|
31
|
-
* @internal
|
|
32
|
-
*/
|
|
33
|
-
_inner;
|
|
34
28
|
/**
|
|
35
29
|
* Whether start() has been called. Drives OTEL forwarding on/off.
|
|
36
30
|
* @internal
|
|
@@ -44,9 +38,7 @@ export class OpenTelemetryTelemetryConnector {
|
|
|
44
38
|
*/
|
|
45
39
|
_providers;
|
|
46
40
|
/**
|
|
47
|
-
*
|
|
48
|
-
* addMetricValue after a process restart. Used to register instruments on new providers
|
|
49
|
-
* without re-querying entity storage on every call.
|
|
41
|
+
* In-memory metric definition registry keyed by metric id.
|
|
50
42
|
* @internal
|
|
51
43
|
*/
|
|
52
44
|
_metricDefs;
|
|
@@ -60,17 +52,6 @@ export class OpenTelemetryTelemetryConnector {
|
|
|
60
52
|
for (const [, readerConfig] of Object.entries(this._config.readers ?? {})) {
|
|
61
53
|
Guards.arrayOneOf(OpenTelemetryTelemetryConnector.CLASS_NAME, "readerConfig.type", readerConfig.type, Object.values(OpenTelemetryReaderTypes));
|
|
62
54
|
}
|
|
63
|
-
this._inner = new EntityStorageTelemetryConnector({
|
|
64
|
-
loggingComponentType: options?.loggingComponentType,
|
|
65
|
-
telemetryMetricStorageConnectorType: options?.telemetryMetricStorageConnectorType,
|
|
66
|
-
telemetryMetricValueStorageConnectorType: options?.telemetryMetricValueStorageConnectorType,
|
|
67
|
-
config: {
|
|
68
|
-
mutexTimeoutMs: this._config.mutexTimeoutMs,
|
|
69
|
-
batchSize: this._config.batchSize,
|
|
70
|
-
batchIntervalMs: this._config.batchIntervalMs,
|
|
71
|
-
maxCacheSize: this._config.maxCacheSize
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
55
|
this._started = false;
|
|
75
56
|
this._providers = {};
|
|
76
57
|
this._metricDefs = {};
|
|
@@ -83,8 +64,8 @@ export class OpenTelemetryTelemetryConnector {
|
|
|
83
64
|
return OpenTelemetryTelemetryConnector.CLASS_NAME;
|
|
84
65
|
}
|
|
85
66
|
/**
|
|
86
|
-
* Enable OTEL forwarding. Subsequent calls to
|
|
87
|
-
*
|
|
67
|
+
* Enable OTEL forwarding. Subsequent calls to addMetricValue will create
|
|
68
|
+
* per-tenant/node MeterProviders on demand.
|
|
88
69
|
* @param nodeLoggingComponentType The node logging component type.
|
|
89
70
|
* @returns A promise that resolves when OTEL forwarding is enabled.
|
|
90
71
|
*/
|
|
@@ -125,24 +106,18 @@ export class OpenTelemetryTelemetryConnector {
|
|
|
125
106
|
}
|
|
126
107
|
}
|
|
127
108
|
/**
|
|
128
|
-
*
|
|
109
|
+
* Register a new metric definition. If the connector is already started,
|
|
110
|
+
* the OTEL instrument is registered immediately for the current context.
|
|
129
111
|
* @param metric The metric details.
|
|
130
|
-
* @returns A promise that resolves when the metric has been
|
|
112
|
+
* @returns A promise that resolves when the metric has been registered.
|
|
131
113
|
*/
|
|
132
114
|
async createMetric(metric) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
catch (err) {
|
|
137
|
-
// Entity storage performs all validation and throws AlreadyExistsError on duplicates.
|
|
138
|
-
// OTP doesn't care about duplicates and will create a new instrument instance on each call
|
|
139
|
-
// so we catch this error and ignore it to allow the OTEL instruments to be registered.
|
|
140
|
-
if (!BaseError.isErrorName(err, AlreadyExistsError.CLASS_NAME)) {
|
|
141
|
-
throw err;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
115
|
+
Guards.object(OpenTelemetryTelemetryConnector.CLASS_NAME, "metric", metric);
|
|
116
|
+
Guards.stringValue(OpenTelemetryTelemetryConnector.CLASS_NAME, "metric.id", metric.id);
|
|
117
|
+
Guards.stringValue(OpenTelemetryTelemetryConnector.CLASS_NAME, "metric.label", metric.label);
|
|
144
118
|
this._metricDefs[metric.id] = {
|
|
145
119
|
type: metric.type,
|
|
120
|
+
label: metric.label,
|
|
146
121
|
description: metric.description,
|
|
147
122
|
unit: metric.unit
|
|
148
123
|
};
|
|
@@ -153,56 +128,57 @@ export class OpenTelemetryTelemetryConnector {
|
|
|
153
128
|
}
|
|
154
129
|
}
|
|
155
130
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*/
|
|
160
|
-
async getMetric(id) {
|
|
161
|
-
const result = await this._inner.getMetric(id);
|
|
162
|
-
const value = result.value ?? { id: "", ts: 0, value: 0 };
|
|
163
|
-
return { metric: result.metric, value };
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Get a specific metric value by its id.
|
|
167
|
-
* @param id The id of the metric.
|
|
168
|
-
* @param valueId The id of the metric value.
|
|
169
|
-
* @returns The metric value.
|
|
170
|
-
*/
|
|
171
|
-
async getMetricValue(id, valueId) {
|
|
172
|
-
return this._inner.getMetricValue(id, valueId);
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Update the metric metadata.
|
|
176
|
-
* Note: OpenTelemetry instrument descriptors are immutable once created.
|
|
177
|
-
* This method updates the persisted metadata mirror only.
|
|
131
|
+
* Update the in-memory metadata for a registered metric.
|
|
132
|
+
* OpenTelemetry instrument descriptors are immutable once created; only the
|
|
133
|
+
* cached label, description and unit are updated.
|
|
178
134
|
* @param metric The metric details (type cannot be changed).
|
|
179
|
-
* @returns A promise that resolves when the
|
|
135
|
+
* @returns A promise that resolves when the metadata has been updated.
|
|
180
136
|
*/
|
|
181
137
|
async updateMetric(metric) {
|
|
182
|
-
|
|
138
|
+
Guards.object(OpenTelemetryTelemetryConnector.CLASS_NAME, "metric", metric);
|
|
139
|
+
Guards.stringValue(OpenTelemetryTelemetryConnector.CLASS_NAME, "metric.id", metric.id);
|
|
140
|
+
const def = this._metricDefs[metric.id];
|
|
141
|
+
if (Is.undefined(def)) {
|
|
142
|
+
throw new NotFoundError(OpenTelemetryTelemetryConnector.CLASS_NAME, "metricNotFound", metric.id);
|
|
143
|
+
}
|
|
144
|
+
this._metricDefs[metric.id] = {
|
|
145
|
+
type: def.type,
|
|
146
|
+
label: metric.label,
|
|
147
|
+
description: metric.description,
|
|
148
|
+
unit: metric.unit
|
|
149
|
+
};
|
|
183
150
|
}
|
|
184
151
|
/**
|
|
185
|
-
* Record a metric value.
|
|
152
|
+
* Record a metric value and forward it to the appropriate OTEL instrument.
|
|
186
153
|
* The current tenant and node IDs are read from `ContextIdStore` and used to
|
|
187
154
|
* select (or create) the matching per-tenant/node `MeterProvider`.
|
|
188
155
|
* @param id The id of the metric.
|
|
189
156
|
* @param value The value for the operation.
|
|
190
157
|
* @param customData Optional custom data forwarded as OTEL attributes.
|
|
191
|
-
* @returns
|
|
158
|
+
* @returns A generated 32-character hex id for the recorded value.
|
|
192
159
|
*/
|
|
193
160
|
async addMetricValue(id, value, customData) {
|
|
194
|
-
|
|
161
|
+
Guards.stringValue(OpenTelemetryTelemetryConnector.CLASS_NAME, "id", id);
|
|
162
|
+
Guards.defined(OpenTelemetryTelemetryConnector.CLASS_NAME, "value", value);
|
|
163
|
+
const def = this._metricDefs[id];
|
|
164
|
+
if (Is.undefined(def)) {
|
|
165
|
+
throw new NotFoundError(OpenTelemetryTelemetryConnector.CLASS_NAME, "metricNotFound", id);
|
|
166
|
+
}
|
|
167
|
+
if (def.type === MetricType.Counter) {
|
|
168
|
+
if (value !== MetricCounterOperation.Increment && (!Is.integer(value) || value <= 0)) {
|
|
169
|
+
throw new GeneralError(OpenTelemetryTelemetryConnector.CLASS_NAME, "counterIncOnly");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else if (def.type === MetricType.Gauge) {
|
|
173
|
+
if (!Is.number(value)) {
|
|
174
|
+
throw new GeneralError(OpenTelemetryTelemetryConnector.CLASS_NAME, "gaugeNoIncDec");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const valueId = Converter.bytesToHex(RandomHelper.generate(16));
|
|
195
178
|
if (this._started) {
|
|
196
179
|
const contextIds = (await ContextIdStore.getContextIds()) ?? {};
|
|
197
180
|
const { meter, instruments } = this.getOrCreateProvider(contextIds);
|
|
198
181
|
if (!(id in instruments)) {
|
|
199
|
-
let def = this._metricDefs[id];
|
|
200
|
-
if (def === undefined) {
|
|
201
|
-
// Process restart path: definition not yet cached, fetch from storage.
|
|
202
|
-
const { metric } = await this._inner.getMetric(id);
|
|
203
|
-
def = { type: metric.type, description: metric.description, unit: metric.unit };
|
|
204
|
-
this._metricDefs[id] = def;
|
|
205
|
-
}
|
|
206
182
|
this.registerInstrument(id, def.type, def.description, def.unit, meter, instruments);
|
|
207
183
|
}
|
|
208
184
|
const entry = instruments[id];
|
|
@@ -233,48 +209,22 @@ export class OpenTelemetryTelemetryConnector {
|
|
|
233
209
|
return valueId;
|
|
234
210
|
}
|
|
235
211
|
/**
|
|
236
|
-
* Remove a metric and
|
|
237
|
-
* Note: OpenTelemetry exposes no API to deregister an instrument from a Meter,
|
|
238
|
-
* so the underlying Counter/UpDownCounter/Gauge remains resident for the lifetime
|
|
239
|
-
* of the MeterProvider. Re-creating a metric with the same id but a different
|
|
240
|
-
* MetricType is therefore not safe.
|
|
212
|
+
* Remove a metric from the in-memory registry and from all cached provider instrument maps.
|
|
241
213
|
* @param id The id of the metric.
|
|
242
|
-
* @returns A promise that resolves when the metric
|
|
214
|
+
* @returns A promise that resolves when the metric has been removed.
|
|
243
215
|
*/
|
|
244
216
|
async removeMetric(id) {
|
|
217
|
+
Guards.stringValue(OpenTelemetryTelemetryConnector.CLASS_NAME, "id", id);
|
|
218
|
+
if (Is.undefined(this._metricDefs[id])) {
|
|
219
|
+
throw new NotFoundError(OpenTelemetryTelemetryConnector.CLASS_NAME, "metricNotFound", id);
|
|
220
|
+
}
|
|
245
221
|
for (const { instruments } of Object.values(this._providers)) {
|
|
246
222
|
delete instruments[id];
|
|
247
223
|
}
|
|
248
224
|
delete this._metricDefs[id];
|
|
249
|
-
return this._inner.removeMetric(id);
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Query the registered metrics, optionally filtered by type.
|
|
253
|
-
* @param type The type of the metric.
|
|
254
|
-
* @param cursor The cursor to request the next page.
|
|
255
|
-
* @param limit Limit the number of entities to return.
|
|
256
|
-
* @returns The matching metrics and an optional cursor for the next page.
|
|
257
|
-
*/
|
|
258
|
-
async query(type, cursor, limit) {
|
|
259
|
-
return this._inner.query(type, cursor, limit);
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Query the recorded values for a metric, ordered by most recent first.
|
|
263
|
-
* @param id The id of the metric.
|
|
264
|
-
* @param timeStart The inclusive start time (epoch ms).
|
|
265
|
-
* @param timeEnd The inclusive end time (epoch ms).
|
|
266
|
-
* @param cursor The cursor returned by the previous call.
|
|
267
|
-
* @param limit Limit the number of values to return.
|
|
268
|
-
* @returns The metric details, matching values, and an optional cursor for the next page.
|
|
269
|
-
*/
|
|
270
|
-
async queryValues(id, timeStart, timeEnd, cursor, limit) {
|
|
271
|
-
return this._inner.queryValues(id, timeStart, timeEnd, cursor, limit);
|
|
272
225
|
}
|
|
273
226
|
/**
|
|
274
227
|
* Return or create the MeterProvider for the given tenant/node pair.
|
|
275
|
-
* Providers are keyed by "nodeId/tenantId" and carry OTEL resource attributes
|
|
276
|
-
* service.namespace=tenantId and service.instance.id=nodeId when those values
|
|
277
|
-
* are present.
|
|
278
228
|
* @param contextIds The current execution context IDs.
|
|
279
229
|
* @returns The cached or newly created provider entry.
|
|
280
230
|
* @internal
|
|
@@ -305,7 +255,6 @@ export class OpenTelemetryTelemetryConnector {
|
|
|
305
255
|
}
|
|
306
256
|
/**
|
|
307
257
|
* Instantiate fresh MetricReader instances from the connector config.
|
|
308
|
-
* Called each time a new MeterProvider is created for a tenant/node pair.
|
|
309
258
|
* @returns The list of reader instances.
|
|
310
259
|
* @internal
|
|
311
260
|
*/
|
|
@@ -369,19 +318,30 @@ export class OpenTelemetryTelemetryConnector {
|
|
|
369
318
|
if (Is.empty(customData)) {
|
|
370
319
|
return {};
|
|
371
320
|
}
|
|
372
|
-
const
|
|
321
|
+
const attributes = {};
|
|
373
322
|
for (const [key, val] of Object.entries(customData)) {
|
|
374
323
|
if (Is.string(val) || Is.number(val) || Is.boolean(val)) {
|
|
375
|
-
|
|
324
|
+
attributes[key] = val;
|
|
376
325
|
}
|
|
377
326
|
else if (Is.arrayValue(val)) {
|
|
378
|
-
if (
|
|
379
|
-
|
|
380
|
-
attrs[key] = val;
|
|
327
|
+
if (Is.string(val[0]) && val.every(el => Is.string(el))) {
|
|
328
|
+
attributes[key] = val;
|
|
381
329
|
}
|
|
330
|
+
else if (Is.number(val[0]) && val.every(el => Is.number(el))) {
|
|
331
|
+
attributes[key] = val;
|
|
332
|
+
}
|
|
333
|
+
else if (Is.boolean(val[0]) && val.every(el => Is.boolean(el))) {
|
|
334
|
+
attributes[key] = val;
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
attributes[key] = JSON.stringify(val);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
else if (!Is.undefined(val)) {
|
|
341
|
+
attributes[key] = JSON.stringify(val);
|
|
382
342
|
}
|
|
383
343
|
}
|
|
384
|
-
return
|
|
344
|
+
return attributes;
|
|
385
345
|
}
|
|
386
346
|
}
|
|
387
347
|
//# sourceMappingURL=openTelemetryTelemetryConnector.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openTelemetryTelemetryConnector.js","sourceRoot":"","sources":["../../src/openTelemetryTelemetryConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { Attributes, Counter, Gauge, Meter, UpDownCounter } from \"@opentelemetry/api\";\nimport { OTLPMetricExporter } from \"@opentelemetry/exporter-metrics-otlp-http\";\nimport { PrometheusExporter } from \"@opentelemetry/exporter-prometheus\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport {\n\tMeterProvider,\n\tPeriodicExportingMetricReader,\n\ttype MetricReader\n} from \"@opentelemetry/sdk-metrics\";\nimport { ContextIdKeys, ContextIdStore, type IContextIds } from \"@twin.org/context\";\nimport { AlreadyExistsError, BaseError, ComponentFactory, Guards, Is } from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { EntityStorageTelemetryConnector } from \"@twin.org/telemetry-connector-entity-storage\";\nimport {\n\ttype ITelemetryConnector,\n\ttype ITelemetryMetric,\n\ttype ITelemetryMetricValue,\n\tMetricCounterOperation,\n\tMetricType\n} from \"@twin.org/telemetry-models\";\nimport type { IOpenTelemetryTelemetryConnectorConfig } from \"./models/IOpenTelemetryTelemetryConnectorConfig.js\";\nimport type { IOpenTelemetryTelemetryConnectorConstructorOptions } from \"./models/IOpenTelemetryTelemetryConnectorConstructorOptions.js\";\nimport { OpenTelemetryReaderTypes } from \"./models/openTelemetryReaderTypes.js\";\n\n/**\n * Class for performing telemetry operations using OpenTelemetry instruments.\n * Metric definitions and value history are persisted via an internal\n * EntityStorageTelemetryConnector instance created at construction time.\n */\nexport class OpenTelemetryTelemetryConnector implements ITelemetryConnector {\n\t/**\n\t * The namespace supported by the telemetry connector.\n\t */\n\tpublic static readonly NAMESPACE: string = \"open-telemetry\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<OpenTelemetryTelemetryConnector>();\n\n\t/**\n\t * Config options stored so provider creation can initialise readers and meter identity.\n\t * @internal\n\t */\n\tprivate readonly _config: IOpenTelemetryTelemetryConnectorConfig;\n\n\t/**\n\t * Internal entity-storage connector that owns all metric metadata and value history.\n\t * @internal\n\t */\n\tprivate readonly _inner: EntityStorageTelemetryConnector;\n\n\t/**\n\t * Whether start() has been called. Drives OTEL forwarding on/off.\n\t * @internal\n\t */\n\tprivate _started: boolean;\n\n\t/**\n\t * Cache of MeterProvider+Meter+instruments keyed by \"nodeId/tenantId\".\n\t * Providers are created on demand when the first metric measurement arrives\n\t * for a given tenant/node pair.\n\t * @internal\n\t */\n\tprivate readonly _providers: {\n\t\t[key: string]: {\n\t\t\tmeterProvider: MeterProvider;\n\t\t\tmeter: Meter;\n\t\t\tinstruments: {\n\t\t\t\t[id: string]: { metricType: MetricType; instrument: Counter | UpDownCounter | Gauge };\n\t\t\t};\n\t\t};\n\t};\n\n\t/**\n\t * Metric definition cache keyed by metric id, populated in createMetric and on first\n\t * addMetricValue after a process restart. Used to register instruments on new providers\n\t * without re-querying entity storage on every call.\n\t * @internal\n\t */\n\tprivate readonly _metricDefs: {\n\t\t[id: string]: { type: MetricType; description?: string; unit?: string };\n\t};\n\n\t/**\n\t * Create a new instance of OpenTelemetryTelemetryConnector.\n\t * @param options The options for the connector.\n\t * @throws GuardError When a reader config specifies an unsupported type.\n\t */\n\tconstructor(options?: IOpenTelemetryTelemetryConnectorConstructorOptions) {\n\t\tthis._config = options?.config ?? {};\n\n\t\tfor (const [, readerConfig] of Object.entries(this._config.readers ?? {})) {\n\t\t\tGuards.arrayOneOf(\n\t\t\t\tOpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\t\tnameof(readerConfig.type),\n\t\t\t\treaderConfig.type,\n\t\t\t\tObject.values(OpenTelemetryReaderTypes)\n\t\t\t);\n\t\t}\n\n\t\tthis._inner = new EntityStorageTelemetryConnector({\n\t\t\tloggingComponentType: options?.loggingComponentType,\n\t\t\ttelemetryMetricStorageConnectorType: options?.telemetryMetricStorageConnectorType,\n\t\t\ttelemetryMetricValueStorageConnectorType: options?.telemetryMetricValueStorageConnectorType,\n\t\t\tconfig: {\n\t\t\t\tmutexTimeoutMs: this._config.mutexTimeoutMs,\n\t\t\t\tbatchSize: this._config.batchSize,\n\t\t\t\tbatchIntervalMs: this._config.batchIntervalMs,\n\t\t\t\tmaxCacheSize: this._config.maxCacheSize\n\t\t\t}\n\t\t});\n\t\tthis._started = false;\n\t\tthis._providers = {};\n\t\tthis._metricDefs = {};\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn OpenTelemetryTelemetryConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Enable OTEL forwarding. Subsequent calls to createMetric and addMetricValue will\n\t * create per-tenant/node MeterProviders on demand.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns A promise that resolves when OTEL forwarding is enabled.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tif (this._started) {\n\t\t\treturn;\n\t\t}\n\t\tthis._started = true;\n\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\t\tawait nodeLogging?.log({\n\t\t\tsource: OpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"connectorStarted\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { readerCount: Object.keys(this._config.readers ?? {}).length }\n\t\t});\n\t}\n\n\t/**\n\t * Shut down all cached MeterProviders and disable OTEL forwarding.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns A promise that resolves when all MeterProviders have shut down.\n\t */\n\tpublic async stop(nodeLoggingComponentType?: string): Promise<void> {\n\t\tif (this._started) {\n\t\t\tfor (const { meterProvider } of Object.values(this._providers)) {\n\t\t\t\tawait meterProvider.shutdown();\n\t\t\t}\n\t\t\tfor (const key of Object.keys(this._providers)) {\n\t\t\t\tdelete this._providers[key];\n\t\t\t}\n\t\t\tthis._started = false;\n\n\t\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tsource: OpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"connectorStopped\",\n\t\t\t\tlevel: \"info\",\n\t\t\t\tdata: {}\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Create a new metric.\n\t * @param metric The metric details.\n\t * @returns A promise that resolves when the metric has been persisted.\n\t */\n\tpublic async createMetric(metric: ITelemetryMetric): Promise<void> {\n\t\ttry {\n\t\t\tawait this._inner.createMetric(metric);\n\t\t} catch (err) {\n\t\t\t// Entity storage performs all validation and throws AlreadyExistsError on duplicates.\n\t\t\t// OTP doesn't care about duplicates and will create a new instrument instance on each call\n\t\t\t// so we catch this error and ignore it to allow the OTEL instruments to be registered.\n\t\t\tif (!BaseError.isErrorName(err, AlreadyExistsError.CLASS_NAME)) {\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t}\n\n\t\tthis._metricDefs[metric.id] = {\n\t\t\ttype: metric.type,\n\t\t\tdescription: metric.description,\n\t\t\tunit: metric.unit\n\t\t};\n\n\t\tif (this._started) {\n\t\t\tconst contextIds = (await ContextIdStore.getContextIds()) ?? {};\n\t\t\tconst { meter, instruments } = this.getOrCreateProvider(contextIds);\n\t\t\tthis.registerInstrument(\n\t\t\t\tmetric.id,\n\t\t\t\tmetric.type,\n\t\t\t\tmetric.description,\n\t\t\t\tmetric.unit,\n\t\t\t\tmeter,\n\t\t\t\tinstruments\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Get the metric details and its most recent value.\n\t * @param id The metric id.\n\t * @returns The metric details and its most recent value.\n\t */\n\tpublic async getMetric(id: string): Promise<{\n\t\tmetric: ITelemetryMetric;\n\t\tvalue: ITelemetryMetricValue;\n\t}> {\n\t\tconst result = await this._inner.getMetric(id);\n\t\tconst value = result.value ?? { id: \"\", ts: 0, value: 0 };\n\t\treturn { metric: result.metric, value };\n\t}\n\n\t/**\n\t * Get a specific metric value by its id.\n\t * @param id The id of the metric.\n\t * @param valueId The id of the metric value.\n\t * @returns The metric value.\n\t */\n\tpublic async getMetricValue(id: string, valueId: string): Promise<ITelemetryMetricValue> {\n\t\treturn this._inner.getMetricValue(id, valueId);\n\t}\n\n\t/**\n\t * Update the metric metadata.\n\t * Note: OpenTelemetry instrument descriptors are immutable once created.\n\t * This method updates the persisted metadata mirror only.\n\t * @param metric The metric details (type cannot be changed).\n\t * @returns A promise that resolves when the persisted metadata has been updated.\n\t */\n\tpublic async updateMetric(metric: Omit<ITelemetryMetric, \"type\">): Promise<void> {\n\t\treturn this._inner.updateMetric(metric);\n\t}\n\n\t/**\n\t * Record a metric value.\n\t * The current tenant and node IDs are read from `ContextIdStore` and used to\n\t * select (or create) the matching per-tenant/node `MeterProvider`.\n\t * @param id The id of the metric.\n\t * @param value The value for the operation.\n\t * @param customData Optional custom data forwarded as OTEL attributes.\n\t * @returns The id of the new metric value entry.\n\t */\n\tpublic async addMetricValue(\n\t\tid: string,\n\t\tvalue: MetricCounterOperation | number,\n\t\tcustomData?: { [key: string]: unknown }\n\t): Promise<string> {\n\t\tconst valueId = await this._inner.addMetricValue(id, value, customData);\n\n\t\tif (this._started) {\n\t\t\tconst contextIds = (await ContextIdStore.getContextIds()) ?? {};\n\t\t\tconst { meter, instruments } = this.getOrCreateProvider(contextIds);\n\n\t\t\tif (!(id in instruments)) {\n\t\t\t\tlet def = this._metricDefs[id];\n\t\t\t\tif (def === undefined) {\n\t\t\t\t\t// Process restart path: definition not yet cached, fetch from storage.\n\t\t\t\t\tconst { metric } = await this._inner.getMetric(id);\n\t\t\t\t\tdef = { type: metric.type, description: metric.description, unit: metric.unit };\n\t\t\t\t\tthis._metricDefs[id] = def;\n\t\t\t\t}\n\t\t\t\tthis.registerInstrument(id, def.type, def.description, def.unit, meter, instruments);\n\t\t\t}\n\n\t\t\tconst entry = instruments[id];\n\t\t\tif (entry !== undefined) {\n\t\t\t\tconst attributes = this.toAttributes(customData);\n\t\t\t\tconst { metricType, instrument } = entry;\n\n\t\t\t\tif (metricType === MetricType.Counter) {\n\t\t\t\t\t(instrument as Counter).add(\n\t\t\t\t\t\tvalue === MetricCounterOperation.Increment ? 1 : (value as number),\n\t\t\t\t\t\tattributes\n\t\t\t\t\t);\n\t\t\t\t} else if (metricType === MetricType.IncDecCounter) {\n\t\t\t\t\tlet delta: number;\n\t\t\t\t\tif (value === MetricCounterOperation.Increment) {\n\t\t\t\t\t\tdelta = 1;\n\t\t\t\t\t} else if (value === MetricCounterOperation.Decrement) {\n\t\t\t\t\t\tdelta = -1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdelta = value;\n\t\t\t\t\t}\n\t\t\t\t\t(instrument as UpDownCounter).add(delta, attributes);\n\t\t\t\t} else {\n\t\t\t\t\t(instrument as Gauge).record(value as number, attributes);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn valueId;\n\t}\n\n\t/**\n\t * Remove a metric and its persisted value history.\n\t * Note: OpenTelemetry exposes no API to deregister an instrument from a Meter,\n\t * so the underlying Counter/UpDownCounter/Gauge remains resident for the lifetime\n\t * of the MeterProvider. Re-creating a metric with the same id but a different\n\t * MetricType is therefore not safe.\n\t * @param id The id of the metric.\n\t * @returns A promise that resolves when the metric and its value history have been removed.\n\t */\n\tpublic async removeMetric(id: string): Promise<void> {\n\t\tfor (const { instruments } of Object.values(this._providers)) {\n\t\t\tdelete instruments[id];\n\t\t}\n\t\tdelete this._metricDefs[id];\n\t\treturn this._inner.removeMetric(id);\n\t}\n\n\t/**\n\t * Query the registered metrics, optionally filtered by type.\n\t * @param type The type of the metric.\n\t * @param cursor The cursor to request the next page.\n\t * @param limit Limit the number of entities to return.\n\t * @returns The matching metrics and an optional cursor for the next page.\n\t */\n\tpublic async query(\n\t\ttype?: MetricType,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\tentities: ITelemetryMetric[];\n\t\tcursor?: string;\n\t}> {\n\t\treturn this._inner.query(type, cursor, limit);\n\t}\n\n\t/**\n\t * Query the recorded values for a metric, ordered by most recent first.\n\t * @param id The id of the metric.\n\t * @param timeStart The inclusive start time (epoch ms).\n\t * @param timeEnd The inclusive end time (epoch ms).\n\t * @param cursor The cursor returned by the previous call.\n\t * @param limit Limit the number of values to return.\n\t * @returns The metric details, matching values, and an optional cursor for the next page.\n\t */\n\tpublic async queryValues(\n\t\tid: string,\n\t\ttimeStart?: number,\n\t\ttimeEnd?: number,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\tmetric: ITelemetryMetric;\n\t\tentities: ITelemetryMetricValue[];\n\t\tcursor?: string;\n\t}> {\n\t\treturn this._inner.queryValues(id, timeStart, timeEnd, cursor, limit);\n\t}\n\n\t/**\n\t * Return or create the MeterProvider for the given tenant/node pair.\n\t * Providers are keyed by \"nodeId/tenantId\" and carry OTEL resource attributes\n\t * service.namespace=tenantId and service.instance.id=nodeId when those values\n\t * are present.\n\t * @param contextIds The current execution context IDs.\n\t * @returns The cached or newly created provider entry.\n\t * @internal\n\t */\n\tprivate getOrCreateProvider(contextIds: IContextIds): {\n\t\tmeterProvider: MeterProvider;\n\t\tmeter: Meter;\n\t\tinstruments: {\n\t\t\t[id: string]: { metricType: MetricType; instrument: Counter | UpDownCounter | Gauge };\n\t\t};\n\t} {\n\t\tconst node = contextIds[ContextIdKeys.Node];\n\t\tconst tenantId = contextIds[ContextIdKeys.Tenant];\n\n\t\tconst key = `${node ?? \"\"}/${tenantId ?? \"\"}`;\n\n\t\tlet cached = this._providers[key];\n\t\tif (!Is.undefined(cached)) {\n\t\t\treturn cached;\n\t\t}\n\n\t\tconst readers = this.createReaders();\n\t\tconst resourcePrefix = \"service\";\n\t\tconst resourceAttrs: { [id: string]: string } = {};\n\t\tif (Is.stringValue(node)) {\n\t\t\tresourceAttrs[`${resourcePrefix}.instance.id`] = node;\n\t\t}\n\t\tif (Is.stringValue(tenantId)) {\n\t\t\tresourceAttrs[`${resourcePrefix}.namespace`] = tenantId;\n\t\t}\n\t\tconst resource =\n\t\t\tObject.keys(resourceAttrs).length > 0 ? resourceFromAttributes(resourceAttrs) : undefined;\n\t\tconst meterProvider = new MeterProvider({ readers, resource });\n\t\tconst meter = meterProvider.getMeter(\n\t\t\tthis._config.meterName ?? \"twin-telemetry\",\n\t\t\tthis._config.meterVersion ?? \"1.0.0\"\n\t\t);\n\t\tcached = { meterProvider, meter, instruments: {} };\n\t\tthis._providers[key] = cached;\n\n\t\treturn cached;\n\t}\n\n\t/**\n\t * Instantiate fresh MetricReader instances from the connector config.\n\t * Called each time a new MeterProvider is created for a tenant/node pair.\n\t * @returns The list of reader instances.\n\t * @internal\n\t */\n\tprivate createReaders(): MetricReader[] {\n\t\tconst readers: MetricReader[] = [];\n\t\tfor (const [, config] of Object.entries(this._config.readers ?? {})) {\n\t\t\tif (config.type === OpenTelemetryReaderTypes.Prometheus) {\n\t\t\t\treaders.push(\n\t\t\t\t\tnew PrometheusExporter({\n\t\t\t\t\t\tport: config.port,\n\t\t\t\t\t\tendpoint: config.endpoint,\n\t\t\t\t\t\tpreventServerStart: !(config.startServer ?? true),\n\t\t\t\t\t\tprefix: config.prefix\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t} else if (config.type === OpenTelemetryReaderTypes.OtlpHttp) {\n\t\t\t\treaders.push(\n\t\t\t\t\tnew PeriodicExportingMetricReader({\n\t\t\t\t\t\texporter: new OTLPMetricExporter({\n\t\t\t\t\t\t\turl: config.url,\n\t\t\t\t\t\t\theaders: config.headers\n\t\t\t\t\t\t}),\n\t\t\t\t\t\texportIntervalMillis: config.exportIntervalMs\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn readers;\n\t}\n\n\t/**\n\t * Register an OTEL instrument on a provider's instruments map if not already present.\n\t * @param id The metric id.\n\t * @param type The metric type.\n\t * @param description The metric description.\n\t * @param unit The metric unit.\n\t * @param meter The meter to create instruments on.\n\t * @param instruments The per-provider instruments map to update.\n\t * @internal\n\t */\n\tprivate registerInstrument(\n\t\tid: string,\n\t\ttype: MetricType,\n\t\tdescription: string | undefined,\n\t\tunit: string | undefined,\n\t\tmeter: Meter,\n\t\tinstruments: {\n\t\t\t[id: string]: { metricType: MetricType; instrument: Counter | UpDownCounter | Gauge };\n\t\t}\n\t): void {\n\t\tif (id in instruments) {\n\t\t\treturn;\n\t\t}\n\t\tconst instrumentOptions = { description, unit };\n\t\tlet instrument: Counter | UpDownCounter | Gauge;\n\t\tif (type === MetricType.Counter) {\n\t\t\tinstrument = meter.createCounter(id, instrumentOptions);\n\t\t} else if (type === MetricType.IncDecCounter) {\n\t\t\tinstrument = meter.createUpDownCounter(id, instrumentOptions);\n\t\t} else {\n\t\t\tinstrument = meter.createGauge(id, instrumentOptions);\n\t\t}\n\t\tinstruments[id] = { metricType: type, instrument };\n\t}\n\n\t/**\n\t * Convert customData to OTEL-compatible Attributes.\n\t * @param customData The raw custom data map.\n\t * @returns An OTEL Attributes object.\n\t * @internal\n\t */\n\tprivate toAttributes(customData?: { [key: string]: unknown }): Attributes {\n\t\tif (Is.empty(customData)) {\n\t\t\treturn {};\n\t\t}\n\t\tconst attrs: Attributes = {};\n\t\tfor (const [key, val] of Object.entries(customData)) {\n\t\t\tif (Is.string(val) || Is.number(val) || Is.boolean(val)) {\n\t\t\t\tattrs[key] = val;\n\t\t\t} else if (Is.arrayValue(val)) {\n\t\t\t\tif (\n\t\t\t\t\t(Is.string(val[0]) || Is.number(val[0]) || Is.boolean(val[0])) &&\n\t\t\t\t\tval.every(el => typeof el === typeof val[0])\n\t\t\t\t) {\n\t\t\t\t\tattrs[key] = val as string[] | number[] | boolean[];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn attrs;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"openTelemetryTelemetryConnector.js","sourceRoot":"","sources":["../../src/openTelemetryTelemetryConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { Attributes, Counter, Gauge, Meter, UpDownCounter } from \"@opentelemetry/api\";\nimport { OTLPMetricExporter } from \"@opentelemetry/exporter-metrics-otlp-http\";\nimport { PrometheusExporter } from \"@opentelemetry/exporter-prometheus\";\nimport { resourceFromAttributes } from \"@opentelemetry/resources\";\nimport {\n\tMeterProvider,\n\tPeriodicExportingMetricReader,\n\ttype MetricReader\n} from \"@opentelemetry/sdk-metrics\";\nimport { ContextIdKeys, ContextIdStore, type IContextIds } from \"@twin.org/context\";\nimport {\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tNotFoundError,\n\tRandomHelper\n} from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\ttype ITelemetryConnector,\n\ttype ITelemetryMetric,\n\tMetricCounterOperation,\n\tMetricType\n} from \"@twin.org/telemetry-models\";\nimport type { IOpenTelemetryTelemetryConnectorConfig } from \"./models/IOpenTelemetryTelemetryConnectorConfig.js\";\nimport type { IOpenTelemetryTelemetryConnectorConstructorOptions } from \"./models/IOpenTelemetryTelemetryConnectorConstructorOptions.js\";\nimport { OpenTelemetryReaderTypes } from \"./models/openTelemetryReaderTypes.js\";\n\n/**\n * Class for performing telemetry operations using OpenTelemetry instruments.\n * Metric definitions are held in memory. Persistence and querying are not supported;\n * use a multi-connector with an EntityStorageTelemetryConnector for those capabilities.\n */\nexport class OpenTelemetryTelemetryConnector implements ITelemetryConnector {\n\t/**\n\t * The namespace supported by the telemetry connector.\n\t */\n\tpublic static readonly NAMESPACE: string = \"open-telemetry\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<OpenTelemetryTelemetryConnector>();\n\n\t/**\n\t * Config options stored so provider creation can initialise readers and meter identity.\n\t * @internal\n\t */\n\tprivate readonly _config: IOpenTelemetryTelemetryConnectorConfig;\n\n\t/**\n\t * Whether start() has been called. Drives OTEL forwarding on/off.\n\t * @internal\n\t */\n\tprivate _started: boolean;\n\n\t/**\n\t * Cache of MeterProvider+Meter+instruments keyed by \"nodeId/tenantId\".\n\t * Providers are created on demand when the first metric measurement arrives\n\t * for a given tenant/node pair.\n\t * @internal\n\t */\n\tprivate readonly _providers: {\n\t\t[key: string]: {\n\t\t\tmeterProvider: MeterProvider;\n\t\t\tmeter: Meter;\n\t\t\tinstruments: {\n\t\t\t\t[id: string]: { metricType: MetricType; instrument: Counter | UpDownCounter | Gauge };\n\t\t\t};\n\t\t};\n\t};\n\n\t/**\n\t * In-memory metric definition registry keyed by metric id.\n\t * @internal\n\t */\n\tprivate readonly _metricDefs: {\n\t\t[id: string]: { type: MetricType; label: string; description?: string; unit?: string };\n\t};\n\n\t/**\n\t * Create a new instance of OpenTelemetryTelemetryConnector.\n\t * @param options The options for the connector.\n\t * @throws GuardError When a reader config specifies an unsupported type.\n\t */\n\tconstructor(options?: IOpenTelemetryTelemetryConnectorConstructorOptions) {\n\t\tthis._config = options?.config ?? {};\n\n\t\tfor (const [, readerConfig] of Object.entries(this._config.readers ?? {})) {\n\t\t\tGuards.arrayOneOf(\n\t\t\t\tOpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\t\tnameof(readerConfig.type),\n\t\t\t\treaderConfig.type,\n\t\t\t\tObject.values(OpenTelemetryReaderTypes)\n\t\t\t);\n\t\t}\n\n\t\tthis._started = false;\n\t\tthis._providers = {};\n\t\tthis._metricDefs = {};\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn OpenTelemetryTelemetryConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Enable OTEL forwarding. Subsequent calls to addMetricValue will create\n\t * per-tenant/node MeterProviders on demand.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns A promise that resolves when OTEL forwarding is enabled.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tif (this._started) {\n\t\t\treturn;\n\t\t}\n\t\tthis._started = true;\n\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\t\tawait nodeLogging?.log({\n\t\t\tsource: OpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"connectorStarted\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { readerCount: Object.keys(this._config.readers ?? {}).length }\n\t\t});\n\t}\n\n\t/**\n\t * Shut down all cached MeterProviders and disable OTEL forwarding.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns A promise that resolves when all MeterProviders have shut down.\n\t */\n\tpublic async stop(nodeLoggingComponentType?: string): Promise<void> {\n\t\tif (this._started) {\n\t\t\tfor (const { meterProvider } of Object.values(this._providers)) {\n\t\t\t\tawait meterProvider.shutdown();\n\t\t\t}\n\t\t\tfor (const key of Object.keys(this._providers)) {\n\t\t\t\tdelete this._providers[key];\n\t\t\t}\n\t\t\tthis._started = false;\n\n\t\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tsource: OpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\t\tmessage: \"connectorStopped\",\n\t\t\t\tlevel: \"info\",\n\t\t\t\tdata: {}\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Register a new metric definition. If the connector is already started,\n\t * the OTEL instrument is registered immediately for the current context.\n\t * @param metric The metric details.\n\t * @returns A promise that resolves when the metric has been registered.\n\t */\n\tpublic async createMetric(metric: ITelemetryMetric): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(\n\t\t\tOpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\tnameof(metric),\n\t\t\tmetric\n\t\t);\n\t\tGuards.stringValue(OpenTelemetryTelemetryConnector.CLASS_NAME, nameof(metric.id), metric.id);\n\t\tGuards.stringValue(\n\t\t\tOpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\tnameof(metric.label),\n\t\t\tmetric.label\n\t\t);\n\n\t\tthis._metricDefs[metric.id] = {\n\t\t\ttype: metric.type,\n\t\t\tlabel: metric.label,\n\t\t\tdescription: metric.description,\n\t\t\tunit: metric.unit\n\t\t};\n\n\t\tif (this._started) {\n\t\t\tconst contextIds = (await ContextIdStore.getContextIds()) ?? {};\n\t\t\tconst { meter, instruments } = this.getOrCreateProvider(contextIds);\n\t\t\tthis.registerInstrument(\n\t\t\t\tmetric.id,\n\t\t\t\tmetric.type,\n\t\t\t\tmetric.description,\n\t\t\t\tmetric.unit,\n\t\t\t\tmeter,\n\t\t\t\tinstruments\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Update the in-memory metadata for a registered metric.\n\t * OpenTelemetry instrument descriptors are immutable once created; only the\n\t * cached label, description and unit are updated.\n\t * @param metric The metric details (type cannot be changed).\n\t * @returns A promise that resolves when the metadata has been updated.\n\t */\n\tpublic async updateMetric(metric: Omit<ITelemetryMetric, \"type\">): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(\n\t\t\tOpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\tnameof(metric),\n\t\t\tmetric\n\t\t);\n\t\tGuards.stringValue(OpenTelemetryTelemetryConnector.CLASS_NAME, nameof(metric.id), metric.id);\n\n\t\tconst def = this._metricDefs[metric.id];\n\t\tif (Is.undefined(def)) {\n\t\t\tthrow new NotFoundError(\n\t\t\t\tOpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\t\t\"metricNotFound\",\n\t\t\t\tmetric.id\n\t\t\t);\n\t\t}\n\n\t\tthis._metricDefs[metric.id] = {\n\t\t\ttype: def.type,\n\t\t\tlabel: metric.label,\n\t\t\tdescription: metric.description,\n\t\t\tunit: metric.unit\n\t\t};\n\t}\n\n\t/**\n\t * Record a metric value and forward it to the appropriate OTEL instrument.\n\t * The current tenant and node IDs are read from `ContextIdStore` and used to\n\t * select (or create) the matching per-tenant/node `MeterProvider`.\n\t * @param id The id of the metric.\n\t * @param value The value for the operation.\n\t * @param customData Optional custom data forwarded as OTEL attributes.\n\t * @returns A generated 32-character hex id for the recorded value.\n\t */\n\tpublic async addMetricValue(\n\t\tid: string,\n\t\tvalue: MetricCounterOperation | number,\n\t\tcustomData?: { [key: string]: unknown }\n\t): Promise<string> {\n\t\tGuards.stringValue(OpenTelemetryTelemetryConnector.CLASS_NAME, nameof(id), id);\n\t\tGuards.defined(OpenTelemetryTelemetryConnector.CLASS_NAME, nameof(value), value);\n\n\t\tconst def = this._metricDefs[id];\n\t\tif (Is.undefined(def)) {\n\t\t\tthrow new NotFoundError(OpenTelemetryTelemetryConnector.CLASS_NAME, \"metricNotFound\", id);\n\t\t}\n\n\t\tif (def.type === MetricType.Counter) {\n\t\t\tif (value !== MetricCounterOperation.Increment && (!Is.integer(value) || value <= 0)) {\n\t\t\t\tthrow new GeneralError(OpenTelemetryTelemetryConnector.CLASS_NAME, \"counterIncOnly\");\n\t\t\t}\n\t\t} else if (def.type === MetricType.Gauge) {\n\t\t\tif (!Is.number(value)) {\n\t\t\t\tthrow new GeneralError(OpenTelemetryTelemetryConnector.CLASS_NAME, \"gaugeNoIncDec\");\n\t\t\t}\n\t\t}\n\n\t\tconst valueId = Converter.bytesToHex(RandomHelper.generate(16));\n\n\t\tif (this._started) {\n\t\t\tconst contextIds = (await ContextIdStore.getContextIds()) ?? {};\n\t\t\tconst { meter, instruments } = this.getOrCreateProvider(contextIds);\n\n\t\t\tif (!(id in instruments)) {\n\t\t\t\tthis.registerInstrument(id, def.type, def.description, def.unit, meter, instruments);\n\t\t\t}\n\n\t\t\tconst entry = instruments[id];\n\t\t\tif (entry !== undefined) {\n\t\t\t\tconst attributes = this.toAttributes(customData);\n\t\t\t\tconst { metricType, instrument } = entry;\n\n\t\t\t\tif (metricType === MetricType.Counter) {\n\t\t\t\t\t(instrument as Counter).add(\n\t\t\t\t\t\tvalue === MetricCounterOperation.Increment ? 1 : (value as number),\n\t\t\t\t\t\tattributes\n\t\t\t\t\t);\n\t\t\t\t} else if (metricType === MetricType.IncDecCounter) {\n\t\t\t\t\tlet delta: number;\n\t\t\t\t\tif (value === MetricCounterOperation.Increment) {\n\t\t\t\t\t\tdelta = 1;\n\t\t\t\t\t} else if (value === MetricCounterOperation.Decrement) {\n\t\t\t\t\t\tdelta = -1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdelta = value;\n\t\t\t\t\t}\n\t\t\t\t\t(instrument as UpDownCounter).add(delta, attributes);\n\t\t\t\t} else {\n\t\t\t\t\t(instrument as Gauge).record(value as number, attributes);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn valueId;\n\t}\n\n\t/**\n\t * Remove a metric from the in-memory registry and from all cached provider instrument maps.\n\t * @param id The id of the metric.\n\t * @returns A promise that resolves when the metric has been removed.\n\t */\n\tpublic async removeMetric(id: string): Promise<void> {\n\t\tGuards.stringValue(OpenTelemetryTelemetryConnector.CLASS_NAME, nameof(id), id);\n\n\t\tif (Is.undefined(this._metricDefs[id])) {\n\t\t\tthrow new NotFoundError(OpenTelemetryTelemetryConnector.CLASS_NAME, \"metricNotFound\", id);\n\t\t}\n\n\t\tfor (const { instruments } of Object.values(this._providers)) {\n\t\t\tdelete instruments[id];\n\t\t}\n\t\tdelete this._metricDefs[id];\n\t}\n\n\t/**\n\t * Return or create the MeterProvider for the given tenant/node pair.\n\t * @param contextIds The current execution context IDs.\n\t * @returns The cached or newly created provider entry.\n\t * @internal\n\t */\n\tprivate getOrCreateProvider(contextIds: IContextIds): {\n\t\tmeterProvider: MeterProvider;\n\t\tmeter: Meter;\n\t\tinstruments: {\n\t\t\t[id: string]: { metricType: MetricType; instrument: Counter | UpDownCounter | Gauge };\n\t\t};\n\t} {\n\t\tconst node = contextIds[ContextIdKeys.Node];\n\t\tconst tenantId = contextIds[ContextIdKeys.Tenant];\n\n\t\tconst key = `${node ?? \"\"}/${tenantId ?? \"\"}`;\n\n\t\tlet cached = this._providers[key];\n\t\tif (!Is.undefined(cached)) {\n\t\t\treturn cached;\n\t\t}\n\n\t\tconst readers = this.createReaders();\n\t\tconst resourcePrefix = \"service\";\n\t\tconst resourceAttrs: { [id: string]: string } = {};\n\t\tif (Is.stringValue(node)) {\n\t\t\tresourceAttrs[`${resourcePrefix}.instance.id`] = node;\n\t\t}\n\t\tif (Is.stringValue(tenantId)) {\n\t\t\tresourceAttrs[`${resourcePrefix}.namespace`] = tenantId;\n\t\t}\n\t\tconst resource =\n\t\t\tObject.keys(resourceAttrs).length > 0 ? resourceFromAttributes(resourceAttrs) : undefined;\n\t\tconst meterProvider = new MeterProvider({ readers, resource });\n\t\tconst meter = meterProvider.getMeter(\n\t\t\tthis._config.meterName ?? \"twin-telemetry\",\n\t\t\tthis._config.meterVersion ?? \"1.0.0\"\n\t\t);\n\t\tcached = { meterProvider, meter, instruments: {} };\n\t\tthis._providers[key] = cached;\n\n\t\treturn cached;\n\t}\n\n\t/**\n\t * Instantiate fresh MetricReader instances from the connector config.\n\t * @returns The list of reader instances.\n\t * @internal\n\t */\n\tprivate createReaders(): MetricReader[] {\n\t\tconst readers: MetricReader[] = [];\n\t\tfor (const [, config] of Object.entries(this._config.readers ?? {})) {\n\t\t\tif (config.type === OpenTelemetryReaderTypes.Prometheus) {\n\t\t\t\treaders.push(\n\t\t\t\t\tnew PrometheusExporter({\n\t\t\t\t\t\tport: config.port,\n\t\t\t\t\t\tendpoint: config.endpoint,\n\t\t\t\t\t\tpreventServerStart: !(config.startServer ?? true),\n\t\t\t\t\t\tprefix: config.prefix\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t} else if (config.type === OpenTelemetryReaderTypes.OtlpHttp) {\n\t\t\t\treaders.push(\n\t\t\t\t\tnew PeriodicExportingMetricReader({\n\t\t\t\t\t\texporter: new OTLPMetricExporter({\n\t\t\t\t\t\t\turl: config.url,\n\t\t\t\t\t\t\theaders: config.headers\n\t\t\t\t\t\t}),\n\t\t\t\t\t\texportIntervalMillis: config.exportIntervalMs\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn readers;\n\t}\n\n\t/**\n\t * Register an OTEL instrument on a provider's instruments map if not already present.\n\t * @param id The metric id.\n\t * @param type The metric type.\n\t * @param description The metric description.\n\t * @param unit The metric unit.\n\t * @param meter The meter to create instruments on.\n\t * @param instruments The per-provider instruments map to update.\n\t * @internal\n\t */\n\tprivate registerInstrument(\n\t\tid: string,\n\t\ttype: MetricType,\n\t\tdescription: string | undefined,\n\t\tunit: string | undefined,\n\t\tmeter: Meter,\n\t\tinstruments: {\n\t\t\t[id: string]: { metricType: MetricType; instrument: Counter | UpDownCounter | Gauge };\n\t\t}\n\t): void {\n\t\tif (id in instruments) {\n\t\t\treturn;\n\t\t}\n\t\tconst instrumentOptions = { description, unit };\n\t\tlet instrument: Counter | UpDownCounter | Gauge;\n\t\tif (type === MetricType.Counter) {\n\t\t\tinstrument = meter.createCounter(id, instrumentOptions);\n\t\t} else if (type === MetricType.IncDecCounter) {\n\t\t\tinstrument = meter.createUpDownCounter(id, instrumentOptions);\n\t\t} else {\n\t\t\tinstrument = meter.createGauge(id, instrumentOptions);\n\t\t}\n\t\tinstruments[id] = { metricType: type, instrument };\n\t}\n\n\t/**\n\t * Convert customData to OTEL-compatible Attributes.\n\t * @param customData The raw custom data map.\n\t * @returns An OTEL Attributes object.\n\t * @internal\n\t */\n\tprivate toAttributes(customData?: { [key: string]: unknown }): Attributes {\n\t\tif (Is.empty(customData)) {\n\t\t\treturn {};\n\t\t}\n\t\tconst attributes: Attributes = {};\n\t\tfor (const [key, val] of Object.entries(customData)) {\n\t\t\tif (Is.string(val) || Is.number(val) || Is.boolean(val)) {\n\t\t\t\tattributes[key] = val;\n\t\t\t} else if (Is.arrayValue(val)) {\n\t\t\t\tif (Is.string(val[0]) && val.every(el => Is.string(el))) {\n\t\t\t\t\tattributes[key] = val;\n\t\t\t\t} else if (Is.number(val[0]) && val.every(el => Is.number(el))) {\n\t\t\t\t\tattributes[key] = val;\n\t\t\t\t} else if (Is.boolean(val[0]) && val.every(el => Is.boolean(el))) {\n\t\t\t\t\tattributes[key] = val;\n\t\t\t\t} else {\n\t\t\t\t\tattributes[key] = JSON.stringify(val);\n\t\t\t\t}\n\t\t\t} else if (!Is.undefined(val)) {\n\t\t\t\tattributes[key] = JSON.stringify(val);\n\t\t\t}\n\t\t}\n\t\treturn attributes;\n\t}\n}\n"]}
|
|
@@ -3,25 +3,6 @@ import type { IOpenTelemetryReaderConfig } from "./IOpenTelemetryReaderConfig.js
|
|
|
3
3
|
* The options for the OpenTelemetry telemetry connector constructor.
|
|
4
4
|
*/
|
|
5
5
|
export interface IOpenTelemetryTelemetryConnectorConfig {
|
|
6
|
-
/**
|
|
7
|
-
* Timeout in milliseconds for acquiring the inner entity-storage metric write mutex lock.
|
|
8
|
-
*/
|
|
9
|
-
mutexTimeoutMs?: number;
|
|
10
|
-
/**
|
|
11
|
-
* Number of metric value entries to accumulate before flushing to entity storage.
|
|
12
|
-
* @default 10
|
|
13
|
-
*/
|
|
14
|
-
batchSize?: number;
|
|
15
|
-
/**
|
|
16
|
-
* Interval in milliseconds between automatic batch flushes to entity storage.
|
|
17
|
-
* @default 5000
|
|
18
|
-
*/
|
|
19
|
-
batchIntervalMs?: number;
|
|
20
|
-
/**
|
|
21
|
-
* Maximum number of entries retained in the write-ahead cache if a flush fails.
|
|
22
|
-
* @default 1000
|
|
23
|
-
*/
|
|
24
|
-
maxCacheSize?: number;
|
|
25
6
|
/**
|
|
26
7
|
* The name of the OpenTelemetry meter used to create instruments.
|
|
27
8
|
* @default twin-telemetry
|
|
@@ -3,23 +3,6 @@ import type { IOpenTelemetryTelemetryConnectorConfig } from "./IOpenTelemetryTel
|
|
|
3
3
|
* The options for the OpenTelemetry telemetry connector constructor.
|
|
4
4
|
*/
|
|
5
5
|
export interface IOpenTelemetryTelemetryConnectorConstructorOptions {
|
|
6
|
-
/**
|
|
7
|
-
* The component type to use for logging inside the connector and the inner
|
|
8
|
-
* entity-storage connector. When omitted logging is disabled.
|
|
9
|
-
*/
|
|
10
|
-
loggingComponentType?: string;
|
|
11
|
-
/**
|
|
12
|
-
* The entity storage connector type to use for storing metric definitions.
|
|
13
|
-
* Must be registered in `EntityStorageConnectorFactory` before calling `start()`.
|
|
14
|
-
* @default telemetry-metric
|
|
15
|
-
*/
|
|
16
|
-
telemetryMetricStorageConnectorType?: string;
|
|
17
|
-
/**
|
|
18
|
-
* The entity storage connector type to use for storing metric values.
|
|
19
|
-
* Must be registered in `EntityStorageConnectorFactory` before calling `start()`.
|
|
20
|
-
* @default telemetry-metric-value
|
|
21
|
-
*/
|
|
22
|
-
telemetryMetricValueStorageConnectorType?: string;
|
|
23
6
|
/**
|
|
24
7
|
* The config for the telemetry connector.
|
|
25
8
|
*/
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type ITelemetryConnector, type ITelemetryMetric,
|
|
1
|
+
import { type ITelemetryConnector, type ITelemetryMetric, MetricCounterOperation } from "@twin.org/telemetry-models";
|
|
2
2
|
import type { IOpenTelemetryTelemetryConnectorConstructorOptions } from "./models/IOpenTelemetryTelemetryConnectorConstructorOptions.js";
|
|
3
3
|
/**
|
|
4
4
|
* Class for performing telemetry operations using OpenTelemetry instruments.
|
|
5
|
-
* Metric definitions
|
|
6
|
-
*
|
|
5
|
+
* Metric definitions are held in memory. Persistence and querying are not supported;
|
|
6
|
+
* use a multi-connector with an EntityStorageTelemetryConnector for those capabilities.
|
|
7
7
|
*/
|
|
8
8
|
export declare class OpenTelemetryTelemetryConnector implements ITelemetryConnector {
|
|
9
9
|
/**
|
|
@@ -26,8 +26,8 @@ export declare class OpenTelemetryTelemetryConnector implements ITelemetryConnec
|
|
|
26
26
|
*/
|
|
27
27
|
className(): string;
|
|
28
28
|
/**
|
|
29
|
-
* Enable OTEL forwarding. Subsequent calls to
|
|
30
|
-
*
|
|
29
|
+
* Enable OTEL forwarding. Subsequent calls to addMetricValue will create
|
|
30
|
+
* per-tenant/node MeterProviders on demand.
|
|
31
31
|
* @param nodeLoggingComponentType The node logging component type.
|
|
32
32
|
* @returns A promise that resolves when OTEL forwarding is enabled.
|
|
33
33
|
*/
|
|
@@ -39,80 +39,36 @@ export declare class OpenTelemetryTelemetryConnector implements ITelemetryConnec
|
|
|
39
39
|
*/
|
|
40
40
|
stop(nodeLoggingComponentType?: string): Promise<void>;
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
42
|
+
* Register a new metric definition. If the connector is already started,
|
|
43
|
+
* the OTEL instrument is registered immediately for the current context.
|
|
43
44
|
* @param metric The metric details.
|
|
44
|
-
* @returns A promise that resolves when the metric has been
|
|
45
|
+
* @returns A promise that resolves when the metric has been registered.
|
|
45
46
|
*/
|
|
46
47
|
createMetric(metric: ITelemetryMetric): Promise<void>;
|
|
47
48
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*/
|
|
52
|
-
getMetric(id: string): Promise<{
|
|
53
|
-
metric: ITelemetryMetric;
|
|
54
|
-
value: ITelemetryMetricValue;
|
|
55
|
-
}>;
|
|
56
|
-
/**
|
|
57
|
-
* Get a specific metric value by its id.
|
|
58
|
-
* @param id The id of the metric.
|
|
59
|
-
* @param valueId The id of the metric value.
|
|
60
|
-
* @returns The metric value.
|
|
61
|
-
*/
|
|
62
|
-
getMetricValue(id: string, valueId: string): Promise<ITelemetryMetricValue>;
|
|
63
|
-
/**
|
|
64
|
-
* Update the metric metadata.
|
|
65
|
-
* Note: OpenTelemetry instrument descriptors are immutable once created.
|
|
66
|
-
* This method updates the persisted metadata mirror only.
|
|
49
|
+
* Update the in-memory metadata for a registered metric.
|
|
50
|
+
* OpenTelemetry instrument descriptors are immutable once created; only the
|
|
51
|
+
* cached label, description and unit are updated.
|
|
67
52
|
* @param metric The metric details (type cannot be changed).
|
|
68
|
-
* @returns A promise that resolves when the
|
|
53
|
+
* @returns A promise that resolves when the metadata has been updated.
|
|
69
54
|
*/
|
|
70
55
|
updateMetric(metric: Omit<ITelemetryMetric, "type">): Promise<void>;
|
|
71
56
|
/**
|
|
72
|
-
* Record a metric value.
|
|
57
|
+
* Record a metric value and forward it to the appropriate OTEL instrument.
|
|
73
58
|
* The current tenant and node IDs are read from `ContextIdStore` and used to
|
|
74
59
|
* select (or create) the matching per-tenant/node `MeterProvider`.
|
|
75
60
|
* @param id The id of the metric.
|
|
76
61
|
* @param value The value for the operation.
|
|
77
62
|
* @param customData Optional custom data forwarded as OTEL attributes.
|
|
78
|
-
* @returns
|
|
63
|
+
* @returns A generated 32-character hex id for the recorded value.
|
|
79
64
|
*/
|
|
80
65
|
addMetricValue(id: string, value: MetricCounterOperation | number, customData?: {
|
|
81
66
|
[key: string]: unknown;
|
|
82
67
|
}): Promise<string>;
|
|
83
68
|
/**
|
|
84
|
-
* Remove a metric and
|
|
85
|
-
* Note: OpenTelemetry exposes no API to deregister an instrument from a Meter,
|
|
86
|
-
* so the underlying Counter/UpDownCounter/Gauge remains resident for the lifetime
|
|
87
|
-
* of the MeterProvider. Re-creating a metric with the same id but a different
|
|
88
|
-
* MetricType is therefore not safe.
|
|
69
|
+
* Remove a metric from the in-memory registry and from all cached provider instrument maps.
|
|
89
70
|
* @param id The id of the metric.
|
|
90
|
-
* @returns A promise that resolves when the metric
|
|
71
|
+
* @returns A promise that resolves when the metric has been removed.
|
|
91
72
|
*/
|
|
92
73
|
removeMetric(id: string): Promise<void>;
|
|
93
|
-
/**
|
|
94
|
-
* Query the registered metrics, optionally filtered by type.
|
|
95
|
-
* @param type The type of the metric.
|
|
96
|
-
* @param cursor The cursor to request the next page.
|
|
97
|
-
* @param limit Limit the number of entities to return.
|
|
98
|
-
* @returns The matching metrics and an optional cursor for the next page.
|
|
99
|
-
*/
|
|
100
|
-
query(type?: MetricType, cursor?: string, limit?: number): Promise<{
|
|
101
|
-
entities: ITelemetryMetric[];
|
|
102
|
-
cursor?: string;
|
|
103
|
-
}>;
|
|
104
|
-
/**
|
|
105
|
-
* Query the recorded values for a metric, ordered by most recent first.
|
|
106
|
-
* @param id The id of the metric.
|
|
107
|
-
* @param timeStart The inclusive start time (epoch ms).
|
|
108
|
-
* @param timeEnd The inclusive end time (epoch ms).
|
|
109
|
-
* @param cursor The cursor returned by the previous call.
|
|
110
|
-
* @param limit Limit the number of values to return.
|
|
111
|
-
* @returns The metric details, matching values, and an optional cursor for the next page.
|
|
112
|
-
*/
|
|
113
|
-
queryValues(id: string, timeStart?: number, timeEnd?: number, cursor?: string, limit?: number): Promise<{
|
|
114
|
-
metric: ITelemetryMetric;
|
|
115
|
-
entities: ITelemetryMetricValue[];
|
|
116
|
-
cursor?: string;
|
|
117
|
-
}>;
|
|
118
74
|
}
|
package/docs/changelog.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.2](https://github.com/iotaledger/twin-telemetry/compare/telemetry-connector-opentelemetry-v0.9.2...telemetry-connector-opentelemetry-v0.9.2) (2026-08-24)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* release to production ([df2151d](https://github.com/iotaledger/twin-telemetry/commit/df2151d24844fd2c3e6092ce3a6f888ac16219a0))
|
|
9
|
+
* release to production ([#50](https://github.com/iotaledger/twin-telemetry/issues/50)) ([192bfe3](https://github.com/iotaledger/twin-telemetry/commit/192bfe3bea060163f2887e51fc6c2ceb74a7683f))
|
|
10
|
+
* release to production ([#68](https://github.com/iotaledger/twin-telemetry/issues/68)) ([157c6a9](https://github.com/iotaledger/twin-telemetry/commit/157c6a944553e5fb2b5a017586cda1b117c4037a))
|
|
11
|
+
* release to production ([#99](https://github.com/iotaledger/twin-telemetry/issues/99)) ([b9d10ef](https://github.com/iotaledger/twin-telemetry/commit/b9d10ef8722a8688718631095a8a5d03f739785a))
|
|
12
|
+
|
|
13
|
+
## [0.9.2-next.8](https://github.com/iotaledger/twin-telemetry/compare/telemetry-connector-opentelemetry-v0.9.2-next.7...telemetry-connector-opentelemetry-v0.9.2-next.8) (2026-08-21)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* reduce mutex locks ([#94](https://github.com/iotaledger/twin-telemetry/issues/94)) ([8deb4eb](https://github.com/iotaledger/twin-telemetry/commit/8deb4eb51649ed7d9c0828a632115fede8c6a436))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Dependencies
|
|
22
|
+
|
|
23
|
+
* The following workspace dependencies were updated
|
|
24
|
+
* dependencies
|
|
25
|
+
* @twin.org/telemetry-connector-entity-storage bumped from 0.9.2-next.7 to 0.9.2-next.8
|
|
26
|
+
* @twin.org/telemetry-models bumped from 0.9.2-next.7 to 0.9.2-next.8
|
|
27
|
+
|
|
3
28
|
## [0.9.2-next.7](https://github.com/iotaledger/twin-telemetry/compare/telemetry-connector-opentelemetry-v0.9.2-next.6...telemetry-connector-opentelemetry-v0.9.2-next.7) (2026-08-19)
|
|
4
29
|
|
|
5
30
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Class: OpenTelemetryTelemetryConnector
|
|
2
2
|
|
|
3
3
|
Class for performing telemetry operations using OpenTelemetry instruments.
|
|
4
|
-
Metric definitions
|
|
5
|
-
|
|
4
|
+
Metric definitions are held in memory. Persistence and querying are not supported;
|
|
5
|
+
use a multi-connector with an EntityStorageTelemetryConnector for those capabilities.
|
|
6
6
|
|
|
7
7
|
## Implements
|
|
8
8
|
|
|
@@ -72,8 +72,8 @@ The class name of the component.
|
|
|
72
72
|
|
|
73
73
|
> **start**(`nodeLoggingComponentType?`): `Promise`\<`void`\>
|
|
74
74
|
|
|
75
|
-
Enable OTEL forwarding. Subsequent calls to
|
|
76
|
-
|
|
75
|
+
Enable OTEL forwarding. Subsequent calls to addMetricValue will create
|
|
76
|
+
per-tenant/node MeterProviders on demand.
|
|
77
77
|
|
|
78
78
|
#### Parameters
|
|
79
79
|
|
|
@@ -125,7 +125,8 @@ A promise that resolves when all MeterProviders have shut down.
|
|
|
125
125
|
|
|
126
126
|
> **createMetric**(`metric`): `Promise`\<`void`\>
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
Register a new metric definition. If the connector is already started,
|
|
129
|
+
the OTEL instrument is registered immediately for the current context.
|
|
129
130
|
|
|
130
131
|
#### Parameters
|
|
131
132
|
|
|
@@ -139,7 +140,7 @@ The metric details.
|
|
|
139
140
|
|
|
140
141
|
`Promise`\<`void`\>
|
|
141
142
|
|
|
142
|
-
A promise that resolves when the metric has been
|
|
143
|
+
A promise that resolves when the metric has been registered.
|
|
143
144
|
|
|
144
145
|
#### Implementation of
|
|
145
146
|
|
|
@@ -147,71 +148,13 @@ A promise that resolves when the metric has been persisted.
|
|
|
147
148
|
|
|
148
149
|
***
|
|
149
150
|
|
|
150
|
-
### getMetric() {#getmetric}
|
|
151
|
-
|
|
152
|
-
> **getMetric**(`id`): `Promise`\<\{ `metric`: `ITelemetryMetric`; `value`: `ITelemetryMetricValue`; \}\>
|
|
153
|
-
|
|
154
|
-
Get the metric details and its most recent value.
|
|
155
|
-
|
|
156
|
-
#### Parameters
|
|
157
|
-
|
|
158
|
-
##### id
|
|
159
|
-
|
|
160
|
-
`string`
|
|
161
|
-
|
|
162
|
-
The metric id.
|
|
163
|
-
|
|
164
|
-
#### Returns
|
|
165
|
-
|
|
166
|
-
`Promise`\<\{ `metric`: `ITelemetryMetric`; `value`: `ITelemetryMetricValue`; \}\>
|
|
167
|
-
|
|
168
|
-
The metric details and its most recent value.
|
|
169
|
-
|
|
170
|
-
#### Implementation of
|
|
171
|
-
|
|
172
|
-
`ITelemetryConnector.getMetric`
|
|
173
|
-
|
|
174
|
-
***
|
|
175
|
-
|
|
176
|
-
### getMetricValue() {#getmetricvalue}
|
|
177
|
-
|
|
178
|
-
> **getMetricValue**(`id`, `valueId`): `Promise`\<`ITelemetryMetricValue`\>
|
|
179
|
-
|
|
180
|
-
Get a specific metric value by its id.
|
|
181
|
-
|
|
182
|
-
#### Parameters
|
|
183
|
-
|
|
184
|
-
##### id
|
|
185
|
-
|
|
186
|
-
`string`
|
|
187
|
-
|
|
188
|
-
The id of the metric.
|
|
189
|
-
|
|
190
|
-
##### valueId
|
|
191
|
-
|
|
192
|
-
`string`
|
|
193
|
-
|
|
194
|
-
The id of the metric value.
|
|
195
|
-
|
|
196
|
-
#### Returns
|
|
197
|
-
|
|
198
|
-
`Promise`\<`ITelemetryMetricValue`\>
|
|
199
|
-
|
|
200
|
-
The metric value.
|
|
201
|
-
|
|
202
|
-
#### Implementation of
|
|
203
|
-
|
|
204
|
-
`ITelemetryConnector.getMetricValue`
|
|
205
|
-
|
|
206
|
-
***
|
|
207
|
-
|
|
208
151
|
### updateMetric() {#updatemetric}
|
|
209
152
|
|
|
210
153
|
> **updateMetric**(`metric`): `Promise`\<`void`\>
|
|
211
154
|
|
|
212
|
-
Update the
|
|
213
|
-
|
|
214
|
-
|
|
155
|
+
Update the in-memory metadata for a registered metric.
|
|
156
|
+
OpenTelemetry instrument descriptors are immutable once created; only the
|
|
157
|
+
cached label, description and unit are updated.
|
|
215
158
|
|
|
216
159
|
#### Parameters
|
|
217
160
|
|
|
@@ -225,7 +168,7 @@ The metric details (type cannot be changed).
|
|
|
225
168
|
|
|
226
169
|
`Promise`\<`void`\>
|
|
227
170
|
|
|
228
|
-
A promise that resolves when the
|
|
171
|
+
A promise that resolves when the metadata has been updated.
|
|
229
172
|
|
|
230
173
|
#### Implementation of
|
|
231
174
|
|
|
@@ -237,7 +180,7 @@ A promise that resolves when the persisted metadata has been updated.
|
|
|
237
180
|
|
|
238
181
|
> **addMetricValue**(`id`, `value`, `customData?`): `Promise`\<`string`\>
|
|
239
182
|
|
|
240
|
-
Record a metric value.
|
|
183
|
+
Record a metric value and forward it to the appropriate OTEL instrument.
|
|
241
184
|
The current tenant and node IDs are read from `ContextIdStore` and used to
|
|
242
185
|
select (or create) the matching per-tenant/node `MeterProvider`.
|
|
243
186
|
|
|
@@ -263,7 +206,7 @@ Optional custom data forwarded as OTEL attributes.
|
|
|
263
206
|
|
|
264
207
|
`Promise`\<`string`\>
|
|
265
208
|
|
|
266
|
-
|
|
209
|
+
A generated 32-character hex id for the recorded value.
|
|
267
210
|
|
|
268
211
|
#### Implementation of
|
|
269
212
|
|
|
@@ -275,11 +218,7 @@ The id of the new metric value entry.
|
|
|
275
218
|
|
|
276
219
|
> **removeMetric**(`id`): `Promise`\<`void`\>
|
|
277
220
|
|
|
278
|
-
Remove a metric and
|
|
279
|
-
Note: OpenTelemetry exposes no API to deregister an instrument from a Meter,
|
|
280
|
-
so the underlying Counter/UpDownCounter/Gauge remains resident for the lifetime
|
|
281
|
-
of the MeterProvider. Re-creating a metric with the same id but a different
|
|
282
|
-
MetricType is therefore not safe.
|
|
221
|
+
Remove a metric from the in-memory registry and from all cached provider instrument maps.
|
|
283
222
|
|
|
284
223
|
#### Parameters
|
|
285
224
|
|
|
@@ -293,96 +232,8 @@ The id of the metric.
|
|
|
293
232
|
|
|
294
233
|
`Promise`\<`void`\>
|
|
295
234
|
|
|
296
|
-
A promise that resolves when the metric
|
|
235
|
+
A promise that resolves when the metric has been removed.
|
|
297
236
|
|
|
298
237
|
#### Implementation of
|
|
299
238
|
|
|
300
239
|
`ITelemetryConnector.removeMetric`
|
|
301
|
-
|
|
302
|
-
***
|
|
303
|
-
|
|
304
|
-
### query() {#query}
|
|
305
|
-
|
|
306
|
-
> **query**(`type?`, `cursor?`, `limit?`): `Promise`\<\{ `entities`: `ITelemetryMetric`[]; `cursor?`: `string`; \}\>
|
|
307
|
-
|
|
308
|
-
Query the registered metrics, optionally filtered by type.
|
|
309
|
-
|
|
310
|
-
#### Parameters
|
|
311
|
-
|
|
312
|
-
##### type?
|
|
313
|
-
|
|
314
|
-
`MetricType`
|
|
315
|
-
|
|
316
|
-
The type of the metric.
|
|
317
|
-
|
|
318
|
-
##### cursor?
|
|
319
|
-
|
|
320
|
-
`string`
|
|
321
|
-
|
|
322
|
-
The cursor to request the next page.
|
|
323
|
-
|
|
324
|
-
##### limit?
|
|
325
|
-
|
|
326
|
-
`number`
|
|
327
|
-
|
|
328
|
-
Limit the number of entities to return.
|
|
329
|
-
|
|
330
|
-
#### Returns
|
|
331
|
-
|
|
332
|
-
`Promise`\<\{ `entities`: `ITelemetryMetric`[]; `cursor?`: `string`; \}\>
|
|
333
|
-
|
|
334
|
-
The matching metrics and an optional cursor for the next page.
|
|
335
|
-
|
|
336
|
-
#### Implementation of
|
|
337
|
-
|
|
338
|
-
`ITelemetryConnector.query`
|
|
339
|
-
|
|
340
|
-
***
|
|
341
|
-
|
|
342
|
-
### queryValues() {#queryvalues}
|
|
343
|
-
|
|
344
|
-
> **queryValues**(`id`, `timeStart?`, `timeEnd?`, `cursor?`, `limit?`): `Promise`\<\{ `metric`: `ITelemetryMetric`; `entities`: `ITelemetryMetricValue`[]; `cursor?`: `string`; \}\>
|
|
345
|
-
|
|
346
|
-
Query the recorded values for a metric, ordered by most recent first.
|
|
347
|
-
|
|
348
|
-
#### Parameters
|
|
349
|
-
|
|
350
|
-
##### id
|
|
351
|
-
|
|
352
|
-
`string`
|
|
353
|
-
|
|
354
|
-
The id of the metric.
|
|
355
|
-
|
|
356
|
-
##### timeStart?
|
|
357
|
-
|
|
358
|
-
`number`
|
|
359
|
-
|
|
360
|
-
The inclusive start time (epoch ms).
|
|
361
|
-
|
|
362
|
-
##### timeEnd?
|
|
363
|
-
|
|
364
|
-
`number`
|
|
365
|
-
|
|
366
|
-
The inclusive end time (epoch ms).
|
|
367
|
-
|
|
368
|
-
##### cursor?
|
|
369
|
-
|
|
370
|
-
`string`
|
|
371
|
-
|
|
372
|
-
The cursor returned by the previous call.
|
|
373
|
-
|
|
374
|
-
##### limit?
|
|
375
|
-
|
|
376
|
-
`number`
|
|
377
|
-
|
|
378
|
-
Limit the number of values to return.
|
|
379
|
-
|
|
380
|
-
#### Returns
|
|
381
|
-
|
|
382
|
-
`Promise`\<\{ `metric`: `ITelemetryMetric`; `entities`: `ITelemetryMetricValue`[]; `cursor?`: `string`; \}\>
|
|
383
|
-
|
|
384
|
-
The metric details, matching values, and an optional cursor for the next page.
|
|
385
|
-
|
|
386
|
-
#### Implementation of
|
|
387
|
-
|
|
388
|
-
`ITelemetryConnector.queryValues`
|
|
@@ -4,56 +4,6 @@ The options for the OpenTelemetry telemetry connector constructor.
|
|
|
4
4
|
|
|
5
5
|
## Properties
|
|
6
6
|
|
|
7
|
-
### mutexTimeoutMs? {#mutextimeoutms}
|
|
8
|
-
|
|
9
|
-
> `optional` **mutexTimeoutMs?**: `number`
|
|
10
|
-
|
|
11
|
-
Timeout in milliseconds for acquiring the inner entity-storage metric write mutex lock.
|
|
12
|
-
|
|
13
|
-
***
|
|
14
|
-
|
|
15
|
-
### batchSize? {#batchsize}
|
|
16
|
-
|
|
17
|
-
> `optional` **batchSize?**: `number`
|
|
18
|
-
|
|
19
|
-
Number of metric value entries to accumulate before flushing to entity storage.
|
|
20
|
-
|
|
21
|
-
#### Default
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
10
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
***
|
|
28
|
-
|
|
29
|
-
### batchIntervalMs? {#batchintervalms}
|
|
30
|
-
|
|
31
|
-
> `optional` **batchIntervalMs?**: `number`
|
|
32
|
-
|
|
33
|
-
Interval in milliseconds between automatic batch flushes to entity storage.
|
|
34
|
-
|
|
35
|
-
#### Default
|
|
36
|
-
|
|
37
|
-
```ts
|
|
38
|
-
5000
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
***
|
|
42
|
-
|
|
43
|
-
### maxCacheSize? {#maxcachesize}
|
|
44
|
-
|
|
45
|
-
> `optional` **maxCacheSize?**: `number`
|
|
46
|
-
|
|
47
|
-
Maximum number of entries retained in the write-ahead cache if a flush fails.
|
|
48
|
-
|
|
49
|
-
#### Default
|
|
50
|
-
|
|
51
|
-
```ts
|
|
52
|
-
1000
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
***
|
|
56
|
-
|
|
57
7
|
### meterName? {#metername}
|
|
58
8
|
|
|
59
9
|
> `optional` **meterName?**: `string`
|
|
@@ -4,45 +4,6 @@ The options for the OpenTelemetry telemetry connector constructor.
|
|
|
4
4
|
|
|
5
5
|
## Properties
|
|
6
6
|
|
|
7
|
-
### loggingComponentType? {#loggingcomponenttype}
|
|
8
|
-
|
|
9
|
-
> `optional` **loggingComponentType?**: `string`
|
|
10
|
-
|
|
11
|
-
The component type to use for logging inside the connector and the inner
|
|
12
|
-
entity-storage connector. When omitted logging is disabled.
|
|
13
|
-
|
|
14
|
-
***
|
|
15
|
-
|
|
16
|
-
### telemetryMetricStorageConnectorType? {#telemetrymetricstorageconnectortype}
|
|
17
|
-
|
|
18
|
-
> `optional` **telemetryMetricStorageConnectorType?**: `string`
|
|
19
|
-
|
|
20
|
-
The entity storage connector type to use for storing metric definitions.
|
|
21
|
-
Must be registered in `EntityStorageConnectorFactory` before calling `start()`.
|
|
22
|
-
|
|
23
|
-
#### Default
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
telemetry-metric
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
***
|
|
30
|
-
|
|
31
|
-
### telemetryMetricValueStorageConnectorType? {#telemetrymetricvaluestorageconnectortype}
|
|
32
|
-
|
|
33
|
-
> `optional` **telemetryMetricValueStorageConnectorType?**: `string`
|
|
34
|
-
|
|
35
|
-
The entity storage connector type to use for storing metric values.
|
|
36
|
-
Must be registered in `EntityStorageConnectorFactory` before calling `start()`.
|
|
37
|
-
|
|
38
|
-
#### Default
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
telemetry-metric-value
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
***
|
|
45
|
-
|
|
46
7
|
### config? {#config}
|
|
47
8
|
|
|
48
9
|
> `optional` **config?**: [`IOpenTelemetryTelemetryConnectorConfig`](IOpenTelemetryTelemetryConnectorConfig.md)
|
package/locales/en.json
CHANGED
|
@@ -4,5 +4,12 @@
|
|
|
4
4
|
"connectorStarted": "The connector was started with \"{readerCount}\" metric reader(s)",
|
|
5
5
|
"connectorStopped": "The connector was stopped"
|
|
6
6
|
}
|
|
7
|
+
},
|
|
8
|
+
"error": {
|
|
9
|
+
"openTelemetryTelemetryConnector": {
|
|
10
|
+
"metricNotFound": "The metric could not be found \"{notFoundId}\"",
|
|
11
|
+
"counterIncOnly": "A counter metric can only be incremented, or added to with an integer",
|
|
12
|
+
"gaugeNoIncDec": "A gauge can not be incremented or decremented"
|
|
13
|
+
}
|
|
7
14
|
}
|
|
8
15
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/telemetry-connector-opentelemetry",
|
|
3
|
-
"version": "0.9.2
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "OpenTelemetry connector for pushing telemetry metrics to OTEL-compatible backends.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
"@opentelemetry/exporter-prometheus": "0.221.0",
|
|
20
20
|
"@opentelemetry/resources": "2.10.0",
|
|
21
21
|
"@opentelemetry/sdk-metrics": "2.10.0",
|
|
22
|
-
"@twin.org/context": "
|
|
23
|
-
"@twin.org/core": "
|
|
24
|
-
"@twin.org/entity-storage-models": "
|
|
25
|
-
"@twin.org/logging-models": "
|
|
26
|
-
"@twin.org/nameof": "
|
|
27
|
-
"@twin.org/telemetry-connector-entity-storage": "0.9.2
|
|
28
|
-
"@twin.org/telemetry-models": "0.9.2
|
|
22
|
+
"@twin.org/context": "^0.9.2",
|
|
23
|
+
"@twin.org/core": "^0.9.2",
|
|
24
|
+
"@twin.org/entity-storage-models": "^0.9.2",
|
|
25
|
+
"@twin.org/logging-models": "^0.9.2",
|
|
26
|
+
"@twin.org/nameof": "^0.9.2",
|
|
27
|
+
"@twin.org/telemetry-connector-entity-storage": "^0.9.2",
|
|
28
|
+
"@twin.org/telemetry-models": "^0.9.2"
|
|
29
29
|
},
|
|
30
30
|
"main": "./dist/es/index.js",
|
|
31
31
|
"types": "./dist/types/index.d.ts",
|