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

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 (83) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +222 -0
  3. package/dist/index.d.ts +3 -2
  4. package/dist/index.js +2 -0
  5. package/dist/lib/exporter/console.d.ts +3 -0
  6. package/dist/lib/exporter/console.js +20 -0
  7. package/dist/lib/exporter/grpc.d.ts +3 -0
  8. package/dist/lib/{grpc.js → exporter/grpc.js} +15 -9
  9. package/dist/lib/exporter/http.d.ts +3 -0
  10. package/dist/lib/{http.js → exporter/http.js} +15 -9
  11. package/dist/lib/exporter/index.d.ts +8 -0
  12. package/dist/lib/index.d.ts +29 -6
  13. package/dist/lib/instrumentation.node.js +32 -5
  14. package/dist/lib/metrics.d.ts +18 -0
  15. package/dist/lib/metrics.js +24 -0
  16. package/dist/lib/processor/enrich-logger-processor.d.ts +10 -0
  17. package/dist/lib/processor/enrich-logger-processor.js +19 -0
  18. package/dist/lib/processor/enrich-span-processor.d.ts +11 -0
  19. package/dist/lib/processor/enrich-span-processor.js +22 -0
  20. package/dist/lib/resource.d.ts +7 -0
  21. package/dist/lib/resource.js +18 -0
  22. package/dist/lib/traces.d.ts +1 -0
  23. package/dist/lib/traces.js +4 -0
  24. package/dist/lib/url-sampler.d.ts +10 -0
  25. package/dist/lib/url-sampler.js +25 -0
  26. package/dist/lib/utils.d.ts +4 -2
  27. package/dist/lib/utils.js +8 -3
  28. package/dist/package.json +57 -0
  29. package/dist/vitest.config.js +15 -1
  30. package/index.ts +4 -2
  31. package/lib/exporter/console.ts +31 -0
  32. package/lib/{grpc.ts → exporter/grpc.ts} +19 -11
  33. package/lib/{http.ts → exporter/http.ts} +19 -11
  34. package/lib/exporter/index.ts +9 -0
  35. package/lib/index.ts +37 -5
  36. package/lib/instrumentation.node.ts +42 -7
  37. package/lib/metrics.ts +75 -0
  38. package/lib/processor/enrich-logger-processor.ts +34 -0
  39. package/lib/processor/enrich-span-processor.ts +39 -0
  40. package/lib/resource.ts +30 -0
  41. package/lib/traces.ts +5 -0
  42. package/lib/url-sampler.ts +52 -0
  43. package/lib/utils.ts +16 -4
  44. package/package.json +32 -25
  45. package/test/index.test.ts +9 -0
  46. package/test/integration/README.md +26 -0
  47. package/test/integration/integration.test.ts +58 -0
  48. package/test/integration/run.sh +88 -0
  49. package/test/metrics.test.ts +142 -0
  50. package/test/node-config.test.ts +70 -31
  51. package/test/processor/enrich-logger-processor.test.ts +58 -0
  52. package/test/processor/enrich-span-processor.test.ts +104 -0
  53. package/test/resource.test.ts +33 -0
  54. package/test/url-sampler.test.ts +215 -0
  55. package/test/utils/alloy-log-parser.ts +46 -0
  56. package/test/validation.test.ts +31 -0
  57. package/tsconfig.json +2 -1
  58. package/vitest.config.ts +15 -1
  59. package/coverage/cobertura-coverage.xml +0 -199
  60. package/coverage/lcov-report/base.css +0 -224
  61. package/coverage/lcov-report/block-navigation.js +0 -87
  62. package/coverage/lcov-report/favicon.png +0 -0
  63. package/coverage/lcov-report/index.html +0 -131
  64. package/coverage/lcov-report/prettify.css +0 -1
  65. package/coverage/lcov-report/prettify.js +0 -2
  66. package/coverage/lcov-report/sdk-node/index.html +0 -116
  67. package/coverage/lcov-report/sdk-node/index.ts.html +0 -106
  68. package/coverage/lcov-report/sdk-node/lib/grpc.ts.html +0 -178
  69. package/coverage/lcov-report/sdk-node/lib/http.ts.html +0 -190
  70. package/coverage/lcov-report/sdk-node/lib/index.html +0 -191
  71. package/coverage/lcov-report/sdk-node/lib/index.ts.html +0 -265
  72. package/coverage/lcov-report/sdk-node/lib/instrumentation.node.ts.html +0 -310
  73. package/coverage/lcov-report/sdk-node/lib/options.ts.html +0 -109
  74. package/coverage/lcov-report/sdk-node/lib/utils.ts.html +0 -115
  75. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  76. package/coverage/lcov-report/sorter.js +0 -196
  77. package/coverage/lcov.info +0 -206
  78. package/dist/lib/grpc.d.ts +0 -3
  79. package/dist/lib/http.d.ts +0 -3
  80. package/dist/lib/options.d.ts +0 -7
  81. package/lib/options.ts +0 -8
  82. package/test-report.xml +0 -39
  83. /package/dist/lib/{options.js → exporter/index.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,79 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.0-beta.10](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@v0.1.0-beta.9...@ogcio/o11y-sdk-node@v0.1.0-beta.10) (2025-04-09)
4
+
5
+
6
+ ### Features
7
+
8
+ * april depedency upgrade AB[#27200](https://github.com/ogcio/o11y/issues/27200) ([#116](https://github.com/ogcio/o11y/issues/116)) ([d792fe5](https://github.com/ogcio/o11y/commit/d792fe5a783b0b495912b5bef2babfe11ef5e01d))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * default attributes in resource ([#112](https://github.com/ogcio/o11y/issues/112)) ([f0f0b9d](https://github.com/ogcio/o11y/commit/f0f0b9d555ef321d31f7171e25dc4b8a5b044522))
14
+
15
+ ## [0.1.0-beta.9](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@v0.1.0-beta.8...@ogcio/o11y-sdk-node@v0.1.0-beta.9) (2025-03-24)
16
+
17
+
18
+ ### Features
19
+
20
+ * add custom log processor for span enrich ([#102](https://github.com/ogcio/o11y/issues/102)) ([bbf8334](https://github.com/ogcio/o11y/commit/bbf83340940ed651dff63bbe7aaa52881d1e8c8c))
21
+ * upgrade to opentelemetry 2 AB[#25863](https://github.com/ogcio/o11y/issues/25863) ([#106](https://github.com/ogcio/o11y/issues/106)) ([3ff0314](https://github.com/ogcio/o11y/commit/3ff0314fef9f4d7b5db76da3b94e9035801384c7))
22
+
23
+ ## [0.1.0-beta.8](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@v0.1.0-beta.7...@ogcio/o11y-sdk-node@v0.1.0-beta.8) (2025-03-07)
24
+
25
+
26
+ ### Features
27
+
28
+ * o11y showcase AB[#25895](https://github.com/ogcio/o11y/issues/25895) ([#84](https://github.com/ogcio/o11y/issues/84)) ([f8f10af](https://github.com/ogcio/o11y/commit/f8f10af97d9f5c188e3e65f7d62d5c673edce25a))
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * improve getMetric attributes types ([#76](https://github.com/ogcio/o11y/issues/76)) ([243649c](https://github.com/ogcio/o11y/commit/243649c4bfe750687a729fbd836772cce16e0cb1))
34
+
35
+ ## [0.1.0-beta.7](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@v0.1.0-beta.6...@ogcio/o11y-sdk-node@v0.1.0-beta.7) (2025-02-18)
36
+
37
+
38
+ ### Features
39
+
40
+ * **sdk-node:** add span customization AB[#25358](https://github.com/ogcio/o11y/issues/25358) ([46ba97b](https://github.com/ogcio/o11y/commit/46ba97bac4004ff326a954592f45213ce0e4d683))
41
+
42
+ ## [0.1.0-beta.6](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@v0.1.0-beta.5...@ogcio/o11y-sdk-node@v0.1.0-beta.6) (2025-02-06)
43
+
44
+
45
+ ### Features
46
+
47
+ * remove pnpm test on prepublishOnly script ([#68](https://github.com/ogcio/o11y/issues/68)) ([41f6f57](https://github.com/ogcio/o11y/commit/41f6f57fa415c4f7adc29f49f983539274ef7320))
48
+
49
+ ## [0.1.0-beta.5](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@v0.1.0-beta.4...@ogcio/o11y-sdk-node@v0.1.0-beta.5) (2025-02-06)
50
+
51
+
52
+ ### Features
53
+
54
+ * add opentelemetry sampler ([#65](https://github.com/ogcio/o11y/issues/65)) ([66793fd](https://github.com/ogcio/o11y/commit/66793fd36bf071e592e3b455f2e33ad9d5b2db37))
55
+
56
+ ## [0.1.0-beta.4](https://github.com/ogcio/o11y/compare/@ogcio/o11y-sdk-node@v0.1.0-beta.3...@ogcio/o11y-sdk-node@v0.1.0-beta.4) (2025-01-29)
57
+
58
+
59
+ ### Bug Fixes
60
+
61
+ * prepublishOnly hook ([#60](https://github.com/ogcio/o11y/issues/60)) ([9fbd3ad](https://github.com/ogcio/o11y/commit/9fbd3ad0b45a1604cf2eccc26b2f8855640417a1))
62
+
63
+ ## [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)
64
+
65
+
66
+ ### Features
67
+
68
+ * **sdk-node:** custom metrics ([#53](https://github.com/ogcio/o11y/issues/53)) ([3cb40b1](https://github.com/ogcio/o11y/commit/3cb40b1add3d80615c8d123d233724094559c7ff))
69
+
70
+ ## [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)
71
+
72
+
73
+ ### Bug Fixes
74
+
75
+ * Add readme file inside submodule ([#27](https://github.com/ogcio/o11y/issues/27)) ([dc518d5](https://github.com/ogcio/o11y/commit/dc518d5dde573368443bc0f8619e80e331880c78))
76
+
3
77
  ## [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
78
 
5
79
 
package/README.md ADDED
@@ -0,0 +1,222 @@
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
+ resourceAttributes: {
31
+ "team.infra.cluster": "dev-01",
32
+ "team.infra.pod": "01",
33
+ "team.service.type": "fastify",
34
+ },
35
+ spanAttributes: {
36
+ "signal.namespace": "documentation",
37
+ },
38
+ ignoreUrls: [{ type: "equals", url: "/api/health" }],
39
+ }),
40
+ );
41
+ ```
42
+
43
+ Run your node script with instrumentation
44
+
45
+ `node --import instrumentation.js server.js`
46
+
47
+ Or setup inside your package.json
48
+
49
+ ```json
50
+ {
51
+ "main": "dist/index.js",
52
+ "type": "module",
53
+ "scripts": {
54
+ "start": "node --env-file=.env --import ./dist/instrumentation.js dist/index.js"
55
+ }
56
+ }
57
+ ```
58
+
59
+ ## Span Customization
60
+
61
+ It is possible to customize spans such as traces and logs globally or in a single code statement using predefined functions.
62
+
63
+ ### Global Configuration
64
+
65
+ In the SDK configuration, you can set the following properties:
66
+
67
+ - `spanAttributes` Object containing static properties or functions used to evaluate custom attributes for every logs and traces.
68
+ - `resourceAttributes` Object containing static properties used as resources attributes for any signal.
69
+ - `traceRatio` Faction value from 0 to 1, used by TraceIdRatioBasedSampler which it deterministically samples a percentage of traces that you pass in as a parameter.
70
+
71
+ ```typescript
72
+ function generateRandomString(): string {
73
+ return Math.random() + "_" + Date.now();
74
+ }
75
+
76
+ instrumentNode({
77
+ resourceAttributes: {
78
+ "property.name.one": "value_one",
79
+ "property.name.two": "value_two",
80
+ },
81
+ spanAttributes: {
82
+ "custom.span.value": "example",
83
+ "custom.span.value_with_function": generateRandomString,
84
+ },
85
+ });
86
+ ```
87
+
88
+ ### Edit Active Span
89
+
90
+ Using `getActiveSpan` function, you can access to current transaction span and customize it.
91
+
92
+ You can use the function everywhere in your code, and set some custom attributes that are enabled for that single span
93
+
94
+ ```typescript
95
+ import { getActiveSpan } from "@ogcio/o11y-sdk-node";
96
+
97
+ async function routes(app: FastifyInstance) {
98
+ app.get("/", async (req, reply) => {
99
+ // validation and business logic
100
+
101
+ // set span attribute
102
+ getActiveSpan()?.setAttribute("business.info", "dummy");
103
+
104
+ reply.status(200).send(response);
105
+ });
106
+ }
107
+ ```
108
+
109
+ ## Sending Custom Metrics
110
+
111
+ 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.
112
+
113
+ To use this functionality, you only need to import `getMetric` and enable the application instrumentation.
114
+
115
+ ```typescript
116
+ import { getMetric } from "@ogcio/o11y-sdk-node";
117
+ ```
118
+
119
+ ### Sync
120
+
121
+ Sync metrics are signals sent when the function has been called.
122
+
123
+ Creating a counter, there are 2 types of counter:
124
+
125
+ - **counter** a simple counter that can only add positive numbers
126
+ - **updowncounter** counter that support also negative numbers
127
+
128
+ ```typescript
129
+ const counter = getMetric("counter", {
130
+ attributeName: "counter",
131
+ metricName: "fastify-counter",
132
+ });
133
+
134
+ counter.add(1, {
135
+ my: "my",
136
+ custom: "custom",
137
+ attributes: "attributes",
138
+ });
139
+ ```
140
+
141
+ Creating a Histogram
142
+
143
+ ```typescript
144
+ const histogram = getMetric("histogram", {
145
+ metricName: "response_duration",
146
+ attributeName: "http_response",
147
+ options: {
148
+ advice: {
149
+ explicitBucketBoundaries: [0, 100, 200, 500, 1000],
150
+ },
151
+ description: "Response durations",
152
+ },
153
+ });
154
+
155
+ histogram.record(120, { path: "/home" });
156
+ ```
157
+
158
+ ### Async
159
+
160
+ Async metrics are called by the scraper collector to read current data using the `Observable` pattern.
161
+ Creating an async metric means that the application will subscribe to the observability URL and record data on call (default 60s).
162
+
163
+ _keep in mind, you can't send signals on-demand with this component_
164
+
165
+ Creating an async Gauge
166
+
167
+ ```typescript
168
+ const asyncGauge = getMetric("async-gauge", {
169
+ metricName: "cpu_usage",
170
+ attributeName: "server_load",
171
+ options: { unit: "percentage" },
172
+ }).addCallback((observer) => {
173
+ observer.observe(50, { host: "server1" });
174
+ });
175
+ ```
176
+
177
+ Creating an async Counter
178
+
179
+ ```typescript
180
+ getMetric("async-counter", {
181
+ attributeName: "scraped-memory",
182
+ metricName: "fastify-counter",
183
+ }).addCallback((observer) => {
184
+ observer.observe(freemem(), {
185
+ "application.os.memory": "free-memory",
186
+ });
187
+ });
188
+ ```
189
+
190
+ ## API Reference
191
+
192
+ #### Protocol
193
+
194
+ protocol is a string parameter used to define how to send signals to observability infrastructure
195
+
196
+ - **grpc** Use gRPC protocol, usually default port use 4317. Is the most performant option for server side applications.
197
+ - **http** Use HTTP standard protocol, usually default port use 4318. Mainly used on web or UI client applications.
198
+ - **console** Used for debugging sending signals to observability cluster, every information will be printed to your runtime console.
199
+
200
+ #### Shared Types
201
+
202
+ ```ts
203
+ export type SDKLogLevel =
204
+ | "NONE"
205
+ | "ERROR"
206
+ | "WARN"
207
+ | "INFO"
208
+ | "DEBUG"
209
+ | "VERBOSE"
210
+ | "ALL";
211
+ ```
212
+
213
+ ### Config
214
+
215
+ | Parameter | Type | Description |
216
+ | :-------------- | :---------------- | :------------------------------------------------------------------------------------------------------- |
217
+ | `collectorUrl` | `string` | **Required**. The opentelemetry collector entrypoint url, if null, instrumentation will not be activated |
218
+ | `serviceName` | `string` | Name of your application used for the collector to group logs |
219
+ | `diagLogLevel` | `SDKLogLevel` | Diagnostic log level for the internal runtime instrumentation |
220
+ | `collectorMode` | `single \| batch` | Signals sending mode, default is batch for performance |
221
+ | `enableFS` | `boolean` | Flag to enable or disable the tracing for node:fs module |
222
+ | `protocol` | `string` | Type of the protocol used to send signals |
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { NodeSDK } from "@opentelemetry/sdk-node";
2
- import type { NodeSDKConfig } from "./lib/index.js";
3
2
  import buildNodeInstrumentation from "./lib/instrumentation.node.js";
4
3
  export type * from "./lib/index.js";
5
- export type { NodeSDKConfig, NodeSDK };
4
+ export type { NodeSDK };
6
5
  export { buildNodeInstrumentation as instrumentNode };
6
+ export * from "./lib/metrics.js";
7
+ export * from "./lib/traces.js";
package/dist/index.js CHANGED
@@ -1,2 +1,4 @@
1
1
  import buildNodeInstrumentation from "./lib/instrumentation.node.js";
2
2
  export { buildNodeInstrumentation as instrumentNode };
3
+ export * from "./lib/metrics.js";
4
+ export * from "./lib/traces.js";
@@ -0,0 +1,3 @@
1
+ import { NodeSDKConfig } from "../index.js";
2
+ import { Exporters } from "./index.js";
3
+ export default function buildConsoleExporters(config: NodeSDKConfig): Exporters;
@@ -0,0 +1,20 @@
1
+ import { ConsoleLogRecordExporter, SimpleLogRecordProcessor, } from "@opentelemetry/sdk-logs";
2
+ import { metrics } from "@opentelemetry/sdk-node";
3
+ import { ConsoleSpanExporter, SimpleSpanProcessor, } from "@opentelemetry/sdk-trace-base";
4
+ import { EnrichSpanProcessor } from "../processor/enrich-span-processor.js";
5
+ import { EnrichLogProcessor } from "../processor/enrich-logger-processor.js";
6
+ export default function buildConsoleExporters(config) {
7
+ return {
8
+ spans: [
9
+ new SimpleSpanProcessor(new ConsoleSpanExporter()),
10
+ new EnrichSpanProcessor(config.spanAttributes),
11
+ ],
12
+ metrics: new metrics.PeriodicExportingMetricReader({
13
+ exporter: new metrics.ConsoleMetricExporter(),
14
+ }),
15
+ logs: [
16
+ new EnrichLogProcessor(config.spanAttributes),
17
+ new SimpleLogRecordProcessor(new ConsoleLogRecordExporter()),
18
+ ],
19
+ };
20
+ }
@@ -0,0 +1,3 @@
1
+ import { NodeSDKConfig } from "../index.js";
2
+ import { Exporters } from "./index.js";
3
+ export default function buildGrpcExporters(config: NodeSDKConfig): Exporters;
@@ -1,22 +1,28 @@
1
- import { LogRecordProcessorMap } from "./utils.js";
1
+ import { metrics } from "@opentelemetry/sdk-node";
2
2
  import { CompressionAlgorithm } from "@opentelemetry/otlp-exporter-base";
3
- import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
4
- import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-grpc";
5
- import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-grpc";
6
3
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-grpc";
4
+ import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-grpc";
5
+ import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-grpc";
6
+ import { LogRecordProcessorMap, SpanProcessorMap } from "../utils.js";
7
+ import { EnrichSpanProcessor } from "../processor/enrich-span-processor.js";
8
+ import { EnrichLogProcessor } from "../processor/enrich-logger-processor.js";
7
9
  export default function buildGrpcExporters(config) {
8
10
  return {
9
- traces: new OTLPTraceExporter({
10
- url: `${config.collectorUrl}`,
11
- compression: CompressionAlgorithm.GZIP,
12
- }),
13
- metrics: new PeriodicExportingMetricReader({
11
+ spans: [
12
+ new SpanProcessorMap[config.collectorMode ?? "batch"](new OTLPTraceExporter({
13
+ url: `${config.collectorUrl}`,
14
+ compression: CompressionAlgorithm.GZIP,
15
+ })),
16
+ new EnrichSpanProcessor(config.spanAttributes),
17
+ ],
18
+ metrics: new metrics.PeriodicExportingMetricReader({
14
19
  exporter: new OTLPMetricExporter({
15
20
  url: `${config.collectorUrl}`,
16
21
  compression: CompressionAlgorithm.GZIP,
17
22
  }),
18
23
  }),
19
24
  logs: [
25
+ new EnrichLogProcessor(config.spanAttributes),
20
26
  new LogRecordProcessorMap[config.collectorMode ?? "batch"](new OTLPLogExporter({
21
27
  url: `${config.collectorUrl}`,
22
28
  compression: CompressionAlgorithm.GZIP,
@@ -0,0 +1,3 @@
1
+ import { Exporters } from "./index.js";
2
+ import { NodeSDKConfig } from "../index.js";
3
+ export default function buildHttpExporters(config: NodeSDKConfig): Exporters;
@@ -1,25 +1,31 @@
1
- import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
2
- import { LogRecordProcessorMap } from "./utils.js";
1
+ import { metrics } from "@opentelemetry/sdk-node";
3
2
  import { CompressionAlgorithm } from "@opentelemetry/otlp-exporter-base";
4
- import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
5
- import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
3
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
6
4
  import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
5
+ import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
6
+ import { LogRecordProcessorMap, SpanProcessorMap } from "../utils.js";
7
+ import { EnrichSpanProcessor } from "../processor/enrich-span-processor.js";
8
+ import { EnrichLogProcessor } from "../processor/enrich-logger-processor.js";
7
9
  export default function buildHttpExporters(config) {
8
10
  if (config.collectorUrl.endsWith("/")) {
9
11
  config.collectorUrl = config.collectorUrl.slice(0, -1);
10
12
  }
11
13
  return {
12
- traces: new OTLPTraceExporter({
13
- url: `${config.collectorUrl}/v1/traces`,
14
- compression: CompressionAlgorithm.GZIP,
15
- }),
16
- metrics: new PeriodicExportingMetricReader({
14
+ spans: [
15
+ new SpanProcessorMap[config.collectorMode ?? "batch"](new OTLPTraceExporter({
16
+ url: `${config.collectorUrl}/v1/traces`,
17
+ compression: CompressionAlgorithm.GZIP,
18
+ })),
19
+ new EnrichSpanProcessor(config.spanAttributes),
20
+ ],
21
+ metrics: new metrics.PeriodicExportingMetricReader({
17
22
  exporter: new OTLPMetricExporter({
18
23
  url: `${config.collectorUrl}/v1/metrics`,
19
24
  compression: CompressionAlgorithm.GZIP,
20
25
  }),
21
26
  }),
22
27
  logs: [
28
+ new EnrichLogProcessor(config.spanAttributes),
23
29
  new LogRecordProcessorMap[config.collectorMode ?? "batch"](new OTLPLogExporter({
24
30
  url: `${config.collectorUrl}/v1/logs`,
25
31
  compression: CompressionAlgorithm.GZIP,
@@ -0,0 +1,8 @@
1
+ import { LogRecordProcessor } from "@opentelemetry/sdk-logs";
2
+ import type { metrics } from "@opentelemetry/sdk-node";
3
+ import { SpanProcessor } from "@opentelemetry/sdk-trace-base";
4
+ export type Exporters = {
5
+ spans: SpanProcessor[];
6
+ metrics: metrics.MetricReader;
7
+ logs: LogRecordProcessor[];
8
+ };
@@ -1,4 +1,4 @@
1
- interface SDKConfig {
1
+ export interface NodeSDKConfig {
2
2
  /**
3
3
  * The opentelemetry collector entrypoint GRPC url.
4
4
  * If the collectoUrl is null or undefined, the instrumentation will not be activated.
@@ -25,8 +25,27 @@ interface SDKConfig {
25
25
  * @default batch
26
26
  */
27
27
  collectorMode?: SDKCollectorMode;
28
- }
29
- export interface NodeSDKConfig extends SDKConfig {
28
+ /**
29
+ * Array of not traced urls.
30
+ *
31
+ * @type {SamplerCondition}
32
+ * @default []
33
+ */
34
+ ignoreUrls?: SamplerCondition[];
35
+ /**
36
+ * Object containing static properties or functions used to evaluate custom attributes for every logs and traces.
37
+ */
38
+ spanAttributes?: Record<string, SignalAttributeValue | (() => SignalAttributeValue)>;
39
+ /**
40
+ * Object containing static properties used as resources attributes for the Node SDK initialization.
41
+ */
42
+ resourceAttributes?: Record<string, SignalAttributeValue>;
43
+ /**
44
+ * Faction value from 0 to 1, used by TraceIdRatioBasedSampler which it deterministically samples a percentage of traces that you pass in as a parameter.
45
+ *
46
+ * @default 1
47
+ */
48
+ traceRatio?: number;
30
49
  /**
31
50
  * Flag to enable or disable the tracing for node:fs module
32
51
  *
@@ -34,13 +53,17 @@ export interface NodeSDKConfig extends SDKConfig {
34
53
  */
35
54
  enableFS?: boolean;
36
55
  /**
37
- * http based connection protocol used to send signals.
56
+ * Protocol used to send signals.
38
57
  *
39
58
  * @default grpc
40
59
  */
41
60
  protocol?: SDKProtocol;
42
61
  }
62
+ export interface SamplerCondition {
63
+ type: "endsWith" | "includes" | "equals";
64
+ url: string;
65
+ }
66
+ export type SignalAttributeValue = string | number | boolean;
43
67
  export type SDKCollectorMode = "single" | "batch";
44
- export type SDKProtocol = "grpc" | "http";
68
+ export type SDKProtocol = "grpc" | "http" | "console";
45
69
  export type SDKLogLevel = "NONE" | "ERROR" | "WARN" | "INFO" | "DEBUG" | "VERBOSE" | "ALL";
46
- export {};
@@ -1,10 +1,13 @@
1
- import { NodeSDK } from "@opentelemetry/sdk-node";
2
- import isUrl from "is-url";
3
- import buildHttpExporters from "./http.js";
4
- import buildGrpcExporters from "./grpc.js";
5
1
  import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
6
2
  import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
7
3
  import { W3CTraceContextPropagator } from "@opentelemetry/core";
4
+ import { NodeSDK } from "@opentelemetry/sdk-node";
5
+ import { AlwaysOffSampler, ParentBasedSampler, TraceIdRatioBasedSampler, } from "@opentelemetry/sdk-trace-base";
6
+ import buildConsoleExporters from "./exporter/console.js";
7
+ import buildGrpcExporters from "./exporter/grpc.js";
8
+ import buildHttpExporters from "./exporter/http.js";
9
+ import { ObservabilityResourceDetector } from "./resource.js";
10
+ import { UrlSampler } from "./url-sampler.js";
8
11
  export default function buildNodeInstrumentation(config) {
9
12
  if (!config) {
10
13
  console.warn("observability config not set. Skipping NodeJS OpenTelemetry instrumentation.");
@@ -22,18 +25,33 @@ export default function buildNodeInstrumentation(config) {
22
25
  if (config.protocol === "http") {
23
26
  exporter = buildHttpExporters(config);
24
27
  }
28
+ else if (config.protocol === "console") {
29
+ exporter = buildConsoleExporters(config);
30
+ }
25
31
  else {
26
32
  exporter = buildGrpcExporters(config);
27
33
  }
34
+ const urlSampler = new UrlSampler(config.ignoreUrls, new TraceIdRatioBasedSampler(config.traceRatio ?? 1));
35
+ const mainSampler = new ParentBasedSampler({
36
+ root: urlSampler,
37
+ remoteParentSampled: urlSampler,
38
+ remoteParentNotSampled: new AlwaysOffSampler(),
39
+ localParentSampled: urlSampler,
40
+ localParentNotSampled: new AlwaysOffSampler(),
41
+ });
28
42
  try {
29
43
  diag.setLogger(new DiagConsoleLogger(), config.diagLogLevel
30
44
  ? DiagLogLevel[config.diagLogLevel]
31
45
  : DiagLogLevel.INFO);
32
46
  const sdk = new NodeSDK({
47
+ resourceDetectors: [
48
+ new ObservabilityResourceDetector(config.resourceAttributes),
49
+ ],
50
+ spanProcessors: exporter.spans,
33
51
  serviceName: config.serviceName,
34
- traceExporter: exporter.traces,
35
52
  metricReader: exporter.metrics,
36
53
  logRecordProcessors: exporter.logs,
54
+ sampler: mainSampler,
37
55
  textMapPropagator: new W3CTraceContextPropagator(),
38
56
  instrumentations: [
39
57
  getNodeAutoInstrumentations({
@@ -51,3 +69,12 @@ export default function buildNodeInstrumentation(config) {
51
69
  console.error("Error starting NodeJS OpenTelemetry instrumentation:", error);
52
70
  }
53
71
  }
72
+ function isUrl(url) {
73
+ try {
74
+ new URL(url);
75
+ return true;
76
+ }
77
+ catch (_) {
78
+ return false;
79
+ }
80
+ }
@@ -0,0 +1,18 @@
1
+ import { Counter, Gauge, Histogram, MetricOptions, ObservableCounter, ObservableGauge, ObservableUpDownCounter, UpDownCounter, Attributes } from "@opentelemetry/api";
2
+ type MetricTypeMap<TAttributes extends Attributes> = {
3
+ counter: Counter<TAttributes>;
4
+ histogram: Histogram<TAttributes>;
5
+ gauge: Gauge<TAttributes>;
6
+ updowncounter: UpDownCounter<TAttributes>;
7
+ "async-counter": ObservableCounter<TAttributes>;
8
+ "async-updowncounter": ObservableUpDownCounter<TAttributes>;
9
+ "async-gauge": ObservableGauge<TAttributes>;
10
+ };
11
+ export type MetricType = keyof MetricTypeMap<Attributes>;
12
+ export interface MetricsParams {
13
+ meterName: string;
14
+ metricName: string;
15
+ options?: MetricOptions;
16
+ }
17
+ export declare function getMetric<T extends MetricType, TAttributes extends Attributes = Attributes>(type: T, p: MetricsParams): MetricTypeMap<TAttributes>[T];
18
+ export {};
@@ -0,0 +1,24 @@
1
+ import { createNoopMeter, metrics, } from "@opentelemetry/api";
2
+ const MetricsFactoryMap = {
3
+ gauge: (meter) => meter.createGauge,
4
+ histogram: (meter) => meter.createHistogram,
5
+ counter: (meter) => meter.createCounter,
6
+ updowncounter: (meter) => meter.createUpDownCounter,
7
+ "async-counter": (meter) => meter.createObservableCounter,
8
+ "async-updowncounter": (meter) => meter.createObservableUpDownCounter,
9
+ "async-gauge": (meter) => meter.createObservableGauge,
10
+ };
11
+ function getMeter({ meterName }) {
12
+ if (!meterName) {
13
+ console.error("Invalid metric name!");
14
+ return createNoopMeter();
15
+ }
16
+ return metrics.getMeter(`custom_metric.${meterName}`);
17
+ }
18
+ export function getMetric(type, p) {
19
+ const meter = getMeter(p);
20
+ if (!MetricsFactoryMap[type]) {
21
+ throw new Error(`Unsupported metric type: ${type}`);
22
+ }
23
+ return MetricsFactoryMap[type](meter).bind(meter)(p.metricName, p.options);
24
+ }
@@ -0,0 +1,10 @@
1
+ import { LogRecord, LogRecordProcessor } from "@opentelemetry/sdk-logs";
2
+ import { Context } from "@opentelemetry/api";
3
+ import { SignalAttributeValue } from "../index.js";
4
+ export declare class EnrichLogProcessor implements LogRecordProcessor {
5
+ private _spanAttributes?;
6
+ constructor(spanAttributes?: Record<string, SignalAttributeValue | (() => SignalAttributeValue)>);
7
+ forceFlush(): Promise<void>;
8
+ onEmit(logRecord: LogRecord, _context?: Context): void;
9
+ shutdown(): Promise<void>;
10
+ }
@@ -0,0 +1,19 @@
1
+ export class EnrichLogProcessor {
2
+ _spanAttributes = undefined;
3
+ constructor(spanAttributes) {
4
+ this._spanAttributes = spanAttributes;
5
+ }
6
+ forceFlush() {
7
+ return Promise.resolve();
8
+ }
9
+ onEmit(logRecord, _context) {
10
+ if (this._spanAttributes) {
11
+ for (const [key, value] of Object.entries(this._spanAttributes)) {
12
+ logRecord.setAttribute(key, typeof value === "function" ? value() : value);
13
+ }
14
+ }
15
+ }
16
+ shutdown() {
17
+ return Promise.resolve();
18
+ }
19
+ }
@@ -0,0 +1,11 @@
1
+ import { Context } from "@opentelemetry/api";
2
+ import { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-base";
3
+ import { SignalAttributeValue } from "../index.js";
4
+ export declare class EnrichSpanProcessor implements SpanProcessor {
5
+ private _spanAttributes;
6
+ constructor(spanAttributes?: Record<string, SignalAttributeValue | (() => SignalAttributeValue)>);
7
+ forceFlush(): Promise<void>;
8
+ onStart(span: Span, _context: Context): void;
9
+ onEnd(_span: ReadableSpan): void;
10
+ shutdown(): Promise<void>;
11
+ }