@ogcio/o11y-sdk-node 0.4.2 → 0.6.1
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/dist/sdk-core/index.d.ts +1 -0
- package/dist/sdk-core/index.js +1 -0
- package/dist/sdk-core/lib/index.d.ts +2 -0
- package/dist/sdk-core/lib/index.js +2 -0
- package/dist/sdk-core/lib/redaction/basic-redactor.d.ts +7 -0
- package/dist/sdk-core/lib/redaction/basic-redactor.js +18 -0
- package/dist/sdk-core/lib/redaction/email-redactor.d.ts +8 -0
- package/dist/sdk-core/lib/redaction/email-redactor.js +17 -0
- package/dist/sdk-core/lib/redaction/index.d.ts +9 -0
- package/dist/sdk-core/lib/redaction/index.js +4 -0
- package/dist/sdk-core/lib/redaction/ip-redactor.d.ts +9 -0
- package/dist/sdk-core/lib/redaction/ip-redactor.js +23 -0
- package/dist/sdk-core/lib/redaction/ppsn-redactor.d.ts +8 -0
- package/dist/sdk-core/lib/redaction/ppsn-redactor.js +17 -0
- package/dist/sdk-core/lib/utils/data-structures.d.ts +15 -0
- package/dist/{lib/internals/redaction/pii-detection.js → sdk-core/lib/utils/data-structures.js} +4 -27
- package/dist/sdk-core/lib/utils/index.d.ts +2 -0
- package/dist/sdk-core/lib/utils/index.js +2 -0
- package/dist/sdk-core/lib/utils/string-decoding.d.ts +7 -0
- package/dist/sdk-core/lib/utils/string-decoding.js +22 -0
- package/dist/{index.d.ts → sdk-node/index.d.ts} +1 -0
- package/dist/{index.js → sdk-node/index.js} +1 -0
- package/dist/{lib → sdk-node/lib}/config-manager.d.ts +1 -1
- package/dist/sdk-node/lib/exporter/console.d.ts +3 -0
- package/dist/sdk-node/lib/exporter/grpc.d.ts +3 -0
- package/dist/sdk-node/lib/exporter/http.d.ts +3 -0
- package/dist/{lib → sdk-node/lib}/exporter/index.d.ts +2 -2
- package/dist/{lib → sdk-node/lib}/exporter/pii-exporter-decorator.d.ts +5 -5
- package/dist/{lib → sdk-node/lib}/exporter/pii-exporter-decorator.js +6 -2
- package/dist/sdk-node/lib/exporter/processor-config.d.ts +7 -0
- package/dist/{lib → sdk-node/lib}/exporter/processor-config.js +4 -0
- package/dist/{lib → sdk-node/lib}/index.d.ts +11 -0
- package/dist/{lib → sdk-node/lib}/instrumentation.node.js +1 -1
- package/dist/sdk-node/lib/internals/redaction/redactors/email.d.ts +8 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/email.js +19 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/index.d.ts +16 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/index.js +13 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ip.d.ts +8 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ip.js +20 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ppsn.d.ts +8 -0
- package/dist/sdk-node/lib/internals/redaction/redactors/ppsn.js +18 -0
- package/dist/{lib → sdk-node/lib}/metrics.d.ts +1 -1
- package/dist/{lib → sdk-node/lib}/processor/enrich-logger-processor.d.ts +3 -3
- package/dist/{lib → sdk-node/lib}/processor/enrich-span-processor.d.ts +3 -3
- package/dist/sdk-node/lib/processor/nextjs-logger-processor.d.ts +7 -0
- package/dist/sdk-node/lib/processor/nextjs-logger-processor.js +30 -0
- package/dist/sdk-node/lib/processor/nextjs-span-processor.d.ts +8 -0
- package/dist/sdk-node/lib/processor/nextjs-span-processor.js +25 -0
- package/dist/{lib → sdk-node/lib}/resource.d.ts +2 -2
- package/dist/{lib → sdk-node/lib}/traces.d.ts +1 -1
- package/dist/{lib → sdk-node/lib}/traces.js +1 -1
- package/dist/{lib → sdk-node/lib}/url-sampler.d.ts +3 -3
- package/dist/{lib → sdk-node/lib}/utils.d.ts +1 -1
- package/dist/sdk-node/package.json +62 -0
- package/package.json +28 -25
- package/CHANGELOG.md +0 -233
- package/dist/lib/exporter/console.d.ts +0 -3
- package/dist/lib/exporter/grpc.d.ts +0 -3
- package/dist/lib/exporter/http.d.ts +0 -3
- package/dist/lib/exporter/processor-config.d.ts +0 -5
- package/dist/lib/internals/redaction/pii-detection.d.ts +0 -25
- package/dist/lib/internals/redaction/redactors/email.d.ts +0 -8
- package/dist/lib/internals/redaction/redactors/email.js +0 -48
- package/dist/lib/internals/redaction/redactors/index.d.ts +0 -4
- package/dist/lib/internals/redaction/redactors/index.js +0 -6
- package/dist/lib/internals/redaction/redactors/ip.d.ts +0 -10
- package/dist/lib/internals/redaction/redactors/ip.js +0 -54
- package/dist/lib/internals/shared-metrics.d.ts +0 -7
- package/dist/lib/internals/shared-metrics.js +0 -18
- package/dist/package.json +0 -59
- package/dist/vitest.config.d.ts +0 -2
- package/dist/vitest.config.js +0 -45
- package/index.ts +0 -9
- package/lib/config-manager.ts +0 -12
- package/lib/exporter/console.ts +0 -33
- package/lib/exporter/grpc.ts +0 -65
- package/lib/exporter/http.ts +0 -56
- package/lib/exporter/index.ts +0 -9
- package/lib/exporter/pii-exporter-decorator.ts +0 -187
- package/lib/exporter/processor-config.ts +0 -23
- package/lib/index.ts +0 -118
- package/lib/instrumentation.node.ts +0 -115
- package/lib/internals/hooks.ts +0 -14
- package/lib/internals/redaction/pii-detection.ts +0 -113
- package/lib/internals/redaction/redactors/email.ts +0 -58
- package/lib/internals/redaction/redactors/index.ts +0 -12
- package/lib/internals/redaction/redactors/ip.ts +0 -68
- package/lib/internals/shared-metrics.ts +0 -34
- package/lib/metrics.ts +0 -75
- package/lib/processor/enrich-logger-processor.ts +0 -34
- package/lib/processor/enrich-span-processor.ts +0 -39
- package/lib/resource.ts +0 -30
- package/lib/traces.ts +0 -78
- package/lib/url-sampler.ts +0 -52
- package/lib/utils.ts +0 -22
- package/test/config-manager.test.ts +0 -34
- package/test/exporter/pii-exporter-decorator.test.ts +0 -88
- package/test/index.test.ts +0 -70
- package/test/integration/README.md +0 -74
- package/test/integration/docker-utils.sh +0 -214
- package/test/integration/main.sh +0 -52
- package/test/integration/teardown.sh +0 -7
- package/test/integration/test_fastify-o11y-pii-enabled/http-tracing.integration.test.ts +0 -56
- package/test/integration/test_fastify-o11y-pii-enabled/pii.integration.test.ts +0 -68
- package/test/integration/test_fastify-o11y-pii-enabled/run.sh +0 -42
- package/test/integration/test_without-o11y/run.sh +0 -30
- package/test/integration/test_without-o11y/verify-status.integration.test.ts +0 -32
- package/test/internals/hooks.test.ts +0 -45
- package/test/internals/pii-detection.test.ts +0 -265
- package/test/internals/redactors/email.test.ts +0 -81
- package/test/internals/redactors/ip.test.ts +0 -93
- package/test/internals/shared-metrics.test.ts +0 -34
- package/test/metrics.test.ts +0 -142
- package/test/node-config.test.ts +0 -190
- package/test/processor/enrich-logger-processor.test.ts +0 -58
- package/test/processor/enrich-span-processor.test.ts +0 -52
- package/test/resource.test.ts +0 -33
- package/test/traces/active-span.test.ts +0 -26
- package/test/traces/with-span.test.ts +0 -356
- package/test/url-sampler.test.ts +0 -215
- package/test/utils/alloy-log-parser.ts +0 -53
- package/test/utils/mock-signals.ts +0 -144
- package/test/validation.test.ts +0 -103
- package/tsconfig.json +0 -15
- package/vitest.config.ts +0 -46
- /package/dist/{lib → sdk-node/lib}/config-manager.js +0 -0
- /package/dist/{lib → sdk-node/lib}/exporter/console.js +0 -0
- /package/dist/{lib → sdk-node/lib}/exporter/grpc.js +0 -0
- /package/dist/{lib → sdk-node/lib}/exporter/http.js +0 -0
- /package/dist/{lib → sdk-node/lib}/exporter/index.js +0 -0
- /package/dist/{lib → sdk-node/lib}/index.js +0 -0
- /package/dist/{lib → sdk-node/lib}/instrumentation.node.d.ts +0 -0
- /package/dist/{lib → sdk-node/lib}/internals/hooks.d.ts +0 -0
- /package/dist/{lib → sdk-node/lib}/internals/hooks.js +0 -0
- /package/dist/{lib → sdk-node/lib}/metrics.js +0 -0
- /package/dist/{lib → sdk-node/lib}/processor/enrich-logger-processor.js +0 -0
- /package/dist/{lib → sdk-node/lib}/processor/enrich-span-processor.js +0 -0
- /package/dist/{lib → sdk-node/lib}/resource.js +0 -0
- /package/dist/{lib → sdk-node/lib}/url-sampler.js +0 -0
- /package/dist/{lib → sdk-node/lib}/utils.js +0 -0
package/lib/index.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import type { Metadata } from "@grpc/grpc-js";
|
|
2
|
-
|
|
3
|
-
export interface NodeSDKConfig {
|
|
4
|
-
/**
|
|
5
|
-
* The opentelemetry collector entrypoint GRPC url.
|
|
6
|
-
* If the collectorUrl is null or undefined, the instrumentation will not be activated.
|
|
7
|
-
* @example http://alloy:4317
|
|
8
|
-
*/
|
|
9
|
-
collectorUrl: string;
|
|
10
|
-
/**
|
|
11
|
-
* Name of your application used for the collector to group logs and naming traces
|
|
12
|
-
*/
|
|
13
|
-
serviceName?: string;
|
|
14
|
-
/**
|
|
15
|
-
* Version of your application used for the collector to group logs and naming traces
|
|
16
|
-
*/
|
|
17
|
-
serviceVersion?: string;
|
|
18
|
-
/**
|
|
19
|
-
* Diagnostic log level for the internal runtime instrumentation
|
|
20
|
-
*
|
|
21
|
-
* @type string
|
|
22
|
-
* @default INFO
|
|
23
|
-
*/
|
|
24
|
-
diagLogLevel?: SDKLogLevel;
|
|
25
|
-
/**
|
|
26
|
-
* Collector signals processing mode.
|
|
27
|
-
* single: makes an http/grpc request for each signal, and it is immediately processed inside grafana
|
|
28
|
-
* batch: sends multiple signals within a time window, optimized to reduce http/grpc calls in production
|
|
29
|
-
*
|
|
30
|
-
* @type string
|
|
31
|
-
* @default batch
|
|
32
|
-
*/
|
|
33
|
-
collectorMode?: SDKCollectorMode;
|
|
34
|
-
/**
|
|
35
|
-
* Array of not traced urls.
|
|
36
|
-
*
|
|
37
|
-
* @type {SamplerCondition}
|
|
38
|
-
* @default []
|
|
39
|
-
*/
|
|
40
|
-
ignoreUrls?: SamplerCondition[];
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Object containing static properties or functions used to evaluate custom attributes for all logs and traces.
|
|
44
|
-
*/
|
|
45
|
-
spanAttributes?: Record<
|
|
46
|
-
string,
|
|
47
|
-
SignalAttributeValue | (() => SignalAttributeValue)
|
|
48
|
-
>;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Object containing static properties used as resources attributes for the Node SDK initialization.
|
|
52
|
-
*/
|
|
53
|
-
resourceAttributes?: Record<string, SignalAttributeValue>;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Faction value from 0 to 1, used by TraceIdRatioBasedSampler which it deterministically samples a percentage of traces that you pass in as a parameter.
|
|
57
|
-
*
|
|
58
|
-
* @default 1
|
|
59
|
-
*/
|
|
60
|
-
traceRatio?: number;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Flag to enable or disable the tracing for node:fs module
|
|
64
|
-
*
|
|
65
|
-
* @default false disabling `instrumentation-fs` because it bloating the traces
|
|
66
|
-
*/
|
|
67
|
-
enableFS?: boolean;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Protocol used to send signals.
|
|
71
|
-
*
|
|
72
|
-
* @default grpc
|
|
73
|
-
*/
|
|
74
|
-
protocol?: SDKProtocol;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Grpc Metadata for the grpc-js client.
|
|
78
|
-
*
|
|
79
|
-
* @default { waitForReady: true }
|
|
80
|
-
*/
|
|
81
|
-
grpcMetadata?: Metadata;
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Enable/Disable PII detection for GDPR data
|
|
85
|
-
*/
|
|
86
|
-
detection?: {
|
|
87
|
-
/**
|
|
88
|
-
* Redact email address
|
|
89
|
-
* @default true
|
|
90
|
-
*/
|
|
91
|
-
email?: boolean;
|
|
92
|
-
/**
|
|
93
|
-
* Redact IPv4/IPv6 addresses
|
|
94
|
-
* @default true
|
|
95
|
-
*/
|
|
96
|
-
ip?: boolean;
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export interface SamplerCondition {
|
|
101
|
-
type: "endsWith" | "includes" | "equals";
|
|
102
|
-
url: string;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export type SignalAttributeValue = string | number | boolean;
|
|
106
|
-
|
|
107
|
-
export type SDKCollectorMode = "single" | "batch";
|
|
108
|
-
|
|
109
|
-
export type SDKProtocol = "grpc" | "http" | "console";
|
|
110
|
-
|
|
111
|
-
export type SDKLogLevel =
|
|
112
|
-
| "NONE"
|
|
113
|
-
| "ERROR"
|
|
114
|
-
| "WARN"
|
|
115
|
-
| "INFO"
|
|
116
|
-
| "DEBUG"
|
|
117
|
-
| "VERBOSE"
|
|
118
|
-
| "ALL";
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api";
|
|
2
|
-
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
|
|
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 { setNodeSdkConfig } from "./config-manager.js";
|
|
11
|
-
import buildConsoleExporters from "./exporter/console.js";
|
|
12
|
-
import buildGrpcExporters from "./exporter/grpc.js";
|
|
13
|
-
import buildHttpExporters from "./exporter/http.js";
|
|
14
|
-
import type { Exporters } from "./exporter/index.js";
|
|
15
|
-
import type { NodeSDKConfig } from "./index.js";
|
|
16
|
-
import { _shutdownHook } from "./internals/hooks.js";
|
|
17
|
-
import { ObservabilityResourceDetector } from "./resource.js";
|
|
18
|
-
import { UrlSampler } from "./url-sampler.js";
|
|
19
|
-
|
|
20
|
-
export default async function buildNodeInstrumentation(
|
|
21
|
-
config?: NodeSDKConfig,
|
|
22
|
-
): Promise<NodeSDK | undefined> {
|
|
23
|
-
if (!config) {
|
|
24
|
-
console.warn(
|
|
25
|
-
"observability config not set. Skipping NodeJS OpenTelemetry instrumentation.",
|
|
26
|
-
);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!config.collectorUrl) {
|
|
31
|
-
console.warn(
|
|
32
|
-
"collectorUrl not set. Skipping NodeJS OpenTelemetry instrumentation.",
|
|
33
|
-
);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (!isUrl(config.collectorUrl)) {
|
|
38
|
-
console.error(
|
|
39
|
-
"collectorUrl does not use a valid format. Skipping NodeJS OpenTelemetry instrumentation.",
|
|
40
|
-
);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Init configManager to make it available to all o11y utils.
|
|
45
|
-
setNodeSdkConfig(config);
|
|
46
|
-
|
|
47
|
-
const urlSampler = new UrlSampler(
|
|
48
|
-
config.ignoreUrls,
|
|
49
|
-
new TraceIdRatioBasedSampler(config.traceRatio ?? 1),
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
const mainSampler = new ParentBasedSampler({
|
|
53
|
-
root: urlSampler,
|
|
54
|
-
remoteParentSampled: urlSampler,
|
|
55
|
-
remoteParentNotSampled: new AlwaysOffSampler(),
|
|
56
|
-
localParentSampled: urlSampler,
|
|
57
|
-
localParentNotSampled: new AlwaysOffSampler(),
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
diag.setLogger(
|
|
61
|
-
new DiagConsoleLogger(),
|
|
62
|
-
config.diagLogLevel ? DiagLogLevel[config.diagLogLevel] : DiagLogLevel.INFO,
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
const nodeSdkInstrumentation = getNodeAutoInstrumentations({
|
|
67
|
-
"@opentelemetry/instrumentation-fs": {
|
|
68
|
-
enabled: config.enableFS ?? false,
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
let exporter: Exporters;
|
|
73
|
-
|
|
74
|
-
if (config.protocol === "http") {
|
|
75
|
-
exporter = buildHttpExporters(config);
|
|
76
|
-
} else if (config.protocol === "console") {
|
|
77
|
-
exporter = buildConsoleExporters(config);
|
|
78
|
-
} else {
|
|
79
|
-
exporter = await buildGrpcExporters(config);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const sdk = new NodeSDK({
|
|
83
|
-
resourceDetectors: [
|
|
84
|
-
new ObservabilityResourceDetector(config.resourceAttributes),
|
|
85
|
-
],
|
|
86
|
-
spanProcessors: exporter.spans,
|
|
87
|
-
serviceName: config.serviceName,
|
|
88
|
-
metricReader: exporter.metrics,
|
|
89
|
-
logRecordProcessors: exporter.logs,
|
|
90
|
-
sampler: mainSampler,
|
|
91
|
-
textMapPropagator: new W3CTraceContextPropagator(),
|
|
92
|
-
instrumentations: [nodeSdkInstrumentation],
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
sdk.start();
|
|
96
|
-
console.log("NodeJS OpenTelemetry instrumentation started successfully.");
|
|
97
|
-
|
|
98
|
-
_shutdownHook(sdk);
|
|
99
|
-
return sdk;
|
|
100
|
-
} catch (error) {
|
|
101
|
-
console.error(
|
|
102
|
-
"Error starting NodeJS OpenTelemetry instrumentation:",
|
|
103
|
-
error,
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function isUrl(url: string): boolean {
|
|
109
|
-
try {
|
|
110
|
-
new URL(url);
|
|
111
|
-
return true;
|
|
112
|
-
} catch (_) {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
}
|
package/lib/internals/hooks.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export function _shutdownHook(sdk: { shutdown: () => Promise<void> }) {
|
|
2
|
-
process.on("SIGTERM", async () => {
|
|
3
|
-
try {
|
|
4
|
-
// Flushing before shutdown is implemented on a per-exporter basis.
|
|
5
|
-
await sdk.shutdown();
|
|
6
|
-
console.log("NodeJS OpenTelemetry instrumentation shutdown successfully");
|
|
7
|
-
} catch (error) {
|
|
8
|
-
console.error(
|
|
9
|
-
"Error shutting down NodeJS OpenTelemetry instrumentation:",
|
|
10
|
-
error,
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import type { AnyValue, AnyValueMap } from "@opentelemetry/api-logs";
|
|
2
|
-
import { Redactor } from "./redactors/index.js";
|
|
3
|
-
|
|
4
|
-
const decoder = new TextDecoder();
|
|
5
|
-
const encoder = new TextEncoder();
|
|
6
|
-
|
|
7
|
-
export type PIISource = "trace" | "log" | "metric";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Checks whether a string contains URI-encoded components.
|
|
11
|
-
*
|
|
12
|
-
* @param {string} value - The string to inspect.
|
|
13
|
-
* @returns {boolean} `true` if the string is encoded, `false` otherwise.
|
|
14
|
-
*/
|
|
15
|
-
export function _containsEncodedComponents(value: string): boolean {
|
|
16
|
-
try {
|
|
17
|
-
const decodedURIComponent = decodeURIComponent(value);
|
|
18
|
-
if (decodeURI(value) !== decodedURIComponent) {
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (value !== decodedURIComponent) {
|
|
23
|
-
return (
|
|
24
|
-
encodeURIComponent(decodedURIComponent) === value ||
|
|
25
|
-
encodeURI(decodedURIComponent) === value
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
} catch {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Cleans a string by redacting configured PIIs and emitting metrics for redacted values.
|
|
37
|
-
*
|
|
38
|
-
* If the string is URL-encoded, it will be decoded before redaction.
|
|
39
|
-
*
|
|
40
|
-
* @template T
|
|
41
|
-
*
|
|
42
|
-
* @param {string} value - The input value to sanitize.
|
|
43
|
-
* @param {"trace" | "log"} source - The source context of the input, used in metrics.
|
|
44
|
-
* @param {Redactor[]} redactors - The string processors containing the redaction logic.
|
|
45
|
-
*
|
|
46
|
-
* @returns {string} The cleaned string with any configured PII replaced by `[REDACTED PII_TYPE]`.
|
|
47
|
-
*/
|
|
48
|
-
export function _cleanStringPII(
|
|
49
|
-
value: string,
|
|
50
|
-
source: PIISource,
|
|
51
|
-
redactors: Redactor[],
|
|
52
|
-
): string {
|
|
53
|
-
if (typeof value !== "string") {
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
let kind: "string" | "url" = "string";
|
|
58
|
-
let decodedValue: string = value;
|
|
59
|
-
|
|
60
|
-
if (_containsEncodedComponents(value)) {
|
|
61
|
-
decodedValue = decodeURIComponent(value);
|
|
62
|
-
kind = "url";
|
|
63
|
-
}
|
|
64
|
-
return redactors.reduce(
|
|
65
|
-
(redactedValue: string, currentRedactor): string =>
|
|
66
|
-
currentRedactor(redactedValue, source, kind),
|
|
67
|
-
decodedValue,
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function _recursiveObjectClean<T extends AnyValue>(
|
|
72
|
-
value: T,
|
|
73
|
-
source: PIISource,
|
|
74
|
-
redactors: Redactor[],
|
|
75
|
-
): T {
|
|
76
|
-
if (typeof value === "string") {
|
|
77
|
-
return _cleanStringPII(value, source, redactors) as T;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (
|
|
81
|
-
typeof value === "number" ||
|
|
82
|
-
typeof value === "boolean" ||
|
|
83
|
-
value == null
|
|
84
|
-
) {
|
|
85
|
-
return value;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (value instanceof Uint8Array) {
|
|
89
|
-
try {
|
|
90
|
-
const decoded = decoder.decode(value);
|
|
91
|
-
const sanitized = _cleanStringPII(decoded, source, redactors);
|
|
92
|
-
return encoder.encode(sanitized) as T;
|
|
93
|
-
} catch {
|
|
94
|
-
return value;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (Array.isArray(value)) {
|
|
99
|
-
return value.map((value) =>
|
|
100
|
-
_recursiveObjectClean(value, source, redactors),
|
|
101
|
-
) as T;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (typeof value === "object") {
|
|
105
|
-
const sanitized: AnyValueMap = {};
|
|
106
|
-
for (const [key, val] of Object.entries(value)) {
|
|
107
|
-
sanitized[key] = _recursiveObjectClean(val, source, redactors);
|
|
108
|
-
}
|
|
109
|
-
return sanitized as T;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return value;
|
|
113
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { _getPIICounterRedactionMetric } from "../../shared-metrics.js";
|
|
2
|
-
|
|
3
|
-
const EMAIL_REGEX = /[\p{L}\p{N}._%+-]+@((?:[\p{L}\p{N}-]+\.)+[\p{L}]{2,})/giu;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Redacts all email addresses in the input string and collects metadata.
|
|
7
|
-
*
|
|
8
|
-
* @param {string} value The input string potentially containing email addresses.
|
|
9
|
-
* @returns {{
|
|
10
|
-
* redacted: string,
|
|
11
|
-
* count: number,
|
|
12
|
-
* domains: Record<string, number>
|
|
13
|
-
* }}
|
|
14
|
-
*
|
|
15
|
-
* An object containing:
|
|
16
|
-
* - `redacted`: the string with email addresses replaced by `[REDACTED EMAIL]`
|
|
17
|
-
* - `count`: total number of email addresses redacted
|
|
18
|
-
* - `domains`: a map of domain names to the number of times they were redacted
|
|
19
|
-
*/
|
|
20
|
-
function _redactEmails(value: string): {
|
|
21
|
-
redacted: string;
|
|
22
|
-
count: number;
|
|
23
|
-
domains: Record<string, number>;
|
|
24
|
-
} {
|
|
25
|
-
let count = 0;
|
|
26
|
-
const domains: Record<string, number> = {};
|
|
27
|
-
|
|
28
|
-
const redacted = value.replace(EMAIL_REGEX, (_, domain) => {
|
|
29
|
-
count++;
|
|
30
|
-
domains[domain] = (domains[domain] || 0) + 1;
|
|
31
|
-
return "[REDACTED EMAIL]";
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return { redacted, count, domains };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Redacts provided input and collects metadata metrics about redacted email domains,
|
|
39
|
-
* data source and kind.
|
|
40
|
-
*
|
|
41
|
-
* @param {string} value The input string potentially containing email addresses.
|
|
42
|
-
* @returns {string} the redacted value
|
|
43
|
-
*/
|
|
44
|
-
export const emailRedactor = (value: string, source: string, kind: string) => {
|
|
45
|
-
const { redacted, count, domains } = _redactEmails(value);
|
|
46
|
-
|
|
47
|
-
if (count > 0) {
|
|
48
|
-
for (const [domain, domainCount] of Object.entries(domains)) {
|
|
49
|
-
_getPIICounterRedactionMetric().add(domainCount, {
|
|
50
|
-
pii_type: "email",
|
|
51
|
-
redaction_source: source,
|
|
52
|
-
pii_email_domain: domain,
|
|
53
|
-
pii_format: kind,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return redacted;
|
|
58
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { NodeSDKConfig } from "../../../index.js";
|
|
2
|
-
import { emailRedactor } from "./email.js";
|
|
3
|
-
import { ipRedactor } from "./ip.js";
|
|
4
|
-
|
|
5
|
-
export type Redactor = (value: string, source: string, kind: string) => string;
|
|
6
|
-
|
|
7
|
-
export type RedactorKeys = keyof NonNullable<NodeSDKConfig["detection"]>;
|
|
8
|
-
|
|
9
|
-
export const redactors: Record<RedactorKeys, Redactor> = {
|
|
10
|
-
email: emailRedactor,
|
|
11
|
-
ip: ipRedactor,
|
|
12
|
-
};
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { _getPIICounterRedactionMetric } from "../../shared-metrics.js";
|
|
2
|
-
|
|
3
|
-
// Generous IP address matchers (might match some invalid addresses like 192.168.01.1)
|
|
4
|
-
const IPV4_REGEX =
|
|
5
|
-
/(?<!\d)(?:%[0-9A-Fa-f]{2})?(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}(?:%[0-9A-Fa-f]{2})?(?!\d)/gi;
|
|
6
|
-
const IPV6_REGEX =
|
|
7
|
-
/(?<![0-9a-f:])(?:%[0-9A-Fa-f]{2})?((?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|:(?::[0-9A-Fa-f]{1,4}){1,7}|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?::[0-9A-Fa-f]{1,4}){1,6}|:(?::[0-9A-Fa-f]{1,4}){1,7}:?|(?:[0-9A-Fa-f]{1,4}:){1,4}:(?:25[0-5]|2[0-4]\d|1\d\d|\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|\d{1,2})){3})(?:%[0-9A-Fa-f]{2})?(?![0-9a-f:])/gi;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Redacts all ip addresses in the input string and collects metadata.
|
|
11
|
-
*
|
|
12
|
-
* @param {string} value The input string potentially containing ip addresses.
|
|
13
|
-
* @returns {{
|
|
14
|
-
* redacted: string,
|
|
15
|
-
* count: number,
|
|
16
|
-
* domains: Record<string, number>
|
|
17
|
-
* }}
|
|
18
|
-
*
|
|
19
|
-
* An object containing:
|
|
20
|
-
* - `redacted`: the string with IP addresses replaced by `[REDACTED IPV*]`
|
|
21
|
-
* - `counters`: total number of addresses redacted by IPv* type
|
|
22
|
-
* - `domains`: a map of domain names to the number of times they were redacted
|
|
23
|
-
*/
|
|
24
|
-
function _redactIps(value: string): {
|
|
25
|
-
redacted: string;
|
|
26
|
-
counters: Record<string, number>;
|
|
27
|
-
} {
|
|
28
|
-
const counters: Record<string, number> = {};
|
|
29
|
-
const redacted = value
|
|
30
|
-
.replace(IPV4_REGEX, () => {
|
|
31
|
-
counters["IPv4"] = (counters["IPv4"] || 0) + 1;
|
|
32
|
-
return "[REDACTED IPV4]";
|
|
33
|
-
})
|
|
34
|
-
.replace(IPV6_REGEX, () => {
|
|
35
|
-
counters["IPv4"] = (counters["IPv4"] || 0) + 1;
|
|
36
|
-
return "[REDACTED IPV6]";
|
|
37
|
-
});
|
|
38
|
-
return { redacted, counters };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Redacts provided input and collects metadata metrics about redacted IPs,
|
|
43
|
-
* data source and kind.
|
|
44
|
-
*
|
|
45
|
-
* @param {string} value The input string potentially containing IP addresses.
|
|
46
|
-
* @param {string} source The source of the attribute being redacted (log, span, metric).
|
|
47
|
-
* @param {string} kind The type of the data structure containing the PII
|
|
48
|
-
* @returns {string} the redacted value
|
|
49
|
-
*/
|
|
50
|
-
export const ipRedactor = (
|
|
51
|
-
value: string,
|
|
52
|
-
source: string,
|
|
53
|
-
kind: string,
|
|
54
|
-
): string => {
|
|
55
|
-
const { redacted, counters } = _redactIps(value);
|
|
56
|
-
|
|
57
|
-
Object.entries(counters).forEach(([type, counter]) => {
|
|
58
|
-
if (counter > 0) {
|
|
59
|
-
_getPIICounterRedactionMetric().add(counter, {
|
|
60
|
-
pii_type: type,
|
|
61
|
-
redaction_source: source,
|
|
62
|
-
pii_format: kind,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
return redacted;
|
|
68
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { Attributes, Counter } from "@opentelemetry/api";
|
|
2
|
-
import { getMetric } from "../metrics.js";
|
|
3
|
-
import { PIISource } from "./redaction/pii-detection.js";
|
|
4
|
-
|
|
5
|
-
interface RedactionMetric extends Attributes {
|
|
6
|
-
/** Type of PII redacted (e.g., "email", "phone"). */
|
|
7
|
-
pii_type: string;
|
|
8
|
-
/** Domain part of the redacted PII (e.g., "gmail.com"). */
|
|
9
|
-
pii_email_domain?: string;
|
|
10
|
-
/** Source of the redaction (trace, log or metric). */
|
|
11
|
-
redaction_source: PIISource;
|
|
12
|
-
/** Format or structure of the redacted value. */
|
|
13
|
-
pii_format: "string" | "url";
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// Cached singleton instance of the redaction counter metric
|
|
17
|
-
let _redactedCounter: undefined | Counter;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Returns a singleton OpenTelemetry counter metric used to record occurrences of PII redactions.
|
|
21
|
-
*
|
|
22
|
-
* @returns {Counter} The singleton OpenTelemetry counter metric for PII redactions.
|
|
23
|
-
*/
|
|
24
|
-
export function _getPIICounterRedactionMetric() {
|
|
25
|
-
if (_redactedCounter) {
|
|
26
|
-
return _redactedCounter;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
_redactedCounter = getMetric<"counter", RedactionMetric>("counter", {
|
|
30
|
-
meterName: "o11y",
|
|
31
|
-
metricName: "o11y_pii_redaction",
|
|
32
|
-
});
|
|
33
|
-
return _redactedCounter;
|
|
34
|
-
}
|
package/lib/metrics.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { SdkLogRecord, 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: SdkLogRecord, _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
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
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
|
-
}
|