@twin.org/telemetry-service 0.0.3-next.11 → 0.0.3-next.13

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.
@@ -1,4 +1,4 @@
1
- import { BaseError, ComponentFactory, Is } from "@twin.org/core";
1
+ import { BaseError, ComponentFactory } from "@twin.org/core";
2
2
  import { MetricsProducerFactory } from "@twin.org/telemetry-models";
3
3
  /**
4
4
  * Collects metrics from registered producers at a fixed interval.
@@ -14,10 +14,10 @@ export class MetricsCollectorService {
14
14
  */
15
15
  _loggingComponent;
16
16
  /**
17
- * Tenant component if configured.
17
+ * Platform component.
18
18
  * @internal
19
19
  */
20
- _tenantComponent;
20
+ _platformComponent;
21
21
  /**
22
22
  * Polling interval in milliseconds.
23
23
  * @internal
@@ -38,8 +38,8 @@ export class MetricsCollectorService {
38
38
  * @param options The options for the service.
39
39
  */
40
40
  constructor(options) {
41
- this._loggingComponent = ComponentFactory.getIfExists(options?.loggingComponentType ?? "logging");
42
- this._tenantComponent = ComponentFactory.getIfExists(options?.tenantComponentType ?? "tenant");
41
+ this._loggingComponent = ComponentFactory.getIfExists(options?.loggingComponentType);
42
+ this._platformComponent = ComponentFactory.get(options?.platformComponentType ?? "platform");
43
43
  const intervalMs = options?.config?.intervalMs ?? 60_000;
44
44
  if (!Number.isFinite(intervalMs) || intervalMs <= 0) {
45
45
  throw new RangeError(`intervalMs must be a finite positive number, got ${intervalMs}`);
@@ -65,12 +65,7 @@ export class MetricsCollectorService {
65
65
  const names = MetricsProducerFactory.names();
66
66
  const producers = names.map(name => MetricsProducerFactory.get(name));
67
67
  for (const producer of producers) {
68
- if (!Is.empty(this._tenantComponent)) {
69
- await this._tenantComponent.runPerTenant(async () => producer.register());
70
- }
71
- else {
72
- await producer.register();
73
- }
68
+ await this._platformComponent.execute(async () => producer.register());
74
69
  }
75
70
  await this.tick();
76
71
  }
@@ -93,12 +88,7 @@ export class MetricsCollectorService {
93
88
  const producers = names.map(name => MetricsProducerFactory.get(name));
94
89
  for (const producer of producers) {
95
90
  try {
96
- if (!Is.empty(this._tenantComponent)) {
97
- await this._tenantComponent.runPerTenant(async () => producer.collect());
98
- }
99
- else {
100
- await producer.collect();
101
- }
91
+ await this._platformComponent.execute(async () => producer.collect());
102
92
  }
103
93
  catch (err) {
104
94
  await this._loggingComponent?.log({
@@ -1 +1 @@
1
- {"version":3,"file":"metricsCollectorService.js","sourceRoot":"","sources":["../../src/metricsCollectorService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITenantComponent } from \"@twin.org/api-models\";\nimport { BaseError, ComponentFactory, Is } from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\ttype IMetricsCollectorComponent,\n\tMetricsProducerFactory\n} from \"@twin.org/telemetry-models\";\nimport type { IMetricsCollectorServiceConstructorOptions } from \"./models/IMetricsCollectorServiceConstructorOptions.js\";\n\n/**\n * Collects metrics from registered producers at a fixed interval.\n */\nexport class MetricsCollectorService implements IMetricsCollectorComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MetricsCollectorService>();\n\n\t/**\n\t * Resolved logging component for internal use.\n\t * @internal\n\t */\n\tprivate readonly _loggingComponent?: ILoggingComponent;\n\n\t/**\n\t * Tenant component if configured.\n\t * @internal\n\t */\n\tprivate readonly _tenantComponent?: ITenantComponent;\n\n\t/**\n\t * Polling interval in milliseconds.\n\t * @internal\n\t */\n\tprivate readonly _intervalMs: number;\n\n\t/**\n\t * Pending scheduled tick handle.\n\t * @internal\n\t */\n\tprivate _timer: ReturnType<typeof globalThis.setTimeout> | undefined;\n\n\t/**\n\t * Whether the service is currently running.\n\t * @internal\n\t */\n\tprivate _running: boolean;\n\n\t/**\n\t * Create a new instance of MetricsCollectorService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options?: IMetricsCollectorServiceConstructorOptions) {\n\t\tthis._loggingComponent = ComponentFactory.getIfExists<ILoggingComponent>(\n\t\t\toptions?.loggingComponentType ?? \"logging\"\n\t\t);\n\t\tthis._tenantComponent = ComponentFactory.getIfExists<ITenantComponent>(\n\t\t\toptions?.tenantComponentType ?? \"tenant\"\n\t\t);\n\n\t\tconst intervalMs = options?.config?.intervalMs ?? 60_000;\n\t\tif (!Number.isFinite(intervalMs) || intervalMs <= 0) {\n\t\t\tthrow new RangeError(`intervalMs must be a finite positive number, got ${intervalMs}`);\n\t\t}\n\t\tthis._intervalMs = intervalMs;\n\t\tthis._running = false;\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 MetricsCollectorService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Start the service: register all producers and begin the polling cycle.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tif (this._running) {\n\t\t\treturn;\n\t\t}\n\t\tthis._running = true;\n\n\t\tconst names = MetricsProducerFactory.names();\n\t\tconst producers = names.map(name => MetricsProducerFactory.get(name));\n\n\t\tfor (const producer of producers) {\n\t\t\tif (!Is.empty(this._tenantComponent)) {\n\t\t\t\tawait this._tenantComponent.runPerTenant(async () => producer.register());\n\t\t\t} else {\n\t\t\t\tawait producer.register();\n\t\t\t}\n\t\t}\n\n\t\tawait this.tick();\n\t}\n\n\t/**\n\t * Stop the service and cancel the pending timer.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis._running = false;\n\t\tif (this._timer !== undefined) {\n\t\t\tglobalThis.clearTimeout(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * One collection cycle across all registered producers.\n\t * Public so tests can drive it deterministically.\n\t */\n\tpublic async tick(): Promise<void> {\n\t\tconst names = MetricsProducerFactory.names();\n\t\tconst producers = names.map(name => MetricsProducerFactory.get(name));\n\n\t\tfor (const producer of producers) {\n\t\t\ttry {\n\t\t\t\tif (!Is.empty(this._tenantComponent)) {\n\t\t\t\t\tawait this._tenantComponent.runPerTenant(async () => producer.collect());\n\t\t\t\t} else {\n\t\t\t\t\tawait producer.collect();\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tawait this._loggingComponent?.log({\n\t\t\t\t\tsource: MetricsCollectorService.CLASS_NAME,\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tmessage: \"producerCollectionFailed\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tproducer: producer.className()\n\t\t\t\t\t},\n\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis.scheduleNext();\n\t}\n\n\t/**\n\t * Schedule the next tick. Re-schedules itself after each tick completes.\n\t * @internal\n\t */\n\tprivate scheduleNext(): void {\n\t\tif (this._running) {\n\t\t\tthis._timer = globalThis.setTimeout(async () => {\n\t\t\t\tawait this.tick();\n\t\t\t}, this._intervalMs);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"metricsCollectorService.js","sourceRoot":"","sources":["../../src/metricsCollectorService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IPlatformComponent } from \"@twin.org/api-models\";\nimport { BaseError, ComponentFactory } from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\ttype IMetricsCollectorComponent,\n\tMetricsProducerFactory\n} from \"@twin.org/telemetry-models\";\nimport type { IMetricsCollectorServiceConstructorOptions } from \"./models/IMetricsCollectorServiceConstructorOptions.js\";\n\n/**\n * Collects metrics from registered producers at a fixed interval.\n */\nexport class MetricsCollectorService implements IMetricsCollectorComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MetricsCollectorService>();\n\n\t/**\n\t * Resolved logging component for internal use.\n\t * @internal\n\t */\n\tprivate readonly _loggingComponent?: ILoggingComponent;\n\n\t/**\n\t * Platform component.\n\t * @internal\n\t */\n\tprivate readonly _platformComponent: IPlatformComponent;\n\n\t/**\n\t * Polling interval in milliseconds.\n\t * @internal\n\t */\n\tprivate readonly _intervalMs: number;\n\n\t/**\n\t * Pending scheduled tick handle.\n\t * @internal\n\t */\n\tprivate _timer: ReturnType<typeof globalThis.setTimeout> | undefined;\n\n\t/**\n\t * Whether the service is currently running.\n\t * @internal\n\t */\n\tprivate _running: boolean;\n\n\t/**\n\t * Create a new instance of MetricsCollectorService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options?: IMetricsCollectorServiceConstructorOptions) {\n\t\tthis._loggingComponent = ComponentFactory.getIfExists<ILoggingComponent>(\n\t\t\toptions?.loggingComponentType\n\t\t);\n\t\tthis._platformComponent = ComponentFactory.get<IPlatformComponent>(\n\t\t\toptions?.platformComponentType ?? \"platform\"\n\t\t);\n\n\t\tconst intervalMs = options?.config?.intervalMs ?? 60_000;\n\t\tif (!Number.isFinite(intervalMs) || intervalMs <= 0) {\n\t\t\tthrow new RangeError(`intervalMs must be a finite positive number, got ${intervalMs}`);\n\t\t}\n\t\tthis._intervalMs = intervalMs;\n\t\tthis._running = false;\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 MetricsCollectorService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Start the service: register all producers and begin the polling cycle.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tif (this._running) {\n\t\t\treturn;\n\t\t}\n\t\tthis._running = true;\n\n\t\tconst names = MetricsProducerFactory.names();\n\t\tconst producers = names.map(name => MetricsProducerFactory.get(name));\n\n\t\tfor (const producer of producers) {\n\t\t\tawait this._platformComponent.execute(async () => producer.register());\n\t\t}\n\n\t\tawait this.tick();\n\t}\n\n\t/**\n\t * Stop the service and cancel the pending timer.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis._running = false;\n\t\tif (this._timer !== undefined) {\n\t\t\tglobalThis.clearTimeout(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * One collection cycle across all registered producers.\n\t * Public so tests can drive it deterministically.\n\t */\n\tpublic async tick(): Promise<void> {\n\t\tconst names = MetricsProducerFactory.names();\n\t\tconst producers = names.map(name => MetricsProducerFactory.get(name));\n\n\t\tfor (const producer of producers) {\n\t\t\ttry {\n\t\t\t\tawait this._platformComponent.execute(async () => producer.collect());\n\t\t\t} catch (err) {\n\t\t\t\tawait this._loggingComponent?.log({\n\t\t\t\t\tsource: MetricsCollectorService.CLASS_NAME,\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tmessage: \"producerCollectionFailed\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tproducer: producer.className()\n\t\t\t\t\t},\n\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis.scheduleNext();\n\t}\n\n\t/**\n\t * Schedule the next tick. Re-schedules itself after each tick completes.\n\t * @internal\n\t */\n\tprivate scheduleNext(): void {\n\t\tif (this._running) {\n\t\t\tthis._timer = globalThis.setTimeout(async () => {\n\t\t\t\tawait this.tick();\n\t\t\t}, this._intervalMs);\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IMetricsCollectorServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IMetricsCollectorServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IMetricsCollectorServiceConfig } from \"./IMetricsCollectorServiceConfig.js\";\n\n/**\n * The options for the metrics collector service constructor.\n */\nexport interface IMetricsCollectorServiceConstructorOptions {\n\t/**\n\t * The type of the logging component to use.\n\t * @default logging\n\t */\n\tloggingComponentType?: string;\n\n\t/**\n\t * The type of the tenant component to use for partitioned producers.\n\t * @default tenant\n\t */\n\ttenantComponentType?: string;\n\n\t/**\n\t * The configuration options for the metrics collector service.\n\t */\n\tconfig?: IMetricsCollectorServiceConfig;\n}\n"]}
1
+ {"version":3,"file":"IMetricsCollectorServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IMetricsCollectorServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IMetricsCollectorServiceConfig } from \"./IMetricsCollectorServiceConfig.js\";\n\n/**\n * The options for the metrics collector service constructor.\n */\nexport interface IMetricsCollectorServiceConstructorOptions {\n\t/**\n\t * The type of the logging component to use, can be undefined for no logging.\n\t */\n\tloggingComponentType?: string;\n\n\t/**\n\t * The type of the platform component to use for partitioned producers.\n\t * @default platform\n\t */\n\tplatformComponentType?: string;\n\n\t/**\n\t * The configuration options for the metrics collector service.\n\t */\n\tconfig?: IMetricsCollectorServiceConfig;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ITelemetryServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/ITelemetryServiceConstructorOptions.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The options for the telemetry service constructor.\n */\nexport interface ITelemetryServiceConstructorOptions {\n\t/**\n\t * The type of the telemetry connector to use.\n\t * @default telemetry\n\t */\n\ttelemetryConnectorType?: string;\n\n\t/**\n\t * The type of the tenant component to use for partition iteration.\n\t * @default tenant\n\t */\n\ttenantComponentType?: string;\n}\n"]}
1
+ {"version":3,"file":"ITelemetryServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/ITelemetryServiceConstructorOptions.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The options for the telemetry service constructor.\n */\nexport interface ITelemetryServiceConstructorOptions {\n\t/**\n\t * The type of the telemetry connector to use.\n\t * @default telemetry\n\t */\n\ttelemetryConnectorType?: string;\n\n\t/**\n\t * The type of the platform component to use for partition iteration.\n\t * @default platform\n\t */\n\tplatformComponentType?: string;\n}\n"]}
@@ -15,17 +15,17 @@ export class TelemetryService {
15
15
  */
16
16
  _telemetryConnector;
17
17
  /**
18
- * Tenant component for internal use, if configured.
18
+ * Platform component.
19
19
  * @internal
20
20
  */
21
- _tenantComponent;
21
+ _platformComponent;
22
22
  /**
23
23
  * Create a new instance of TelemetryService.
24
24
  * @param options The options for the connector.
25
25
  */
26
26
  constructor(options) {
27
27
  this._telemetryConnector = TelemetryConnectorFactory.get(options?.telemetryConnectorType ?? "telemetry");
28
- this._tenantComponent = ComponentFactory.getIfExists(options?.tenantComponentType ?? "tenant");
28
+ this._platformComponent = ComponentFactory.get(options?.platformComponentType ?? "platform");
29
29
  }
30
30
  /**
31
31
  * Returns the class name of the component.
@@ -41,15 +41,16 @@ export class TelemetryService {
41
41
  */
42
42
  async createMetric(metric) {
43
43
  Guards.object(TelemetryService.CLASS_NAME, "metric", metric);
44
- // Create metric must be run once per tenant in multi tenant mode
45
- // if the tenant id is not already set in the context
46
- // as these are most likely startup metrics run outside of a tenant context
44
+ // If we don't have a tenant context ID and the tenant component is multi-tenant,
45
+ // we consider the entry as per-tenant and run it in all the tenant context to ensure
46
+ // the metric is created for each tenant.
47
47
  const contextIds = (await ContextIdStore.getContextIds()) ?? {};
48
- if (Is.stringValue(contextIds[ContextIdKeys.Tenant]) || Is.empty(this._tenantComponent)) {
49
- await this._telemetryConnector.createMetric(metric);
48
+ const perTenant = !Is.stringValue(contextIds[ContextIdKeys.Tenant]) && this._platformComponent.isMultiTenant();
49
+ if (perTenant) {
50
+ await this._platformComponent.execute(async () => this._telemetryConnector.createMetric(metric));
50
51
  }
51
52
  else {
52
- await this._tenantComponent.runPerTenant(async () => this._telemetryConnector.createMetric(metric));
53
+ await this._telemetryConnector.createMetric(metric);
53
54
  }
54
55
  }
55
56
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"telemetryService.js","sourceRoot":"","sources":["../../src/telemetryService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITenantComponent } from \"@twin.org/api-models\";\nimport { ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport { ComponentFactory, Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tTelemetryConnectorFactory,\n\ttype ITelemetryComponent,\n\ttype ITelemetryConnector,\n\ttype ITelemetryMetric,\n\ttype ITelemetryMetricValue,\n\ttype MetricCounterOperation,\n\ttype MetricType\n} from \"@twin.org/telemetry-models\";\nimport type { ITelemetryServiceConstructorOptions } from \"./models/ITelemetryServiceConstructorOptions.js\";\n\n/**\n * Service for performing telemetry operations to a connector.\n */\nexport class TelemetryService implements ITelemetryComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TelemetryService>();\n\n\t/**\n\t * Telemetry connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _telemetryConnector: ITelemetryConnector;\n\n\t/**\n\t * Tenant component for internal use, if configured.\n\t * @internal\n\t */\n\tprivate readonly _tenantComponent?: ITenantComponent;\n\n\t/**\n\t * Create a new instance of TelemetryService.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: ITelemetryServiceConstructorOptions) {\n\t\tthis._telemetryConnector = TelemetryConnectorFactory.get(\n\t\t\toptions?.telemetryConnectorType ?? \"telemetry\"\n\t\t);\n\t\tthis._tenantComponent = ComponentFactory.getIfExists<ITenantComponent>(\n\t\t\toptions?.tenantComponentType ?? \"tenant\"\n\t\t);\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 TelemetryService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Create a new metric.\n\t * @param metric The metric details.\n\t * @returns Nothing.\n\t */\n\tpublic async createMetric(metric: ITelemetryMetric): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(TelemetryService.CLASS_NAME, nameof(metric), metric);\n\n\t\t// Create metric must be run once per tenant in multi tenant mode\n\t\t// if the tenant id is not already set in the context\n\t\t// as these are most likely startup metrics run outside of a tenant context\n\t\tconst contextIds = (await ContextIdStore.getContextIds()) ?? {};\n\t\tif (Is.stringValue(contextIds[ContextIdKeys.Tenant]) || Is.empty(this._tenantComponent)) {\n\t\t\tawait this._telemetryConnector.createMetric(metric);\n\t\t} else {\n\t\t\tawait this._tenantComponent.runPerTenant(async () =>\n\t\t\t\tthis._telemetryConnector.createMetric(metric)\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Get the metric details and it's most recent value.\n\t * @param id The metric id.\n\t * @returns The metric details and it's most recent value.\n\t */\n\tpublic async getMetric(id: string): Promise<{\n\t\tmetric: ITelemetryMetric;\n\t\tvalue: ITelemetryMetricValue;\n\t}> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.getMetric(id);\n\t}\n\n\t/**\n\t * Update metric.\n\t * @param metric The metric details.\n\t * @returns Nothing.\n\t */\n\tpublic async updateMetric(metric: Omit<ITelemetryMetric, \"type\">): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(TelemetryService.CLASS_NAME, nameof(metric), metric);\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(metric.id), metric.id);\n\t\treturn this._telemetryConnector.updateMetric(metric);\n\t}\n\n\t/**\n\t * Add a metric value.\n\t * @param id The id of the metric.\n\t * @param value The value for the add operation.\n\t * @param customData The custom data for the add operation.\n\t * @returns The created metric value id.\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(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\tGuards.defined(TelemetryService.CLASS_NAME, nameof(value), value);\n\t\treturn this._telemetryConnector.addMetricValue(id, value, customData);\n\t}\n\n\t/**\n\t * Remove metric.\n\t * @param id The id of the metric.\n\t * @returns Nothing.\n\t */\n\tpublic async removeMetric(id: string): Promise<void> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.removeMetric(id);\n\t}\n\n\t/**\n\t * Query the metrics.\n\t * @param type The type of the metric.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t * @throws NotImplementedError if the implementation does not support retrieval.\n\t */\n\tpublic async query(\n\t\ttype?: MetricType,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The metrics.\n\t\t */\n\t\tentities: ITelemetryMetric[];\n\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\treturn this._telemetryConnector.query(type, cursor, limit);\n\t}\n\n\t/**\n\t * Query the metric values.\n\t * @param id The id of the metric.\n\t * @param timeStart The inclusive time as the start of the metric entries.\n\t * @param timeEnd The inclusive time as the end of the metric entries.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t * @throws NotImplementedError if the implementation does not support retrieval.\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\t/**\n\t\t * The metric details.\n\t\t */\n\t\tmetric: ITelemetryMetric;\n\t\t/**\n\t\t * The values for the metric.\n\t\t */\n\t\tentities: ITelemetryMetricValue[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.queryValues(id, timeStart, timeEnd, cursor, limit);\n\t}\n}\n"]}
1
+ {"version":3,"file":"telemetryService.js","sourceRoot":"","sources":["../../src/telemetryService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IPlatformComponent } from \"@twin.org/api-models\";\nimport { ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport { ComponentFactory, Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tTelemetryConnectorFactory,\n\ttype ITelemetryComponent,\n\ttype ITelemetryConnector,\n\ttype ITelemetryMetric,\n\ttype ITelemetryMetricValue,\n\ttype MetricCounterOperation,\n\ttype MetricType\n} from \"@twin.org/telemetry-models\";\nimport type { ITelemetryServiceConstructorOptions } from \"./models/ITelemetryServiceConstructorOptions.js\";\n\n/**\n * Service for performing telemetry operations to a connector.\n */\nexport class TelemetryService implements ITelemetryComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TelemetryService>();\n\n\t/**\n\t * Telemetry connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _telemetryConnector: ITelemetryConnector;\n\n\t/**\n\t * Platform component.\n\t * @internal\n\t */\n\tprivate readonly _platformComponent: IPlatformComponent;\n\n\t/**\n\t * Create a new instance of TelemetryService.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: ITelemetryServiceConstructorOptions) {\n\t\tthis._telemetryConnector = TelemetryConnectorFactory.get(\n\t\t\toptions?.telemetryConnectorType ?? \"telemetry\"\n\t\t);\n\t\tthis._platformComponent = ComponentFactory.get<IPlatformComponent>(\n\t\t\toptions?.platformComponentType ?? \"platform\"\n\t\t);\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 TelemetryService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Create a new metric.\n\t * @param metric The metric details.\n\t * @returns Nothing.\n\t */\n\tpublic async createMetric(metric: ITelemetryMetric): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(TelemetryService.CLASS_NAME, nameof(metric), metric);\n\n\t\t// If we don't have a tenant context ID and the tenant component is multi-tenant,\n\t\t// we consider the entry as per-tenant and run it in all the tenant context to ensure\n\t\t// the metric is created for each tenant.\n\t\tconst contextIds = (await ContextIdStore.getContextIds()) ?? {};\n\t\tconst perTenant =\n\t\t\t!Is.stringValue(contextIds[ContextIdKeys.Tenant]) && this._platformComponent.isMultiTenant();\n\n\t\tif (perTenant) {\n\t\t\tawait this._platformComponent.execute(async () =>\n\t\t\t\tthis._telemetryConnector.createMetric(metric)\n\t\t\t);\n\t\t} else {\n\t\t\tawait this._telemetryConnector.createMetric(metric);\n\t\t}\n\t}\n\n\t/**\n\t * Get the metric details and it's most recent value.\n\t * @param id The metric id.\n\t * @returns The metric details and it's most recent value.\n\t */\n\tpublic async getMetric(id: string): Promise<{\n\t\tmetric: ITelemetryMetric;\n\t\tvalue: ITelemetryMetricValue;\n\t}> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.getMetric(id);\n\t}\n\n\t/**\n\t * Update metric.\n\t * @param metric The metric details.\n\t * @returns Nothing.\n\t */\n\tpublic async updateMetric(metric: Omit<ITelemetryMetric, \"type\">): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(TelemetryService.CLASS_NAME, nameof(metric), metric);\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(metric.id), metric.id);\n\t\treturn this._telemetryConnector.updateMetric(metric);\n\t}\n\n\t/**\n\t * Add a metric value.\n\t * @param id The id of the metric.\n\t * @param value The value for the add operation.\n\t * @param customData The custom data for the add operation.\n\t * @returns The created metric value id.\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(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\tGuards.defined(TelemetryService.CLASS_NAME, nameof(value), value);\n\t\treturn this._telemetryConnector.addMetricValue(id, value, customData);\n\t}\n\n\t/**\n\t * Remove metric.\n\t * @param id The id of the metric.\n\t * @returns Nothing.\n\t */\n\tpublic async removeMetric(id: string): Promise<void> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.removeMetric(id);\n\t}\n\n\t/**\n\t * Query the metrics.\n\t * @param type The type of the metric.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t * @throws NotImplementedError if the implementation does not support retrieval.\n\t */\n\tpublic async query(\n\t\ttype?: MetricType,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The metrics.\n\t\t */\n\t\tentities: ITelemetryMetric[];\n\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\treturn this._telemetryConnector.query(type, cursor, limit);\n\t}\n\n\t/**\n\t * Query the metric values.\n\t * @param id The id of the metric.\n\t * @param timeStart The inclusive time as the start of the metric entries.\n\t * @param timeEnd The inclusive time as the end of the metric entries.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t * @throws NotImplementedError if the implementation does not support retrieval.\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\t/**\n\t\t * The metric details.\n\t\t */\n\t\tmetric: ITelemetryMetric;\n\t\t/**\n\t\t * The values for the metric.\n\t\t */\n\t\tentities: ITelemetryMetricValue[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.queryValues(id, timeStart, timeEnd, cursor, limit);\n\t}\n}\n"]}
@@ -4,15 +4,14 @@ import type { IMetricsCollectorServiceConfig } from "./IMetricsCollectorServiceC
4
4
  */
5
5
  export interface IMetricsCollectorServiceConstructorOptions {
6
6
  /**
7
- * The type of the logging component to use.
8
- * @default logging
7
+ * The type of the logging component to use, can be undefined for no logging.
9
8
  */
10
9
  loggingComponentType?: string;
11
10
  /**
12
- * The type of the tenant component to use for partitioned producers.
13
- * @default tenant
11
+ * The type of the platform component to use for partitioned producers.
12
+ * @default platform
14
13
  */
15
- tenantComponentType?: string;
14
+ platformComponentType?: string;
16
15
  /**
17
16
  * The configuration options for the metrics collector service.
18
17
  */
@@ -8,8 +8,8 @@ export interface ITelemetryServiceConstructorOptions {
8
8
  */
9
9
  telemetryConnectorType?: string;
10
10
  /**
11
- * The type of the tenant component to use for partition iteration.
12
- * @default tenant
11
+ * The type of the platform component to use for partition iteration.
12
+ * @default platform
13
13
  */
14
- tenantComponentType?: string;
14
+ platformComponentType?: string;
15
15
  }
package/docs/changelog.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.3-next.13](https://github.com/iotaledger/twin-telemetry/compare/telemetry-service-v0.0.3-next.12...telemetry-service-v0.0.3-next.13) (2026-06-11)
4
+
5
+
6
+ ### Features
7
+
8
+ * organization identifiers ([#42](https://github.com/iotaledger/twin-telemetry/issues/42)) ([1cea68c](https://github.com/iotaledger/twin-telemetry/commit/1cea68c858deea67ab3364c93db56928e0064ec3))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/telemetry-models bumped from 0.0.3-next.12 to 0.0.3-next.13
16
+
17
+ ## [0.0.3-next.12](https://github.com/iotaledger/twin-telemetry/compare/telemetry-service-v0.0.3-next.11...telemetry-service-v0.0.3-next.12) (2026-06-08)
18
+
19
+
20
+ ### Miscellaneous Chores
21
+
22
+ * **telemetry-service:** Synchronize repo versions
23
+
24
+
25
+ ### Dependencies
26
+
27
+ * The following workspace dependencies were updated
28
+ * dependencies
29
+ * @twin.org/telemetry-models bumped from 0.0.3-next.11 to 0.0.3-next.12
30
+
3
31
  ## [0.0.3-next.11](https://github.com/iotaledger/twin-telemetry/compare/telemetry-service-v0.0.3-next.10...telemetry-service-v0.0.3-next.11) (2026-06-04)
4
32
 
5
33
 
@@ -8,26 +8,20 @@ The options for the metrics collector service constructor.
8
8
 
9
9
  > `optional` **loggingComponentType?**: `string`
10
10
 
11
- The type of the logging component to use.
12
-
13
- #### Default
14
-
15
- ```ts
16
- logging
17
- ```
11
+ The type of the logging component to use, can be undefined for no logging.
18
12
 
19
13
  ***
20
14
 
21
- ### tenantComponentType? {#tenantcomponenttype}
15
+ ### platformComponentType? {#platformcomponenttype}
22
16
 
23
- > `optional` **tenantComponentType?**: `string`
17
+ > `optional` **platformComponentType?**: `string`
24
18
 
25
- The type of the tenant component to use for partitioned producers.
19
+ The type of the platform component to use for partitioned producers.
26
20
 
27
21
  #### Default
28
22
 
29
23
  ```ts
30
- tenant
24
+ platform
31
25
  ```
32
26
 
33
27
  ***
@@ -18,14 +18,14 @@ telemetry
18
18
 
19
19
  ***
20
20
 
21
- ### tenantComponentType? {#tenantcomponenttype}
21
+ ### platformComponentType? {#platformcomponenttype}
22
22
 
23
- > `optional` **tenantComponentType?**: `string`
23
+ > `optional` **platformComponentType?**: `string`
24
24
 
25
- The type of the tenant component to use for partition iteration.
25
+ The type of the platform component to use for partition iteration.
26
26
 
27
27
  #### Default
28
28
 
29
29
  ```ts
30
- tenant
30
+ platform
31
31
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/telemetry-service",
3
- "version": "0.0.3-next.11",
3
+ "version": "0.0.3-next.13",
4
4
  "description": "Telemetry service implementation with REST entry points.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,7 +20,7 @@
20
20
  "@twin.org/entity": "next",
21
21
  "@twin.org/logging-models": "next",
22
22
  "@twin.org/nameof": "next",
23
- "@twin.org/telemetry-models": "0.0.3-next.11",
23
+ "@twin.org/telemetry-models": "0.0.3-next.13",
24
24
  "@twin.org/web": "next"
25
25
  },
26
26
  "main": "./dist/es/index.js",