@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
@@ -0,0 +1,22 @@
1
+ export class EnrichSpanProcessor {
2
+ _spanAttributes;
3
+ constructor(spanAttributes) {
4
+ this._spanAttributes = spanAttributes;
5
+ }
6
+ forceFlush() {
7
+ return Promise.resolve();
8
+ }
9
+ onStart(span, _context) {
10
+ if (this._spanAttributes != undefined) {
11
+ for (const [key, value] of Object.entries(this._spanAttributes)) {
12
+ span.setAttribute(key, typeof value === "function" ? value() : value);
13
+ }
14
+ }
15
+ }
16
+ onEnd(_span) {
17
+ return;
18
+ }
19
+ shutdown() {
20
+ return Promise.resolve();
21
+ }
22
+ }
@@ -0,0 +1,7 @@
1
+ import { ResourceDetector, DetectedResource } from "@opentelemetry/resources";
2
+ import { SignalAttributeValue } from "./index.js";
3
+ export declare class ObservabilityResourceDetector implements ResourceDetector {
4
+ private _resourceAttributes;
5
+ constructor(resourceAttributes?: Record<string, SignalAttributeValue>);
6
+ detect(): DetectedResource;
7
+ }
@@ -0,0 +1,18 @@
1
+ import packageJson from "../package.json" with { type: "json" };
2
+ export class ObservabilityResourceDetector {
3
+ _resourceAttributes;
4
+ constructor(resourceAttributes) {
5
+ this._resourceAttributes = resourceAttributes;
6
+ }
7
+ detect() {
8
+ let attributes = {};
9
+ if (this._resourceAttributes) {
10
+ attributes = {
11
+ ...this._resourceAttributes,
12
+ };
13
+ }
14
+ attributes["o11y.sdk.name"] = packageJson.name;
15
+ attributes["o11y.sdk.version"] = packageJson.version;
16
+ return { attributes };
17
+ }
18
+ }
@@ -0,0 +1 @@
1
+ export declare function getActiveSpan(): import("@opentelemetry/api").Span | undefined;
@@ -0,0 +1,4 @@
1
+ import { trace } from "@opentelemetry/api";
2
+ export function getActiveSpan() {
3
+ return trace.getActiveSpan();
4
+ }
@@ -0,0 +1,10 @@
1
+ import { Attributes, Context, Link, SpanKind } from "@opentelemetry/api";
2
+ import { Sampler, SamplingResult } from "@opentelemetry/sdk-trace-base";
3
+ import { SamplerCondition } from "./index.js";
4
+ export declare class UrlSampler implements Sampler {
5
+ private _samplerCondition;
6
+ private _nextSampler;
7
+ constructor(samplerCondition: SamplerCondition[] | undefined, nextSampler: Sampler);
8
+ shouldSample(_context: Context, traceId: string, _spanName: string, _spanKind: SpanKind, attributes: Attributes, _links: Link[]): SamplingResult;
9
+ toString(): string;
10
+ }
@@ -0,0 +1,25 @@
1
+ import { SamplingDecision, } from "@opentelemetry/sdk-trace-base";
2
+ export class UrlSampler {
3
+ _samplerCondition;
4
+ _nextSampler;
5
+ constructor(samplerCondition = [], nextSampler) {
6
+ this._samplerCondition = samplerCondition;
7
+ this._nextSampler = nextSampler;
8
+ }
9
+ shouldSample(_context, traceId, _spanName, _spanKind, attributes, _links) {
10
+ const url = attributes["http.target"]?.toString();
11
+ if (url) {
12
+ for (const condition of this._samplerCondition) {
13
+ if ((condition.type === "equals" && url === condition.url) ||
14
+ (condition.type === "endsWith" && url.endsWith(condition.url)) ||
15
+ (condition.type === "includes" && url.includes(condition.url))) {
16
+ return { decision: SamplingDecision.NOT_RECORD };
17
+ }
18
+ }
19
+ }
20
+ return this._nextSampler.shouldSample(_context, traceId, _spanName, _spanKind, attributes, _links);
21
+ }
22
+ toString() {
23
+ return "UrlSampler";
24
+ }
25
+ }
@@ -1,3 +1,5 @@
1
+ import { BatchLogRecordProcessor, SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
1
2
  import { SDKCollectorMode } from "./index.js";
2
- import { logs } from "@opentelemetry/sdk-node";
3
- export declare const LogRecordProcessorMap: Record<SDKCollectorMode, typeof logs.SimpleLogRecordProcessor | typeof logs.BatchLogRecordProcessor>;
3
+ import { tracing } from "@opentelemetry/sdk-node";
4
+ export declare const LogRecordProcessorMap: Record<SDKCollectorMode, typeof SimpleLogRecordProcessor | typeof BatchLogRecordProcessor>;
5
+ export declare const SpanProcessorMap: Record<SDKCollectorMode, typeof tracing.SimpleSpanProcessor | typeof tracing.BatchSpanProcessor>;
package/dist/lib/utils.js CHANGED
@@ -1,5 +1,10 @@
1
- import { logs } from "@opentelemetry/sdk-node";
1
+ import { BatchLogRecordProcessor, SimpleLogRecordProcessor, } from "@opentelemetry/sdk-logs";
2
+ import { tracing } from "@opentelemetry/sdk-node";
2
3
  export const LogRecordProcessorMap = {
3
- single: logs.SimpleLogRecordProcessor,
4
- batch: logs.BatchLogRecordProcessor,
4
+ single: SimpleLogRecordProcessor,
5
+ batch: BatchLogRecordProcessor,
6
+ };
7
+ export const SpanProcessorMap = {
8
+ single: tracing.SimpleSpanProcessor,
9
+ batch: tracing.BatchSpanProcessor,
5
10
  };
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@ogcio/o11y-sdk-node",
3
+ "version": "0.1.0-beta.10",
4
+ "description": "Opentelemetry standard instrumentation SDK for NodeJS based project",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "build": "rm -rf dist && tsc -p tsconfig.json",
9
+ "test": "vitest",
10
+ "prepublishOnly": "pnpm i && pnpm build",
11
+ "test:unit": "vitest --project unit",
12
+ "test:integration": "pnpm --filter @ogcio/o11y run prepare:integration && vitest --project integration",
13
+ "test:integration:dryrun": "vitest --project integration"
14
+ },
15
+ "exports": {
16
+ ".": "./dist/index.js",
17
+ "./*": "./dist/*.js"
18
+ },
19
+ "keywords": [
20
+ "observability",
21
+ "o11y",
22
+ "opentelemetry",
23
+ "node",
24
+ "nodejs",
25
+ "ogcio"
26
+ ],
27
+ "author": "team:ogcio/observability",
28
+ "license": "ISC",
29
+ "dependencies": {
30
+ "@opentelemetry/api": "^1.9.0",
31
+ "@opentelemetry/auto-instrumentations-node": "^0.57.1",
32
+ "@opentelemetry/core": "^2.0.0",
33
+ "@opentelemetry/exporter-logs-otlp-grpc": "^0.200.0",
34
+ "@opentelemetry/exporter-logs-otlp-http": "^0.200.0",
35
+ "@opentelemetry/exporter-metrics-otlp-grpc": "^0.200.0",
36
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.200.0",
37
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.200.0",
38
+ "@opentelemetry/exporter-trace-otlp-http": "^0.200.0",
39
+ "@opentelemetry/instrumentation": "^0.200.0",
40
+ "@opentelemetry/otlp-exporter-base": "^0.200.0",
41
+ "@opentelemetry/resources": "^2.0.0",
42
+ "@opentelemetry/sdk-logs": "^0.200.0",
43
+ "@opentelemetry/sdk-metrics": "^2.0.0",
44
+ "@opentelemetry/sdk-node": "^0.200.0",
45
+ "@opentelemetry/sdk-trace-base": "^2.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.14.0",
49
+ "@vitest/coverage-v8": "^3.1.1",
50
+ "tsx": "^4.19.3",
51
+ "typescript": "^5.8.3",
52
+ "vitest": "^3.1.1"
53
+ },
54
+ "engines": {
55
+ "node": ">=20.6.0"
56
+ }
57
+ }
@@ -3,7 +3,6 @@ export default defineConfig({
3
3
  test: {
4
4
  globals: true,
5
5
  watch: false,
6
- include: ["**/test/*.test.ts"],
7
6
  exclude: ["**/fixtures/**", "**/dist/**"],
8
7
  poolOptions: {
9
8
  threads: {
@@ -21,5 +20,20 @@ export default defineConfig({
21
20
  },
22
21
  reporters: ["default", ["junit", { outputFile: "test-report.xml" }]],
23
22
  environment: "node",
23
+ pool: "threads",
24
+ workspace: [
25
+ {
26
+ test: {
27
+ include: ["**/test/*.test.ts", "**/test/processor/*.test.ts"],
28
+ name: "unit",
29
+ },
30
+ },
31
+ {
32
+ test: {
33
+ include: ["**/test/integration/*.test.ts"],
34
+ name: "integration",
35
+ },
36
+ },
37
+ ],
24
38
  },
25
39
  });
package/index.ts CHANGED
@@ -1,7 +1,9 @@
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
 
5
4
  export type * from "./lib/index.js";
6
- export type { NodeSDKConfig, NodeSDK };
5
+ export type { NodeSDK };
7
6
  export { buildNodeInstrumentation as instrumentNode };
7
+
8
+ export * from "./lib/metrics.js";
9
+ export * from "./lib/traces.js";
@@ -0,0 +1,31 @@
1
+ import {
2
+ ConsoleLogRecordExporter,
3
+ SimpleLogRecordProcessor,
4
+ } from "@opentelemetry/sdk-logs";
5
+ import { metrics } from "@opentelemetry/sdk-node";
6
+ import {
7
+ ConsoleSpanExporter,
8
+ SimpleSpanProcessor,
9
+ } from "@opentelemetry/sdk-trace-base";
10
+ import { NodeSDKConfig } from "../index.js";
11
+ import { Exporters } from "./index.js";
12
+ import { EnrichSpanProcessor } from "../processor/enrich-span-processor.js";
13
+ import { EnrichLogProcessor } from "../processor/enrich-logger-processor.js";
14
+
15
+ export default function buildConsoleExporters(
16
+ config: NodeSDKConfig,
17
+ ): Exporters {
18
+ return {
19
+ spans: [
20
+ new SimpleSpanProcessor(new ConsoleSpanExporter()),
21
+ new EnrichSpanProcessor(config.spanAttributes),
22
+ ],
23
+ metrics: new metrics.PeriodicExportingMetricReader({
24
+ exporter: new metrics.ConsoleMetricExporter(),
25
+ }),
26
+ logs: [
27
+ new EnrichLogProcessor(config.spanAttributes),
28
+ new SimpleLogRecordProcessor(new ConsoleLogRecordExporter()),
29
+ ],
30
+ };
31
+ }
@@ -1,25 +1,33 @@
1
- import type { NodeSDKConfig } from "./index.js";
2
- import type { Exporters } from "./options.js";
3
- import { LogRecordProcessorMap } from "./utils.js";
1
+ import { metrics } from "@opentelemetry/sdk-node";
4
2
  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
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 { NodeSDKConfig } from "../index.js";
7
+ import { Exporters } from "./index.js";
8
+ import { LogRecordProcessorMap, SpanProcessorMap } from "../utils.js";
9
+ import { EnrichSpanProcessor } from "../processor/enrich-span-processor.js";
10
+ import { EnrichLogProcessor } from "../processor/enrich-logger-processor.js";
9
11
 
10
12
  export default function buildGrpcExporters(config: NodeSDKConfig): Exporters {
11
13
  return {
12
- traces: new OTLPTraceExporter({
13
- url: `${config.collectorUrl}`,
14
- compression: CompressionAlgorithm.GZIP,
15
- }),
16
- metrics: new PeriodicExportingMetricReader({
14
+ spans: [
15
+ new SpanProcessorMap[config.collectorMode ?? "batch"](
16
+ new OTLPTraceExporter({
17
+ url: `${config.collectorUrl}`,
18
+ compression: CompressionAlgorithm.GZIP,
19
+ }),
20
+ ),
21
+ new EnrichSpanProcessor(config.spanAttributes),
22
+ ],
23
+ metrics: new metrics.PeriodicExportingMetricReader({
17
24
  exporter: new OTLPMetricExporter({
18
25
  url: `${config.collectorUrl}`,
19
26
  compression: CompressionAlgorithm.GZIP,
20
27
  }),
21
28
  }),
22
29
  logs: [
30
+ new EnrichLogProcessor(config.spanAttributes),
23
31
  new LogRecordProcessorMap[config.collectorMode ?? "batch"](
24
32
  new OTLPLogExporter({
25
33
  url: `${config.collectorUrl}`,
@@ -1,11 +1,13 @@
1
- import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
2
- import type { NodeSDKConfig } from "./index.js";
3
- import type { Exporters } from "./options.js";
4
- import { LogRecordProcessorMap } from "./utils.js";
1
+ import { metrics } from "@opentelemetry/sdk-node";
5
2
  import { CompressionAlgorithm } from "@opentelemetry/otlp-exporter-base";
6
- import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
7
- import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
3
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
8
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 { Exporters } from "./index.js";
9
+ import { NodeSDKConfig } from "../index.js";
10
+ import { EnrichLogProcessor } from "../processor/enrich-logger-processor.js";
9
11
 
10
12
  export default function buildHttpExporters(config: NodeSDKConfig): Exporters {
11
13
  if (config.collectorUrl.endsWith("/")) {
@@ -13,17 +15,23 @@ export default function buildHttpExporters(config: NodeSDKConfig): Exporters {
13
15
  }
14
16
 
15
17
  return {
16
- traces: new OTLPTraceExporter({
17
- url: `${config.collectorUrl}/v1/traces`,
18
- compression: CompressionAlgorithm.GZIP,
19
- }),
20
- metrics: new PeriodicExportingMetricReader({
18
+ spans: [
19
+ new SpanProcessorMap[config.collectorMode ?? "batch"](
20
+ new OTLPTraceExporter({
21
+ url: `${config.collectorUrl}/v1/traces`,
22
+ compression: CompressionAlgorithm.GZIP,
23
+ }),
24
+ ),
25
+ new EnrichSpanProcessor(config.spanAttributes),
26
+ ],
27
+ metrics: new metrics.PeriodicExportingMetricReader({
21
28
  exporter: new OTLPMetricExporter({
22
29
  url: `${config.collectorUrl}/v1/metrics`,
23
30
  compression: CompressionAlgorithm.GZIP,
24
31
  }),
25
32
  }),
26
33
  logs: [
34
+ new EnrichLogProcessor(config.spanAttributes),
27
35
  new LogRecordProcessorMap[config.collectorMode ?? "batch"](
28
36
  new OTLPLogExporter({
29
37
  url: `${config.collectorUrl}/v1/logs`,
@@ -0,0 +1,9 @@
1
+ import { LogRecordProcessor } from "@opentelemetry/sdk-logs";
2
+ import type { metrics } from "@opentelemetry/sdk-node";
3
+ import { SpanProcessor } from "@opentelemetry/sdk-trace-base";
4
+
5
+ export type Exporters = {
6
+ spans: SpanProcessor[];
7
+ metrics: metrics.MetricReader;
8
+ logs: LogRecordProcessor[];
9
+ };
package/lib/index.ts CHANGED
@@ -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,9 +25,34 @@ interface SDKConfig {
25
25
  * @default batch
26
26
  */
27
27
  collectorMode?: SDKCollectorMode;
28
- }
28
+ /**
29
+ * Array of not traced urls.
30
+ *
31
+ * @type {SamplerCondition}
32
+ * @default []
33
+ */
34
+ ignoreUrls?: SamplerCondition[];
35
+
36
+ /**
37
+ * Object containing static properties or functions used to evaluate custom attributes for every logs and traces.
38
+ */
39
+ spanAttributes?: Record<
40
+ string,
41
+ SignalAttributeValue | (() => SignalAttributeValue)
42
+ >;
43
+
44
+ /**
45
+ * Object containing static properties used as resources attributes for the Node SDK initialization.
46
+ */
47
+ resourceAttributes?: Record<string, SignalAttributeValue>;
48
+
49
+ /**
50
+ * Faction value from 0 to 1, used by TraceIdRatioBasedSampler which it deterministically samples a percentage of traces that you pass in as a parameter.
51
+ *
52
+ * @default 1
53
+ */
54
+ traceRatio?: number;
29
55
 
30
- export interface NodeSDKConfig extends SDKConfig {
31
56
  /**
32
57
  * Flag to enable or disable the tracing for node:fs module
33
58
  *
@@ -36,16 +61,23 @@ export interface NodeSDKConfig extends SDKConfig {
36
61
  enableFS?: boolean;
37
62
 
38
63
  /**
39
- * http based connection protocol used to send signals.
64
+ * Protocol used to send signals.
40
65
  *
41
66
  * @default grpc
42
67
  */
43
68
  protocol?: SDKProtocol;
44
69
  }
45
70
 
71
+ export interface SamplerCondition {
72
+ type: "endsWith" | "includes" | "equals";
73
+ url: string;
74
+ }
75
+
76
+ export type SignalAttributeValue = string | number | boolean;
77
+
46
78
  export type SDKCollectorMode = "single" | "batch";
47
79
 
48
- export type SDKProtocol = "grpc" | "http";
80
+ export type SDKProtocol = "grpc" | "http" | "console";
49
81
 
50
82
  export type SDKLogLevel =
51
83
  | "NONE"
@@ -1,12 +1,19 @@
1
- import { NodeSDK } from "@opentelemetry/sdk-node";
2
- import type { NodeSDKConfig } from "./index.js";
3
- import type { Exporters } from "./options.js";
4
- import isUrl from "is-url";
5
- import buildHttpExporters from "./http.js";
6
- import buildGrpcExporters from "./grpc.js";
7
1
  import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
8
2
  import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
9
3
  import { W3CTraceContextPropagator } from "@opentelemetry/core";
4
+ import { NodeSDK } from "@opentelemetry/sdk-node";
5
+ import {
6
+ AlwaysOffSampler,
7
+ ParentBasedSampler,
8
+ TraceIdRatioBasedSampler,
9
+ } from "@opentelemetry/sdk-trace-base";
10
+ import buildConsoleExporters from "./exporter/console.js";
11
+ import buildGrpcExporters from "./exporter/grpc.js";
12
+ import buildHttpExporters from "./exporter/http.js";
13
+ import type { Exporters } from "./exporter/index.js";
14
+ import type { NodeSDKConfig } from "./index.js";
15
+ import { ObservabilityResourceDetector } from "./resource.js";
16
+ import { UrlSampler } from "./url-sampler.js";
10
17
 
11
18
  export default function buildNodeInstrumentation(
12
19
  config?: NodeSDKConfig,
@@ -36,10 +43,25 @@ export default function buildNodeInstrumentation(
36
43
 
37
44
  if (config.protocol === "http") {
38
45
  exporter = buildHttpExporters(config);
46
+ } else if (config.protocol === "console") {
47
+ exporter = buildConsoleExporters(config);
39
48
  } else {
40
49
  exporter = buildGrpcExporters(config);
41
50
  }
42
51
 
52
+ const urlSampler = new UrlSampler(
53
+ config.ignoreUrls,
54
+ new TraceIdRatioBasedSampler(config.traceRatio ?? 1),
55
+ );
56
+
57
+ const mainSampler = new ParentBasedSampler({
58
+ root: urlSampler,
59
+ remoteParentSampled: urlSampler,
60
+ remoteParentNotSampled: new AlwaysOffSampler(),
61
+ localParentSampled: urlSampler,
62
+ localParentNotSampled: new AlwaysOffSampler(),
63
+ });
64
+
43
65
  try {
44
66
  diag.setLogger(
45
67
  new DiagConsoleLogger(),
@@ -49,10 +71,14 @@ export default function buildNodeInstrumentation(
49
71
  );
50
72
 
51
73
  const sdk = new NodeSDK({
74
+ resourceDetectors: [
75
+ new ObservabilityResourceDetector(config.resourceAttributes),
76
+ ],
77
+ spanProcessors: exporter.spans,
52
78
  serviceName: config.serviceName,
53
- traceExporter: exporter.traces,
54
79
  metricReader: exporter.metrics,
55
80
  logRecordProcessors: exporter.logs,
81
+ sampler: mainSampler,
56
82
  textMapPropagator: new W3CTraceContextPropagator(),
57
83
  instrumentations: [
58
84
  getNodeAutoInstrumentations({
@@ -73,3 +99,12 @@ export default function buildNodeInstrumentation(
73
99
  );
74
100
  }
75
101
  }
102
+
103
+ function isUrl(url: string): boolean {
104
+ try {
105
+ new URL(url);
106
+ return true;
107
+ } catch (_) {
108
+ return false;
109
+ }
110
+ }
package/lib/metrics.ts ADDED
@@ -0,0 +1,75 @@
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
+ Attributes,
14
+ } from "@opentelemetry/api";
15
+
16
+ type MetricTypeMap<TAttributes extends Attributes> = {
17
+ counter: Counter<TAttributes>;
18
+ histogram: Histogram<TAttributes>;
19
+ gauge: Gauge<TAttributes>;
20
+ updowncounter: UpDownCounter<TAttributes>;
21
+ "async-counter": ObservableCounter<TAttributes>;
22
+ "async-updowncounter": ObservableUpDownCounter<TAttributes>;
23
+ "async-gauge": ObservableGauge<TAttributes>;
24
+ };
25
+
26
+ export type MetricType = keyof MetricTypeMap<Attributes>;
27
+
28
+ const MetricsFactoryMap: Record<
29
+ MetricType,
30
+ (
31
+ meter: Meter,
32
+ ) => (
33
+ name: string,
34
+ options?: MetricOptions,
35
+ ) => MetricTypeMap<Attributes>[MetricType]
36
+ > = {
37
+ gauge: (meter: Meter) => meter.createGauge,
38
+ histogram: (meter: Meter) => meter.createHistogram,
39
+ counter: (meter: Meter) => meter.createCounter,
40
+ updowncounter: (meter: Meter) => meter.createUpDownCounter,
41
+ "async-counter": (meter: Meter) => meter.createObservableCounter,
42
+ "async-updowncounter": (meter: Meter) => meter.createObservableUpDownCounter,
43
+ "async-gauge": (meter: Meter) => meter.createObservableGauge,
44
+ } as const;
45
+
46
+ export interface MetricsParams {
47
+ meterName: string;
48
+ metricName: string;
49
+ options?: MetricOptions;
50
+ }
51
+
52
+ function getMeter({ meterName }: MetricsParams) {
53
+ if (!meterName) {
54
+ console.error("Invalid metric name!");
55
+ return createNoopMeter();
56
+ }
57
+
58
+ return metrics.getMeter(`custom_metric.${meterName}`);
59
+ }
60
+
61
+ export function getMetric<
62
+ T extends MetricType,
63
+ TAttributes extends Attributes = Attributes,
64
+ >(type: T, p: MetricsParams): MetricTypeMap<TAttributes>[T] {
65
+ const meter = getMeter(p);
66
+
67
+ if (!MetricsFactoryMap[type]) {
68
+ throw new Error(`Unsupported metric type: ${type}`);
69
+ }
70
+
71
+ return MetricsFactoryMap[type](meter).bind(meter)(
72
+ p.metricName,
73
+ p.options,
74
+ ) as MetricTypeMap<TAttributes>[T];
75
+ }
@@ -0,0 +1,34 @@
1
+ import { LogRecord, LogRecordProcessor } from "@opentelemetry/sdk-logs";
2
+ import { Context } from "@opentelemetry/api";
3
+ import { SignalAttributeValue } from "../index.js";
4
+
5
+ export class EnrichLogProcessor implements LogRecordProcessor {
6
+ private _spanAttributes?:
7
+ | Record<string, SignalAttributeValue | (() => SignalAttributeValue)>
8
+ | undefined = undefined;
9
+
10
+ constructor(
11
+ spanAttributes?: Record<
12
+ string,
13
+ SignalAttributeValue | (() => SignalAttributeValue)
14
+ >,
15
+ ) {
16
+ this._spanAttributes = spanAttributes;
17
+ }
18
+ forceFlush(): Promise<void> {
19
+ return Promise.resolve();
20
+ }
21
+ onEmit(logRecord: LogRecord, _context?: Context): void {
22
+ if (this._spanAttributes) {
23
+ for (const [key, value] of Object.entries(this._spanAttributes)) {
24
+ logRecord.setAttribute(
25
+ key,
26
+ typeof value === "function" ? value() : value,
27
+ );
28
+ }
29
+ }
30
+ }
31
+ shutdown(): Promise<void> {
32
+ return Promise.resolve();
33
+ }
34
+ }
@@ -0,0 +1,39 @@
1
+ import { Context } from "@opentelemetry/api";
2
+ import {
3
+ ReadableSpan,
4
+ Span,
5
+ SpanProcessor,
6
+ } from "@opentelemetry/sdk-trace-base";
7
+ import { SignalAttributeValue } from "../index.js";
8
+
9
+ export class EnrichSpanProcessor implements SpanProcessor {
10
+ private _spanAttributes:
11
+ | Record<string, SignalAttributeValue | (() => SignalAttributeValue)>
12
+ | undefined;
13
+
14
+ constructor(
15
+ spanAttributes?: Record<
16
+ string,
17
+ SignalAttributeValue | (() => SignalAttributeValue)
18
+ >,
19
+ ) {
20
+ this._spanAttributes = spanAttributes;
21
+ }
22
+
23
+ forceFlush(): Promise<void> {
24
+ return Promise.resolve();
25
+ }
26
+ onStart(span: Span, _context: Context): void {
27
+ if (this._spanAttributes != undefined) {
28
+ for (const [key, value] of Object.entries(this._spanAttributes)) {
29
+ span.setAttribute(key, typeof value === "function" ? value() : value);
30
+ }
31
+ }
32
+ }
33
+ onEnd(_span: ReadableSpan): void {
34
+ return;
35
+ }
36
+ shutdown(): Promise<void> {
37
+ return Promise.resolve();
38
+ }
39
+ }