@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.
- package/CHANGELOG.md +14 -0
- package/README.md +163 -0
- package/index.ts +2 -0
- package/lib/console.ts +15 -0
- package/lib/grpc.ts +4 -4
- package/lib/http.ts +4 -4
- package/lib/index.ts +2 -2
- package/lib/instrumentation.node.ts +9 -1
- package/lib/metrics.ts +70 -0
- package/lib/options.ts +2 -3
- package/package.json +22 -22
- package/test/metrics.test.ts +142 -0
- package/test/node-config.test.ts +35 -9
- package/test/validation.test.ts +31 -0
- package/tsconfig.json +2 -1
- package/coverage/cobertura-coverage.xml +0 -199
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -131
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sdk-node/index.html +0 -116
- package/coverage/lcov-report/sdk-node/index.ts.html +0 -106
- package/coverage/lcov-report/sdk-node/lib/grpc.ts.html +0 -178
- package/coverage/lcov-report/sdk-node/lib/http.ts.html +0 -190
- package/coverage/lcov-report/sdk-node/lib/index.html +0 -191
- package/coverage/lcov-report/sdk-node/lib/index.ts.html +0 -265
- package/coverage/lcov-report/sdk-node/lib/instrumentation.node.ts.html +0 -310
- package/coverage/lcov-report/sdk-node/lib/options.ts.html +0 -109
- package/coverage/lcov-report/sdk-node/lib/utils.ts.html +0 -115
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov.info +0 -206
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -2
- package/dist/lib/grpc.d.ts +0 -3
- package/dist/lib/grpc.js +0 -26
- package/dist/lib/http.d.ts +0 -3
- package/dist/lib/http.js +0 -29
- package/dist/lib/index.d.ts +0 -46
- package/dist/lib/index.js +0 -1
- package/dist/lib/instrumentation.node.d.ts +0 -3
- package/dist/lib/instrumentation.node.js +0 -53
- package/dist/lib/options.d.ts +0 -7
- package/dist/lib/options.js +0 -1
- package/dist/lib/utils.d.ts +0 -3
- package/dist/lib/utils.js +0 -5
- package/dist/vitest.config.d.ts +0 -2
- package/dist/vitest.config.js +0 -25
- 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
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 {
|
|
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
|
-
*
|
|
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 {
|
|
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.
|
|
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.
|
|
28
|
-
"@opentelemetry/core": "1.
|
|
29
|
-
"@opentelemetry/exporter-logs-otlp-grpc": "^0.
|
|
30
|
-
"@opentelemetry/exporter-logs-otlp-http": "^0.
|
|
31
|
-
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.
|
|
32
|
-
"@opentelemetry/exporter-metrics-otlp-http": "^0.
|
|
33
|
-
"@opentelemetry/exporter-trace-otlp-grpc": "^0.
|
|
34
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.
|
|
35
|
-
"@opentelemetry/instrumentation": "^0.
|
|
36
|
-
"@opentelemetry/otlp-exporter-base": "^0.
|
|
37
|
-
"@opentelemetry/sdk-metrics": "^1.
|
|
38
|
-
"@opentelemetry/sdk-node": "^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.
|
|
44
|
-
"@vitest/coverage-v8": "^
|
|
39
|
+
"@types/node": "^22.12.0",
|
|
40
|
+
"@vitest/coverage-v8": "^3.0.4",
|
|
45
41
|
"tsx": "^4.19.2",
|
|
46
|
-
"typescript": "^5.7.
|
|
47
|
-
"vitest": "^
|
|
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
|
+
});
|
package/test/node-config.test.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|