@ogcio/o11y-sdk-node 0.1.0-beta.1 → 0.1.0-beta.3

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +163 -0
  3. package/index.ts +2 -0
  4. package/lib/console.ts +15 -0
  5. package/lib/grpc.ts +4 -4
  6. package/lib/http.ts +4 -4
  7. package/lib/index.ts +2 -2
  8. package/lib/instrumentation.node.ts +9 -1
  9. package/lib/metrics.ts +70 -0
  10. package/lib/options.ts +2 -3
  11. package/package.json +22 -22
  12. package/test/metrics.test.ts +142 -0
  13. package/test/node-config.test.ts +35 -9
  14. package/test/validation.test.ts +31 -0
  15. package/tsconfig.json +2 -1
  16. package/coverage/cobertura-coverage.xml +0 -199
  17. package/coverage/lcov-report/base.css +0 -224
  18. package/coverage/lcov-report/block-navigation.js +0 -87
  19. package/coverage/lcov-report/favicon.png +0 -0
  20. package/coverage/lcov-report/index.html +0 -131
  21. package/coverage/lcov-report/prettify.css +0 -1
  22. package/coverage/lcov-report/prettify.js +0 -2
  23. package/coverage/lcov-report/sdk-node/index.html +0 -116
  24. package/coverage/lcov-report/sdk-node/index.ts.html +0 -106
  25. package/coverage/lcov-report/sdk-node/lib/grpc.ts.html +0 -178
  26. package/coverage/lcov-report/sdk-node/lib/http.ts.html +0 -190
  27. package/coverage/lcov-report/sdk-node/lib/index.html +0 -191
  28. package/coverage/lcov-report/sdk-node/lib/index.ts.html +0 -265
  29. package/coverage/lcov-report/sdk-node/lib/instrumentation.node.ts.html +0 -310
  30. package/coverage/lcov-report/sdk-node/lib/options.ts.html +0 -109
  31. package/coverage/lcov-report/sdk-node/lib/utils.ts.html +0 -115
  32. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  33. package/coverage/lcov-report/sorter.js +0 -196
  34. package/coverage/lcov.info +0 -206
  35. package/dist/index.d.ts +0 -6
  36. package/dist/index.js +0 -2
  37. package/dist/lib/grpc.d.ts +0 -3
  38. package/dist/lib/grpc.js +0 -26
  39. package/dist/lib/http.d.ts +0 -3
  40. package/dist/lib/http.js +0 -29
  41. package/dist/lib/index.d.ts +0 -46
  42. package/dist/lib/index.js +0 -1
  43. package/dist/lib/instrumentation.node.d.ts +0 -3
  44. package/dist/lib/instrumentation.node.js +0 -53
  45. package/dist/lib/options.d.ts +0 -7
  46. package/dist/lib/options.js +0 -1
  47. package/dist/lib/utils.d.ts +0 -3
  48. package/dist/lib/utils.js +0 -5
  49. package/dist/vitest.config.d.ts +0 -2
  50. package/dist/vitest.config.js +0 -25
  51. package/test-report.xml +0 -39
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.0-beta.3](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@0.1.0-beta.2...@ogcio/o11y-sdk-node@v0.1.0-beta.3) (2025-01-28)
4
+
5
+
6
+ ### Features
7
+
8
+ * **sdk-node:** custom metrics ([#53](https://github.com/ogcio/o11y/issues/53)) ([3cb40b1](https://github.com/ogcio/o11y/commit/3cb40b1add3d80615c8d123d233724094559c7ff))
9
+
10
+ ## [0.1.0-beta.2](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@0.1.0-beta.1...@ogcio/o11y-sdk-node@0.1.0-beta.2) (2024-11-28)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * Add readme file inside submodule ([#27](https://github.com/ogcio/o11y/issues/27)) ([dc518d5](https://github.com/ogcio/o11y/commit/dc518d5dde573368443bc0f8619e80e331880c78))
16
+
3
17
  ## [0.1.0-beta.1](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@0.0.1-beta.1...@ogcio/o11y-sdk-node@0.1.0-beta.1) (2024-11-27)
4
18
 
5
19
 
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # Observability NodeJS SDK
2
+
3
+ The NodeJS observability sdk is a npm package used to setup and implement opentelemetry instrumentation.
4
+
5
+ ## Installation
6
+
7
+ pnpm
8
+
9
+ ```bash
10
+ pnpm i --save @ogcio/o11y-sdk-node
11
+ ```
12
+
13
+ npm
14
+
15
+ ```bash
16
+ npm i @ogcio/o11y-sdk-node
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Setup using constructor function
22
+
23
+ ```javascript
24
+ // instrumentation.ts
25
+
26
+ import("@ogcio/o11y-sdk-node/lib/index").then((sdk) =>
27
+ sdk.instrumentNode({
28
+ serviceName: "node-microservice",
29
+ collectorUrl: "http://localhost:4317",
30
+ }),
31
+ );
32
+ ```
33
+
34
+ Run your node script with instrumentation
35
+
36
+ `node --import instrumentation.js server.js`
37
+
38
+ Or setup inside your package.json
39
+
40
+ ```json
41
+ {
42
+ "main": "dist/index.js",
43
+ "type": "module",
44
+ "scripts": {
45
+ "start": "node --env-file=.env --import ./dist/instrumentation.js dist/index.js"
46
+ }
47
+ }
48
+ ```
49
+
50
+ ## Sending Custom Metrics
51
+
52
+ This package gives the possibility to send custom metrics and define them as desired in the code, you can choose between sync metrics and observable async metrics.
53
+
54
+ To use this functionality, you only need to import `getMetric` and enable the application instrumentation.
55
+
56
+ ```typescript
57
+ import { getMetric } from "@ogcio/o11y-sdk-node";
58
+ ```
59
+
60
+ ### Sync
61
+
62
+ Sync metrics are signals sent when the function has been called.
63
+
64
+ Creating a counter, there are 2 types of counter:
65
+
66
+ - **counter** a simple counter that can only add positive numbers
67
+ - **updowncounter** counter that support also negative numbers
68
+
69
+ ```typescript
70
+ const counter = getMetric("counter", {
71
+ attributeName: "counter",
72
+ metricName: "fastify-counter",
73
+ });
74
+
75
+ counter.add(1, {
76
+ my: "my",
77
+ custom: "custom",
78
+ attributes: "attributes",
79
+ });
80
+ ```
81
+
82
+ Creating a Histogram
83
+
84
+ ```typescript
85
+ const histogram = getMetric("histogram", {
86
+ metricName: "response_duration",
87
+ attributeName: "http_response",
88
+ options: {
89
+ advice: {
90
+ explicitBucketBoundaries: [0, 100, 200, 500, 1000],
91
+ },
92
+ description: "Response durations",
93
+ },
94
+ });
95
+
96
+ histogram.record(120, { path: "/home" });
97
+ ```
98
+
99
+ ### Async
100
+
101
+ Async metrics are called by the scraper collector to read current data using the `Observable` pattern.
102
+ Creating an async metric means that the application will subscribe to the observability URL and record data on call (default 60s).
103
+
104
+ _keep in mind, you can't send signals on-demand with this component_
105
+
106
+ Creating an async Gauge
107
+
108
+ ```typescript
109
+ const asyncGauge = getMetric("async-gauge", {
110
+ metricName: "cpu_usage",
111
+ attributeName: "server_load",
112
+ options: { unit: "percentage" },
113
+ }).addCallback((observer) => {
114
+ observer.observe(50, { host: "server1" });
115
+ });
116
+ ```
117
+
118
+ Creating an async Counter
119
+
120
+ ```typescript
121
+ getMetric("async-counter", {
122
+ attributeName: "scraped-memory",
123
+ metricName: "fastify-counter",
124
+ }).addCallback((observer) => {
125
+ observer.observe(freemem(), {
126
+ "application.os.memory": "free-memory",
127
+ });
128
+ });
129
+ ```
130
+
131
+ ## API Reference
132
+
133
+ #### Protocol
134
+
135
+ protocol is a string parameter used to define how to send signals to observability infrastructure
136
+
137
+ - **grpc** Use gRPC protocol, usually default port use 4317. Is the most performant option for server side applications.
138
+ - **http** Use HTTP standard protocol, usually default port use 4318. Mainly used on web or UI client applications.
139
+ - **console** Used for debugging sending signals to observability cluster, every information will be printed to your runtime console.
140
+
141
+ #### Shared Types
142
+
143
+ ```ts
144
+ export type SDKLogLevel =
145
+ | "NONE"
146
+ | "ERROR"
147
+ | "WARN"
148
+ | "INFO"
149
+ | "DEBUG"
150
+ | "VERBOSE"
151
+ | "ALL";
152
+ ```
153
+
154
+ ### Config
155
+
156
+ | Parameter | Type | Description |
157
+ | :-------------- | :---------------- | :------------------------------------------------------------------------------------------------------- |
158
+ | `collectorUrl` | `string` | **Required**. The opentelemetry collector entrypoint url, if null, instrumentation will not be activated |
159
+ | `serviceName` | `string` | Name of your application used for the collector to group logs |
160
+ | `diagLogLevel` | `SDKLogLevel` | Diagnostic log level for the internal runtime instrumentation |
161
+ | `collectorMode` | `single \| batch` | Signals sending mode, default is batch for performance |
162
+ | `enableFS` | `boolean` | Flag to enable or disable the tracing for node:fs module |
163
+ | `protocol` | `string` | Type of the protocol used to send signals |
package/index.ts CHANGED
@@ -5,3 +5,5 @@ import buildNodeInstrumentation from "./lib/instrumentation.node.js";
5
5
  export type * from "./lib/index.js";
6
6
  export type { NodeSDKConfig, NodeSDK };
7
7
  export { buildNodeInstrumentation as instrumentNode };
8
+
9
+ export * from "./lib/metrics.js";
package/lib/console.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { NodeSDKConfig } from "./index.js";
2
+ import type { Exporters } from "./options.js";
3
+ import { logs, metrics, tracing } from "@opentelemetry/sdk-node";
4
+
5
+ export default function buildConsoleExporters(_: NodeSDKConfig): Exporters {
6
+ return {
7
+ traces: new tracing.ConsoleSpanExporter(),
8
+ metrics: new metrics.PeriodicExportingMetricReader({
9
+ exporter: new metrics.ConsoleMetricExporter(),
10
+ }),
11
+ logs: [
12
+ new logs.SimpleLogRecordProcessor(new logs.ConsoleLogRecordExporter()),
13
+ ],
14
+ };
15
+ }
package/lib/grpc.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import type { NodeSDKConfig } from "./index.js";
2
2
  import type { Exporters } from "./options.js";
3
3
  import { LogRecordProcessorMap } from "./utils.js";
4
+ import { metrics } from "@opentelemetry/sdk-node";
4
5
  import { CompressionAlgorithm } from "@opentelemetry/otlp-exporter-base";
5
- import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
6
- import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-grpc";
7
- import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-grpc";
8
6
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";
7
+ import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-grpc";
8
+ import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-grpc";
9
9
 
10
10
  export default function buildGrpcExporters(config: NodeSDKConfig): Exporters {
11
11
  return {
@@ -13,7 +13,7 @@ export default function buildGrpcExporters(config: NodeSDKConfig): Exporters {
13
13
  url: `${config.collectorUrl}`,
14
14
  compression: CompressionAlgorithm.GZIP,
15
15
  }),
16
- metrics: new PeriodicExportingMetricReader({
16
+ metrics: new metrics.PeriodicExportingMetricReader({
17
17
  exporter: new OTLPMetricExporter({
18
18
  url: `${config.collectorUrl}`,
19
19
  compression: CompressionAlgorithm.GZIP,
package/lib/http.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
2
1
  import type { NodeSDKConfig } from "./index.js";
3
2
  import type { Exporters } from "./options.js";
4
3
  import { LogRecordProcessorMap } from "./utils.js";
4
+ import { metrics } from "@opentelemetry/sdk-node";
5
5
  import { CompressionAlgorithm } from "@opentelemetry/otlp-exporter-base";
6
- import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
7
- import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
6
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
8
7
  import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
8
+ import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
9
9
 
10
10
  export default function buildHttpExporters(config: NodeSDKConfig): Exporters {
11
11
  if (config.collectorUrl.endsWith("/")) {
@@ -17,7 +17,7 @@ export default function buildHttpExporters(config: NodeSDKConfig): Exporters {
17
17
  url: `${config.collectorUrl}/v1/traces`,
18
18
  compression: CompressionAlgorithm.GZIP,
19
19
  }),
20
- metrics: new PeriodicExportingMetricReader({
20
+ metrics: new metrics.PeriodicExportingMetricReader({
21
21
  exporter: new OTLPMetricExporter({
22
22
  url: `${config.collectorUrl}/v1/metrics`,
23
23
  compression: CompressionAlgorithm.GZIP,
package/lib/index.ts CHANGED
@@ -36,7 +36,7 @@ export interface NodeSDKConfig extends SDKConfig {
36
36
  enableFS?: boolean;
37
37
 
38
38
  /**
39
- * http based connection protocol used to send signals.
39
+ * protocol used to send signals.
40
40
  *
41
41
  * @default grpc
42
42
  */
@@ -45,7 +45,7 @@ export interface NodeSDKConfig extends SDKConfig {
45
45
 
46
46
  export type SDKCollectorMode = "single" | "batch";
47
47
 
48
- export type SDKProtocol = "grpc" | "http";
48
+ export type SDKProtocol = "grpc" | "http" | "console";
49
49
 
50
50
  export type SDKLogLevel =
51
51
  | "NONE"
@@ -1,12 +1,14 @@
1
- import { NodeSDK } from "@opentelemetry/sdk-node";
1
+ import { NodeSDK, resources } from "@opentelemetry/sdk-node";
2
2
  import type { NodeSDKConfig } from "./index.js";
3
3
  import type { Exporters } from "./options.js";
4
4
  import isUrl from "is-url";
5
5
  import buildHttpExporters from "./http.js";
6
6
  import buildGrpcExporters from "./grpc.js";
7
+ import buildConsoleExporters from "./console.js";
7
8
  import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
8
9
  import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
9
10
  import { W3CTraceContextPropagator } from "@opentelemetry/core";
11
+ import packageJson from "../package.json" with { type: "json" };
10
12
 
11
13
  export default function buildNodeInstrumentation(
12
14
  config?: NodeSDKConfig,
@@ -36,6 +38,8 @@ export default function buildNodeInstrumentation(
36
38
 
37
39
  if (config.protocol === "http") {
38
40
  exporter = buildHttpExporters(config);
41
+ } else if (config.protocol === "console") {
42
+ exporter = buildConsoleExporters(config);
39
43
  } else {
40
44
  exporter = buildGrpcExporters(config);
41
45
  }
@@ -49,6 +53,10 @@ export default function buildNodeInstrumentation(
49
53
  );
50
54
 
51
55
  const sdk = new NodeSDK({
56
+ resource: new resources.Resource({
57
+ "o11y.sdk.name": packageJson.name,
58
+ "o11y.sdk.version": packageJson.version,
59
+ }),
52
60
  serviceName: config.serviceName,
53
61
  traceExporter: exporter.traces,
54
62
  metricReader: exporter.metrics,
package/lib/metrics.ts ADDED
@@ -0,0 +1,70 @@
1
+ import {
2
+ Counter,
3
+ createNoopMeter,
4
+ Gauge,
5
+ Histogram,
6
+ Meter,
7
+ MetricOptions,
8
+ metrics,
9
+ ObservableCounter,
10
+ ObservableGauge,
11
+ ObservableUpDownCounter,
12
+ UpDownCounter,
13
+ } from "@opentelemetry/api";
14
+
15
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
+ declare const MetricsMap: {
17
+ gauge: Gauge;
18
+ histogram: Histogram;
19
+ counter: Counter;
20
+ updowncounter: UpDownCounter;
21
+ "async-counter": ObservableCounter;
22
+ "async-updowncounter": ObservableUpDownCounter;
23
+ "async-gauge": ObservableGauge;
24
+ };
25
+
26
+ type MetricType = keyof typeof MetricsMap;
27
+
28
+ const MetricsFactoryMap: {
29
+ [K in MetricType]: (
30
+ meter: Meter,
31
+ ) => (name: string, options?: MetricOptions) => (typeof MetricsMap)[K];
32
+ } = {
33
+ gauge: (meter: Meter) => meter.createGauge,
34
+ histogram: (meter: Meter) => meter.createHistogram,
35
+ counter: (meter: Meter) => meter.createCounter,
36
+ updowncounter: (meter: Meter) => meter.createUpDownCounter,
37
+ "async-counter": (meter: Meter) => meter.createObservableCounter,
38
+ "async-updowncounter": (meter: Meter) => meter.createObservableUpDownCounter,
39
+ "async-gauge": (meter: Meter) => meter.createObservableGauge,
40
+ };
41
+
42
+ export interface MetricsParams {
43
+ metricName: string;
44
+ attributeName: string;
45
+ options?: MetricOptions;
46
+ }
47
+
48
+ function getMeter({ metricName, attributeName }: MetricsParams) {
49
+ let meter: Meter;
50
+ if (!metricName || !attributeName) {
51
+ console.error("Invaid metric configuration!");
52
+ meter = createNoopMeter();
53
+ } else {
54
+ meter = metrics.getMeter(`custom_metric.${metricName}`);
55
+ }
56
+ return meter;
57
+ }
58
+
59
+ export function getMetric<T extends MetricType>(
60
+ type: T,
61
+ p: MetricsParams,
62
+ ): (typeof MetricsMap)[T] {
63
+ const meter = getMeter(p);
64
+
65
+ if (!MetricsFactoryMap[type]) {
66
+ throw new Error(`Unsupported metric type: ${type}`);
67
+ }
68
+
69
+ return MetricsFactoryMap[type](meter).bind(meter)(p.attributeName, p.options);
70
+ }
package/lib/options.ts CHANGED
@@ -1,8 +1,7 @@
1
- import type { MetricReader } from "@opentelemetry/sdk-metrics";
2
- import type { logs, tracing } from "@opentelemetry/sdk-node";
1
+ import type { logs, tracing, metrics } from "@opentelemetry/sdk-node";
3
2
 
4
3
  export type Exporters = {
5
4
  traces: tracing.SpanExporter;
6
- metrics: MetricReader;
5
+ metrics: metrics.MetricReader;
7
6
  logs: logs.LogRecordProcessor[];
8
7
  };
package/package.json CHANGED
@@ -1,13 +1,9 @@
1
1
  {
2
2
  "name": "@ogcio/o11y-sdk-node",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.3",
4
4
  "description": "Opentelemetry standard instrumentation SDK for NodeJS based project",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
7
- "scripts": {
8
- "build": "rm -rf dist && tsc -p tsconfig.json",
9
- "test": "vitest"
10
- },
11
7
  "exports": {
12
8
  ".": "./dist/index.js",
13
9
  "./*": "./dist/*.js"
@@ -24,26 +20,30 @@
24
20
  "license": "ISC",
25
21
  "dependencies": {
26
22
  "@opentelemetry/api": "^1.9.0",
27
- "@opentelemetry/auto-instrumentations-node": "^0.53.0",
28
- "@opentelemetry/core": "1.28.0",
29
- "@opentelemetry/exporter-logs-otlp-grpc": "^0.55.0",
30
- "@opentelemetry/exporter-logs-otlp-http": "^0.55.0",
31
- "@opentelemetry/exporter-metrics-otlp-grpc": "^0.55.0",
32
- "@opentelemetry/exporter-metrics-otlp-http": "^0.55.0",
33
- "@opentelemetry/exporter-trace-otlp-grpc": "^0.55.0",
34
- "@opentelemetry/exporter-trace-otlp-http": "^0.55.0",
35
- "@opentelemetry/instrumentation": "^0.55.0",
36
- "@opentelemetry/otlp-exporter-base": "^0.55.0",
37
- "@opentelemetry/sdk-metrics": "^1.28.0",
38
- "@opentelemetry/sdk-node": "^0.55.0",
23
+ "@opentelemetry/auto-instrumentations-node": "^0.55.3",
24
+ "@opentelemetry/core": "1.30.1",
25
+ "@opentelemetry/exporter-logs-otlp-grpc": "^0.57.1",
26
+ "@opentelemetry/exporter-logs-otlp-http": "^0.57.1",
27
+ "@opentelemetry/exporter-metrics-otlp-grpc": "^0.57.1",
28
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.57.1",
29
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.57.1",
30
+ "@opentelemetry/exporter-trace-otlp-http": "^0.57.1",
31
+ "@opentelemetry/instrumentation": "^0.57.1",
32
+ "@opentelemetry/otlp-exporter-base": "^0.57.1",
33
+ "@opentelemetry/sdk-metrics": "^1.30.1",
34
+ "@opentelemetry/sdk-node": "^0.57.1",
39
35
  "is-url": "^1.2.4"
40
36
  },
41
37
  "devDependencies": {
42
38
  "@types/is-url": "^1.2.32",
43
- "@types/node": "^22.10.0",
44
- "@vitest/coverage-v8": "^2.1.6",
39
+ "@types/node": "^22.12.0",
40
+ "@vitest/coverage-v8": "^3.0.4",
45
41
  "tsx": "^4.19.2",
46
- "typescript": "^5.7.2",
47
- "vitest": "^2.1.6"
42
+ "typescript": "^5.7.3",
43
+ "vitest": "^3.0.4"
44
+ },
45
+ "scripts": {
46
+ "build": "rm -rf dist && tsc -p tsconfig.json",
47
+ "test": "vitest"
48
48
  }
49
- }
49
+ }
@@ -0,0 +1,142 @@
1
+ import { describe, test, expect, vi, beforeEach, assert } from "vitest";
2
+ import { getMetric, MetricsParams } from "../lib/metrics";
3
+
4
+ const mockMeter = {
5
+ createGauge: vi.fn(),
6
+ createHistogram: vi.fn(),
7
+ createCounter: vi.fn(),
8
+ createUpDownCounter: vi.fn(),
9
+ createObservableCounter: vi.fn(),
10
+ createObservableUpDownCounter: vi.fn(),
11
+ createObservableGauge: vi.fn(),
12
+ };
13
+
14
+ vi.mock("@opentelemetry/api", async () => {
15
+ const { createNoopMeter } = await import("@opentelemetry/api");
16
+
17
+ return {
18
+ metrics: {
19
+ getMeter: vi.fn(() => mockMeter),
20
+ },
21
+ createNoopMeter: createNoopMeter,
22
+ };
23
+ });
24
+
25
+ describe("MetricsFactoryMap", () => {
26
+ beforeEach(() => {
27
+ vi.clearAllMocks();
28
+ });
29
+
30
+ const validMetricParams: MetricsParams = {
31
+ metricName: "test-metric",
32
+ attributeName: "test-attribute",
33
+ options: { description: "A test metric" },
34
+ };
35
+
36
+ test("should call createGauge when type is 'gauge'", () => {
37
+ mockMeter.createGauge.mockReturnValue("mocked-gauge");
38
+
39
+ const result = getMetric("gauge", validMetricParams);
40
+
41
+ expect(result).toBe("mocked-gauge");
42
+ expect(mockMeter.createGauge).toHaveBeenCalledWith(
43
+ validMetricParams.attributeName,
44
+ validMetricParams.options,
45
+ );
46
+ });
47
+
48
+ test("should call createHistogram when type is 'histogram'", () => {
49
+ mockMeter.createHistogram.mockReturnValue("mocked-histogram");
50
+
51
+ const result = getMetric("histogram", validMetricParams);
52
+
53
+ expect(result).toBe("mocked-histogram");
54
+ expect(mockMeter.createHistogram).toHaveBeenCalledWith(
55
+ validMetricParams.attributeName,
56
+ validMetricParams.options,
57
+ );
58
+ });
59
+
60
+ test("should call createCounter when type is 'counter'", () => {
61
+ mockMeter.createCounter.mockReturnValue("mocked-counter");
62
+
63
+ const result = getMetric("counter", validMetricParams);
64
+
65
+ expect(result).toBe("mocked-counter");
66
+ expect(mockMeter.createCounter).toHaveBeenCalledWith(
67
+ validMetricParams.attributeName,
68
+ validMetricParams.options,
69
+ );
70
+ });
71
+
72
+ test("should call createUpDownCounter when type is 'updowncounter'", () => {
73
+ mockMeter.createUpDownCounter.mockReturnValue("mocked-updowncounter");
74
+
75
+ const result = getMetric("updowncounter", validMetricParams);
76
+
77
+ expect(result).toBe("mocked-updowncounter");
78
+ expect(mockMeter.createUpDownCounter).toHaveBeenCalledWith(
79
+ validMetricParams.attributeName,
80
+ validMetricParams.options,
81
+ );
82
+ });
83
+
84
+ test("should call createObservableCounter when type is 'async-counter'", () => {
85
+ mockMeter.createObservableCounter.mockReturnValue("mocked-async-counter");
86
+
87
+ const result = getMetric("async-counter", validMetricParams);
88
+
89
+ expect(result).toBe("mocked-async-counter");
90
+ expect(mockMeter.createObservableCounter).toHaveBeenCalledWith(
91
+ validMetricParams.attributeName,
92
+ validMetricParams.options,
93
+ );
94
+ });
95
+
96
+ test("should call createObservableUpDownCounter when type is 'async-updowncounter'", () => {
97
+ mockMeter.createObservableUpDownCounter.mockReturnValue(
98
+ "mocked-async-updowncounter",
99
+ );
100
+
101
+ const result = getMetric("async-updowncounter", validMetricParams);
102
+
103
+ expect(result).toBe("mocked-async-updowncounter");
104
+ expect(mockMeter.createObservableUpDownCounter).toHaveBeenCalledWith(
105
+ validMetricParams.attributeName,
106
+ validMetricParams.options,
107
+ );
108
+ });
109
+
110
+ test("should call createObservableGauge when type is 'async-gauge'", () => {
111
+ mockMeter.createObservableGauge.mockReturnValue("mocked-async-gauge");
112
+
113
+ const result = getMetric("async-gauge", validMetricParams);
114
+
115
+ expect(result).toBe("mocked-async-gauge");
116
+ expect(mockMeter.createObservableGauge).toHaveBeenCalledWith(
117
+ validMetricParams.attributeName,
118
+ validMetricParams.options,
119
+ );
120
+ });
121
+
122
+ test("should throw an error for unsupported metric types", () => {
123
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
124
+ const invalidMetricType = "invalid-type" as any;
125
+
126
+ expect(() => getMetric(invalidMetricType, validMetricParams)).toThrow(
127
+ `Unsupported metric type: ${invalidMetricType}`,
128
+ );
129
+ });
130
+
131
+ test("should return noop metric fallback for null config", async () => {
132
+ const nullMetricParams: MetricsParams = {
133
+ metricName: null!,
134
+ attributeName: "",
135
+ };
136
+
137
+ const result = getMetric("async-gauge", nullMetricParams);
138
+
139
+ assert.isNotNull(result);
140
+ assert.equal(result.constructor.name, "NoopObservableGaugeMetric");
141
+ });
142
+ });
@@ -1,6 +1,6 @@
1
1
  import { test, describe, assert, expect } from "vitest";
2
2
  import buildNodeInstrumentation from "../lib/instrumentation.node.js";
3
- import { NodeSDK, logs } from "@opentelemetry/sdk-node";
3
+ import { NodeSDK, logs, metrics, tracing } from "@opentelemetry/sdk-node";
4
4
  import { OTLPTraceExporter as GRPC_OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";
5
5
  import { OTLPMetricExporter as GRPC_OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-grpc";
6
6
  import { OTLPLogExporter as GRPC_OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-grpc";
@@ -32,10 +32,7 @@ describe("verify config settings", () => {
32
32
  assert.equal(_configuration.serviceName, commonConfig.serviceName);
33
33
 
34
34
  const traceExporter = _configuration.traceExporter;
35
- assert.equal(
36
- traceExporter["_transport"]["_parameters"]["compression"],
37
- "gzip",
38
- );
35
+
39
36
  assert.ok(traceExporter instanceof GRPC_OTLPTraceExporter);
40
37
 
41
38
  const logRecordProcessors = _configuration.logRecordProcessors;
@@ -64,10 +61,6 @@ describe("verify config settings", () => {
64
61
  assert.equal(_configuration.serviceName, commonConfig.serviceName);
65
62
 
66
63
  const traceExporter = _configuration.traceExporter;
67
- assert.equal(
68
- traceExporter._transport._transport._parameters.compression,
69
- "gzip",
70
- );
71
64
  assert.ok(traceExporter instanceof HTTP_OTLPTraceExporter);
72
65
 
73
66
  const logRecordProcessors = _configuration.logRecordProcessors;
@@ -82,6 +75,39 @@ describe("verify config settings", () => {
82
75
  assert.ok(metricReader._exporter instanceof HTTP_OTLPMetricExporter);
83
76
  });
84
77
 
78
+ test("console - console config", () => {
79
+ const config: NodeSDKConfig = {
80
+ ...commonConfig,
81
+ protocol: "console",
82
+ diagLogLevel: "NONE",
83
+ };
84
+
85
+ const sdk: NodeSDK | undefined = buildNodeInstrumentation(config);
86
+ assert.ok(sdk);
87
+
88
+ const _configuration = sdk["_configuration"];
89
+ assert.equal(_configuration.serviceName, commonConfig.serviceName);
90
+
91
+ const traceExporter = _configuration.traceExporter;
92
+
93
+ assert.ok(traceExporter instanceof tracing.ConsoleSpanExporter);
94
+ assert.isUndefined(traceExporter._transport);
95
+
96
+ const logRecordProcessors = _configuration.logRecordProcessors;
97
+ assert.equal(logRecordProcessors.length, 1);
98
+
99
+ assert.ok(logRecordProcessors[0] instanceof logs.SimpleLogRecordProcessor);
100
+ assert.ok(
101
+ logRecordProcessors[0]["_exporter"] instanceof
102
+ logs.ConsoleLogRecordExporter,
103
+ );
104
+ assert.isUndefined(logRecordProcessors[0]["_exporter"]._transport);
105
+
106
+ const metricReader = _configuration.metricReader;
107
+ assert.ok(metricReader._exporter instanceof metrics.ConsoleMetricExporter);
108
+ assert.isUndefined(metricReader._exporter._transport);
109
+ });
110
+
85
111
  test("single log sending config", () => {
86
112
  const config: NodeSDKConfig = {
87
113
  ...commonConfig,