@ogcio/o11y-sdk-node 0.4.2 → 0.6.0

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 (140) hide show
  1. package/dist/sdk-core/index.d.ts +1 -0
  2. package/dist/sdk-core/index.js +1 -0
  3. package/dist/sdk-core/lib/index.d.ts +2 -0
  4. package/dist/sdk-core/lib/index.js +2 -0
  5. package/dist/sdk-core/lib/redaction/basic-redactor.d.ts +7 -0
  6. package/dist/sdk-core/lib/redaction/basic-redactor.js +18 -0
  7. package/dist/sdk-core/lib/redaction/email-redactor.d.ts +8 -0
  8. package/dist/sdk-core/lib/redaction/email-redactor.js +17 -0
  9. package/dist/sdk-core/lib/redaction/index.d.ts +9 -0
  10. package/dist/sdk-core/lib/redaction/index.js +4 -0
  11. package/dist/sdk-core/lib/redaction/ip-redactor.d.ts +9 -0
  12. package/dist/sdk-core/lib/redaction/ip-redactor.js +23 -0
  13. package/dist/sdk-core/lib/redaction/ppsn-redactor.d.ts +8 -0
  14. package/dist/sdk-core/lib/redaction/ppsn-redactor.js +17 -0
  15. package/dist/sdk-core/lib/utils/data-structures.d.ts +15 -0
  16. package/dist/{lib/internals/redaction/pii-detection.js → sdk-core/lib/utils/data-structures.js} +4 -27
  17. package/dist/sdk-core/lib/utils/index.d.ts +2 -0
  18. package/dist/sdk-core/lib/utils/index.js +2 -0
  19. package/dist/sdk-core/lib/utils/string-decoding.d.ts +7 -0
  20. package/dist/sdk-core/lib/utils/string-decoding.js +22 -0
  21. package/dist/{index.d.ts → sdk-node/index.d.ts} +1 -0
  22. package/dist/{index.js → sdk-node/index.js} +1 -0
  23. package/dist/{lib → sdk-node/lib}/config-manager.d.ts +1 -1
  24. package/dist/sdk-node/lib/exporter/console.d.ts +3 -0
  25. package/dist/sdk-node/lib/exporter/grpc.d.ts +3 -0
  26. package/dist/sdk-node/lib/exporter/http.d.ts +3 -0
  27. package/dist/{lib → sdk-node/lib}/exporter/index.d.ts +2 -2
  28. package/dist/{lib → sdk-node/lib}/exporter/pii-exporter-decorator.d.ts +5 -5
  29. package/dist/{lib → sdk-node/lib}/exporter/pii-exporter-decorator.js +6 -2
  30. package/dist/sdk-node/lib/exporter/processor-config.d.ts +7 -0
  31. package/dist/{lib → sdk-node/lib}/exporter/processor-config.js +4 -0
  32. package/dist/{lib → sdk-node/lib}/index.d.ts +11 -0
  33. package/dist/{lib → sdk-node/lib}/instrumentation.node.js +1 -1
  34. package/dist/sdk-node/lib/internals/redaction/redactors/email.d.ts +8 -0
  35. package/dist/sdk-node/lib/internals/redaction/redactors/email.js +19 -0
  36. package/dist/sdk-node/lib/internals/redaction/redactors/index.d.ts +16 -0
  37. package/dist/sdk-node/lib/internals/redaction/redactors/index.js +13 -0
  38. package/dist/sdk-node/lib/internals/redaction/redactors/ip.d.ts +8 -0
  39. package/dist/sdk-node/lib/internals/redaction/redactors/ip.js +20 -0
  40. package/dist/sdk-node/lib/internals/redaction/redactors/ppsn.d.ts +8 -0
  41. package/dist/sdk-node/lib/internals/redaction/redactors/ppsn.js +18 -0
  42. package/dist/{lib → sdk-node/lib}/metrics.d.ts +1 -1
  43. package/dist/{lib → sdk-node/lib}/processor/enrich-logger-processor.d.ts +3 -3
  44. package/dist/{lib → sdk-node/lib}/processor/enrich-span-processor.d.ts +3 -3
  45. package/dist/sdk-node/lib/processor/nextjs-logger-processor.d.ts +7 -0
  46. package/dist/sdk-node/lib/processor/nextjs-logger-processor.js +30 -0
  47. package/dist/sdk-node/lib/processor/nextjs-span-processor.d.ts +8 -0
  48. package/dist/sdk-node/lib/processor/nextjs-span-processor.js +25 -0
  49. package/dist/{lib → sdk-node/lib}/resource.d.ts +2 -2
  50. package/dist/{lib → sdk-node/lib}/traces.d.ts +1 -1
  51. package/dist/{lib → sdk-node/lib}/traces.js +1 -1
  52. package/dist/{lib → sdk-node/lib}/url-sampler.d.ts +3 -3
  53. package/dist/{lib → sdk-node/lib}/utils.d.ts +1 -1
  54. package/dist/sdk-node/package.json +62 -0
  55. package/package.json +25 -22
  56. package/CHANGELOG.md +0 -233
  57. package/dist/lib/exporter/console.d.ts +0 -3
  58. package/dist/lib/exporter/grpc.d.ts +0 -3
  59. package/dist/lib/exporter/http.d.ts +0 -3
  60. package/dist/lib/exporter/processor-config.d.ts +0 -5
  61. package/dist/lib/internals/redaction/pii-detection.d.ts +0 -25
  62. package/dist/lib/internals/redaction/redactors/email.d.ts +0 -8
  63. package/dist/lib/internals/redaction/redactors/email.js +0 -48
  64. package/dist/lib/internals/redaction/redactors/index.d.ts +0 -4
  65. package/dist/lib/internals/redaction/redactors/index.js +0 -6
  66. package/dist/lib/internals/redaction/redactors/ip.d.ts +0 -10
  67. package/dist/lib/internals/redaction/redactors/ip.js +0 -54
  68. package/dist/lib/internals/shared-metrics.d.ts +0 -7
  69. package/dist/lib/internals/shared-metrics.js +0 -18
  70. package/dist/package.json +0 -59
  71. package/dist/vitest.config.d.ts +0 -2
  72. package/dist/vitest.config.js +0 -45
  73. package/index.ts +0 -9
  74. package/lib/config-manager.ts +0 -12
  75. package/lib/exporter/console.ts +0 -33
  76. package/lib/exporter/grpc.ts +0 -65
  77. package/lib/exporter/http.ts +0 -56
  78. package/lib/exporter/index.ts +0 -9
  79. package/lib/exporter/pii-exporter-decorator.ts +0 -187
  80. package/lib/exporter/processor-config.ts +0 -23
  81. package/lib/index.ts +0 -118
  82. package/lib/instrumentation.node.ts +0 -115
  83. package/lib/internals/hooks.ts +0 -14
  84. package/lib/internals/redaction/pii-detection.ts +0 -113
  85. package/lib/internals/redaction/redactors/email.ts +0 -58
  86. package/lib/internals/redaction/redactors/index.ts +0 -12
  87. package/lib/internals/redaction/redactors/ip.ts +0 -68
  88. package/lib/internals/shared-metrics.ts +0 -34
  89. package/lib/metrics.ts +0 -75
  90. package/lib/processor/enrich-logger-processor.ts +0 -34
  91. package/lib/processor/enrich-span-processor.ts +0 -39
  92. package/lib/resource.ts +0 -30
  93. package/lib/traces.ts +0 -78
  94. package/lib/url-sampler.ts +0 -52
  95. package/lib/utils.ts +0 -22
  96. package/test/config-manager.test.ts +0 -34
  97. package/test/exporter/pii-exporter-decorator.test.ts +0 -88
  98. package/test/index.test.ts +0 -70
  99. package/test/integration/README.md +0 -74
  100. package/test/integration/docker-utils.sh +0 -214
  101. package/test/integration/main.sh +0 -52
  102. package/test/integration/teardown.sh +0 -7
  103. package/test/integration/test_fastify-o11y-pii-enabled/http-tracing.integration.test.ts +0 -56
  104. package/test/integration/test_fastify-o11y-pii-enabled/pii.integration.test.ts +0 -68
  105. package/test/integration/test_fastify-o11y-pii-enabled/run.sh +0 -42
  106. package/test/integration/test_without-o11y/run.sh +0 -30
  107. package/test/integration/test_without-o11y/verify-status.integration.test.ts +0 -32
  108. package/test/internals/hooks.test.ts +0 -45
  109. package/test/internals/pii-detection.test.ts +0 -265
  110. package/test/internals/redactors/email.test.ts +0 -81
  111. package/test/internals/redactors/ip.test.ts +0 -93
  112. package/test/internals/shared-metrics.test.ts +0 -34
  113. package/test/metrics.test.ts +0 -142
  114. package/test/node-config.test.ts +0 -190
  115. package/test/processor/enrich-logger-processor.test.ts +0 -58
  116. package/test/processor/enrich-span-processor.test.ts +0 -52
  117. package/test/resource.test.ts +0 -33
  118. package/test/traces/active-span.test.ts +0 -26
  119. package/test/traces/with-span.test.ts +0 -356
  120. package/test/url-sampler.test.ts +0 -215
  121. package/test/utils/alloy-log-parser.ts +0 -53
  122. package/test/utils/mock-signals.ts +0 -144
  123. package/test/validation.test.ts +0 -103
  124. package/tsconfig.json +0 -15
  125. package/vitest.config.ts +0 -46
  126. /package/dist/{lib → sdk-node/lib}/config-manager.js +0 -0
  127. /package/dist/{lib → sdk-node/lib}/exporter/console.js +0 -0
  128. /package/dist/{lib → sdk-node/lib}/exporter/grpc.js +0 -0
  129. /package/dist/{lib → sdk-node/lib}/exporter/http.js +0 -0
  130. /package/dist/{lib → sdk-node/lib}/exporter/index.js +0 -0
  131. /package/dist/{lib → sdk-node/lib}/index.js +0 -0
  132. /package/dist/{lib → sdk-node/lib}/instrumentation.node.d.ts +0 -0
  133. /package/dist/{lib → sdk-node/lib}/internals/hooks.d.ts +0 -0
  134. /package/dist/{lib → sdk-node/lib}/internals/hooks.js +0 -0
  135. /package/dist/{lib → sdk-node/lib}/metrics.js +0 -0
  136. /package/dist/{lib → sdk-node/lib}/processor/enrich-logger-processor.js +0 -0
  137. /package/dist/{lib → sdk-node/lib}/processor/enrich-span-processor.js +0 -0
  138. /package/dist/{lib → sdk-node/lib}/resource.js +0 -0
  139. /package/dist/{lib → sdk-node/lib}/url-sampler.js +0 -0
  140. /package/dist/{lib → sdk-node/lib}/utils.js +0 -0
@@ -0,0 +1 @@
1
+ export * from "./lib/index.js";
@@ -0,0 +1 @@
1
+ export * from "./lib/index.js";
@@ -0,0 +1,2 @@
1
+ export * from "./redaction/index.js";
2
+ export * from "./utils/index.js";
@@ -0,0 +1,2 @@
1
+ export * from "./redaction/index.js";
2
+ export * from "./utils/index.js";
@@ -0,0 +1,7 @@
1
+ import type { StringKind, RedactionResult } from "./index.js";
2
+ export declare abstract class BasicRedactor {
3
+ constructor();
4
+ process(value: string, source: string, kind: StringKind): string;
5
+ protected redact(_value: string, _source: string, _kind: StringKind): RedactionResult<Record<string, unknown>>;
6
+ protected exportMetrics(_context: RedactionResult<Record<string, unknown>>["context"], _source: string, _kind: StringKind): void;
7
+ }
@@ -0,0 +1,18 @@
1
+ export class BasicRedactor {
2
+ constructor() { }
3
+ process(value, source, kind) {
4
+ const { redactedValue, context } = this.redact(value, source, kind);
5
+ this.exportMetrics(context, source, kind);
6
+ return redactedValue;
7
+ }
8
+ redact(_value, _source, _kind) {
9
+ // no-op by default
10
+ return {
11
+ redactedValue: _value,
12
+ context: {},
13
+ };
14
+ }
15
+ exportMetrics(_context, _source, _kind) {
16
+ // no-op by default
17
+ }
18
+ }
@@ -0,0 +1,8 @@
1
+ import { BasicRedactor } from "./basic-redactor.js";
2
+ import type { RedactionResult } from "./index.js";
3
+ export declare class EmailRedactor extends BasicRedactor {
4
+ private static readonly EMAIL_REGEX;
5
+ protected redact(value: string): RedactionResult<{
6
+ domains: Record<string, number>;
7
+ }>;
8
+ }
@@ -0,0 +1,17 @@
1
+ import { BasicRedactor } from "./basic-redactor.js";
2
+ export class EmailRedactor extends BasicRedactor {
3
+ static EMAIL_REGEX = /[\p{L}\p{N}._%+-]+@((?:[\p{L}\p{N}-]+\.)+[\p{L}]{2,})/giu;
4
+ redact(value) {
5
+ const domains = {};
6
+ const redactedValue = value.replace(EmailRedactor.EMAIL_REGEX, (_, domain) => {
7
+ domains[domain] = (domains[domain] || 0) + 1;
8
+ return "[REDACTED EMAIL]";
9
+ });
10
+ return {
11
+ redactedValue: redactedValue,
12
+ context: {
13
+ domains,
14
+ },
15
+ };
16
+ }
17
+ }
@@ -0,0 +1,9 @@
1
+ export interface RedactionResult<T extends Record<string, unknown>> {
2
+ redactedValue: string;
3
+ context: T;
4
+ }
5
+ export type StringKind = "string" | "url";
6
+ export { BasicRedactor } from "./basic-redactor.js";
7
+ export { EmailRedactor } from "./email-redactor.js";
8
+ export { IpRedactor } from "./ip-redactor.js";
9
+ export { PpsnRedactor } from "./ppsn-redactor.js";
@@ -0,0 +1,4 @@
1
+ export { BasicRedactor } from "./basic-redactor.js";
2
+ export { EmailRedactor } from "./email-redactor.js";
3
+ export { IpRedactor } from "./ip-redactor.js";
4
+ export { PpsnRedactor } from "./ppsn-redactor.js";
@@ -0,0 +1,9 @@
1
+ import { BasicRedactor } from "./basic-redactor.js";
2
+ import type { RedactionResult } from "./index.js";
3
+ export declare class IpRedactor extends BasicRedactor {
4
+ private static readonly IPV4_REGEX;
5
+ private static readonly IPV6_REGEX;
6
+ protected redact(value: string): RedactionResult<{
7
+ counters: Record<string, number>;
8
+ }>;
9
+ }
@@ -0,0 +1,23 @@
1
+ import { BasicRedactor } from "./basic-redactor.js";
2
+ export class IpRedactor extends BasicRedactor {
3
+ static IPV4_REGEX = /(?<!\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;
4
+ static IPV6_REGEX = /(?<![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;
5
+ redact(value) {
6
+ const counters = {};
7
+ const redactedValue = value
8
+ .replace(IpRedactor.IPV4_REGEX, () => {
9
+ counters["IPv4"] = (counters["IPv4"] || 0) + 1;
10
+ return "[REDACTED IPV4]";
11
+ })
12
+ .replace(IpRedactor.IPV6_REGEX, () => {
13
+ counters["IPv6"] = (counters["IPv6"] || 0) + 1;
14
+ return "[REDACTED IPV6]";
15
+ });
16
+ return {
17
+ redactedValue: redactedValue,
18
+ context: {
19
+ counters,
20
+ },
21
+ };
22
+ }
23
+ }
@@ -0,0 +1,8 @@
1
+ import { BasicRedactor } from "./basic-redactor.js";
2
+ import type { RedactionResult } from "./index.js";
3
+ export declare class PpsnRedactor extends BasicRedactor {
4
+ private static readonly PPSN_REGEX;
5
+ protected redact(value: string): RedactionResult<{
6
+ redactedCounter: number;
7
+ }>;
8
+ }
@@ -0,0 +1,17 @@
1
+ import { BasicRedactor } from "./basic-redactor.js";
2
+ export class PpsnRedactor extends BasicRedactor {
3
+ static PPSN_REGEX = /(?<!([0-9]|[a-z]))[0-9]{7}[a-z]{1,2}(?!([0-9]|[a-z]))/gi;
4
+ redact(value) {
5
+ let redactedCounter = 0;
6
+ const redactedValue = value.replace(PpsnRedactor.PPSN_REGEX, (_) => {
7
+ redactedCounter++;
8
+ return "[REDACTED PPSN]";
9
+ });
10
+ return {
11
+ redactedValue: redactedValue,
12
+ context: {
13
+ redactedCounter,
14
+ },
15
+ };
16
+ }
17
+ }
@@ -0,0 +1,15 @@
1
+ import type { AnyValue } from "@opentelemetry/api-logs";
2
+ import { BasicRedactor } from "../redaction/index.js";
3
+ /**
4
+ * Cleans a string by redacting configured PIIs and emitting metrics for redacted values.
5
+ *
6
+ * If the string is URL-encoded, it will be decoded before redaction.
7
+ *
8
+ * @param {string} value - The input value to sanitize.
9
+ * @param {string} source - The source context of the input, used in metrics.
10
+ * @param {BasicRedactor[]} redactors - The string redactors containing the redaction logic.
11
+ *
12
+ * @returns {string} The cleaned string with any configured PII replaced by `[REDACTED PII_TYPE]`.
13
+ */
14
+ export declare function _cleanStringPII<T extends string>(value: string, source: T, redactors: BasicRedactor[]): string;
15
+ export declare function _recursiveObjectClean<T extends AnyValue, R extends string>(value: T, source: R, redactors: BasicRedactor[]): T;
@@ -1,37 +1,14 @@
1
+ import { _containsEncodedComponents } from "./string-decoding.js";
1
2
  const decoder = new TextDecoder();
2
3
  const encoder = new TextEncoder();
3
- /**
4
- * Checks whether a string contains URI-encoded components.
5
- *
6
- * @param {string} value - The string to inspect.
7
- * @returns {boolean} `true` if the string is encoded, `false` otherwise.
8
- */
9
- export function _containsEncodedComponents(value) {
10
- try {
11
- const decodedURIComponent = decodeURIComponent(value);
12
- if (decodeURI(value) !== decodedURIComponent) {
13
- return true;
14
- }
15
- if (value !== decodedURIComponent) {
16
- return (encodeURIComponent(decodedURIComponent) === value ||
17
- encodeURI(decodedURIComponent) === value);
18
- }
19
- }
20
- catch {
21
- return false;
22
- }
23
- return false;
24
- }
25
4
  /**
26
5
  * Cleans a string by redacting configured PIIs and emitting metrics for redacted values.
27
6
  *
28
7
  * If the string is URL-encoded, it will be decoded before redaction.
29
8
  *
30
- * @template T
31
- *
32
9
  * @param {string} value - The input value to sanitize.
33
- * @param {"trace" | "log"} source - The source context of the input, used in metrics.
34
- * @param {Redactor[]} redactors - The string processors containing the redaction logic.
10
+ * @param {string} source - The source context of the input, used in metrics.
11
+ * @param {BasicRedactor[]} redactors - The string redactors containing the redaction logic.
35
12
  *
36
13
  * @returns {string} The cleaned string with any configured PII replaced by `[REDACTED PII_TYPE]`.
37
14
  */
@@ -45,7 +22,7 @@ export function _cleanStringPII(value, source, redactors) {
45
22
  decodedValue = decodeURIComponent(value);
46
23
  kind = "url";
47
24
  }
48
- return redactors.reduce((redactedValue, currentRedactor) => currentRedactor(redactedValue, source, kind), decodedValue);
25
+ return redactors.reduce((redactedValue, currentRedactor) => currentRedactor.process(redactedValue, source, kind), decodedValue);
49
26
  }
50
27
  export function _recursiveObjectClean(value, source, redactors) {
51
28
  if (typeof value === "string") {
@@ -0,0 +1,2 @@
1
+ export { _cleanStringPII, _recursiveObjectClean } from "./data-structures.js";
2
+ export { _containsEncodedComponents } from "./string-decoding.js";
@@ -0,0 +1,2 @@
1
+ export { _cleanStringPII, _recursiveObjectClean } from "./data-structures.js";
2
+ export { _containsEncodedComponents } from "./string-decoding.js";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Checks whether a string contains URI-encoded components.
3
+ *
4
+ * @param {string} value - The string to inspect.
5
+ * @returns {boolean} `true` if the string is encoded, `false` otherwise.
6
+ */
7
+ export declare function _containsEncodedComponents(value: string): boolean;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Checks whether a string contains URI-encoded components.
3
+ *
4
+ * @param {string} value - The string to inspect.
5
+ * @returns {boolean} `true` if the string is encoded, `false` otherwise.
6
+ */
7
+ export function _containsEncodedComponents(value) {
8
+ try {
9
+ const decodedURIComponent = decodeURIComponent(value);
10
+ if (decodeURI(value) !== decodedURIComponent) {
11
+ return true;
12
+ }
13
+ if (value !== decodedURIComponent) {
14
+ return (encodeURIComponent(decodedURIComponent) === value ||
15
+ encodeURI(decodedURIComponent) === value);
16
+ }
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ return false;
22
+ }
@@ -5,3 +5,4 @@ export type { NodeSDK };
5
5
  export { buildNodeInstrumentation as instrumentNode };
6
6
  export * from "./lib/metrics.js";
7
7
  export * from "./lib/traces.js";
8
+ export * from "../sdk-core/index.js";
@@ -2,3 +2,4 @@ import buildNodeInstrumentation from "./lib/instrumentation.node.js";
2
2
  export { buildNodeInstrumentation as instrumentNode };
3
3
  export * from "./lib/metrics.js";
4
4
  export * from "./lib/traces.js";
5
+ export * from "../sdk-core/index.js";
@@ -1,3 +1,3 @@
1
- import { NodeSDKConfig } from "./index.js";
1
+ import type { NodeSDKConfig } from "./index.js";
2
2
  export declare const setNodeSdkConfig: (config: NodeSDKConfig) => void;
3
3
  export declare const getNodeSdkConfig: () => NodeSDKConfig | undefined;
@@ -0,0 +1,3 @@
1
+ import type { NodeSDKConfig } from "../index.js";
2
+ import type { Exporters } from "./index.js";
3
+ export default function buildConsoleExporters(config: NodeSDKConfig): Exporters;
@@ -0,0 +1,3 @@
1
+ import type { NodeSDKConfig } from "../index.js";
2
+ import type { Exporters } from "./index.js";
3
+ export default function buildGrpcExporters(config: NodeSDKConfig): Promise<Exporters>;
@@ -0,0 +1,3 @@
1
+ import type { NodeSDKConfig } from "../index.js";
2
+ import type { Exporters } from "./index.js";
3
+ export default function buildHttpExporters(config: NodeSDKConfig): Exporters;
@@ -1,6 +1,6 @@
1
- import { LogRecordProcessor } from "@opentelemetry/sdk-logs";
1
+ import type { LogRecordProcessor } from "@opentelemetry/sdk-logs";
2
2
  import type { metrics } from "@opentelemetry/sdk-node";
3
- import { SpanProcessor } from "@opentelemetry/sdk-trace-base";
3
+ import type { SpanProcessor } from "@opentelemetry/sdk-trace-base";
4
4
  export type Exporters = {
5
5
  spans: SpanProcessor[];
6
6
  metrics: metrics.MetricReader;
@@ -1,9 +1,9 @@
1
- import { ExportResult } from "@opentelemetry/core";
1
+ import type { ExportResult } from "@opentelemetry/core";
2
2
  import { OTLPExporterBase } from "@opentelemetry/otlp-exporter-base";
3
- import { ReadableLogRecord } from "@opentelemetry/sdk-logs";
4
- import { PushMetricExporter, ResourceMetrics } from "@opentelemetry/sdk-metrics";
5
- import { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-base";
6
- import { NodeSDKConfig } from "../index.js";
3
+ import type { ReadableLogRecord } from "@opentelemetry/sdk-logs";
4
+ import type { PushMetricExporter, ResourceMetrics } from "@opentelemetry/sdk-metrics";
5
+ import type { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-base";
6
+ import type { NodeSDKConfig } from "../index.js";
7
7
  export declare class PIIExporterDecorator extends OTLPExporterBase<(ReadableSpan | ReadableLogRecord)[] | ResourceMetrics> implements SpanExporter, PushMetricExporter {
8
8
  private readonly _exporter;
9
9
  private readonly _config;
@@ -1,6 +1,6 @@
1
1
  import { OTLPExporterBase } from "@opentelemetry/otlp-exporter-base";
2
- import { _cleanStringPII, _recursiveObjectClean, } from "../internals/redaction/pii-detection.js";
3
2
  import { redactors, } from "../internals/redaction/redactors/index.js";
3
+ import { _cleanStringPII, _recursiveObjectClean, BasicRedactor, } from "../../../sdk-core/index.js";
4
4
  export class PIIExporterDecorator extends OTLPExporterBase {
5
5
  _exporter;
6
6
  _config;
@@ -110,10 +110,14 @@ export class PIIExporterDecorator extends OTLPExporterBase {
110
110
  }
111
111
  // Default opt-in every redactor available, excluding only those explicitly configured to false
112
112
  _buildRedactors(redactorsConfig = {}) {
113
- return Object.entries(redactors)
113
+ const defaultRedactors = Object.entries(redactors)
114
114
  .filter(([key]) => {
115
115
  return redactorsConfig[key] !== false;
116
116
  })
117
117
  .map(([_, value]) => value);
118
+ const customRedactors = redactorsConfig.custom?.length
119
+ ? (redactorsConfig.custom.filter((redactor) => redactor instanceof BasicRedactor) ?? [])
120
+ : [];
121
+ return [...defaultRedactors, ...customRedactors];
118
122
  }
119
123
  }
@@ -0,0 +1,7 @@
1
+ import type { NodeSDKConfig } from "../index.js";
2
+ import { EnrichLogProcessor } from "../processor/enrich-logger-processor.js";
3
+ import { EnrichSpanProcessor } from "../processor/enrich-span-processor.js";
4
+ import { NextJsLogProcessor } from "../processor/nextjs-logger-processor.js";
5
+ import { NextJsSpanProcessor } from "../processor/nextjs-span-processor.js";
6
+ export declare function _spansProcessorConfig(config: NodeSDKConfig): (EnrichSpanProcessor | NextJsSpanProcessor)[];
7
+ export declare function _logsProcessorConfig(config: NodeSDKConfig): (EnrichLogProcessor | NextJsLogProcessor)[];
@@ -1,7 +1,10 @@
1
1
  import { EnrichLogProcessor } from "../processor/enrich-logger-processor.js";
2
2
  import { EnrichSpanProcessor } from "../processor/enrich-span-processor.js";
3
+ import { NextJsLogProcessor } from "../processor/nextjs-logger-processor.js";
4
+ import { NextJsSpanProcessor } from "../processor/nextjs-span-processor.js";
3
5
  export function _spansProcessorConfig(config) {
4
6
  const _processor = [];
7
+ _processor.push(new NextJsSpanProcessor());
5
8
  if (config.spanAttributes) {
6
9
  _processor.push(new EnrichSpanProcessor(config.spanAttributes));
7
10
  }
@@ -9,6 +12,7 @@ export function _spansProcessorConfig(config) {
9
12
  }
10
13
  export function _logsProcessorConfig(config) {
11
14
  const _processor = [];
15
+ _processor.push(new NextJsLogProcessor());
12
16
  if (config.spanAttributes) {
13
17
  _processor.push(new EnrichLogProcessor(config.spanAttributes));
14
18
  }
@@ -1,4 +1,5 @@
1
1
  import type { Metadata } from "@grpc/grpc-js";
2
+ import { BasicRedactor } from "../../sdk-core/lib/redaction/basic-redactor.js";
2
3
  export interface NodeSDKConfig {
3
4
  /**
4
5
  * The opentelemetry collector entrypoint GRPC url.
@@ -83,6 +84,16 @@ export interface NodeSDKConfig {
83
84
  * @default true
84
85
  */
85
86
  ip?: boolean;
87
+ /**
88
+ * Redact PPSN (Personal Public Service Number)
89
+ * @default true
90
+ */
91
+ ppsn?: boolean;
92
+ /**
93
+ * Custom redactors to be added together with the default ones
94
+ * @default []
95
+ */
96
+ custom?: BasicRedactor[];
86
97
  };
87
98
  }
88
99
  export interface SamplerCondition {
@@ -56,7 +56,7 @@ export default async function buildNodeInstrumentation(config) {
56
56
  ],
57
57
  spanProcessors: exporter.spans,
58
58
  serviceName: config.serviceName,
59
- metricReader: exporter.metrics,
59
+ metricReaders: [exporter.metrics],
60
60
  logRecordProcessors: exporter.logs,
61
61
  sampler: mainSampler,
62
62
  textMapPropagator: new W3CTraceContextPropagator(),
@@ -0,0 +1,8 @@
1
+ import type { PIISource, RedactionMetric } from "./index.js";
2
+ import type { Counter } from "@opentelemetry/api";
3
+ import { EmailRedactor, type StringKind } from "../../../../../sdk-core/index.js";
4
+ export declare class EmailRedactorWithMetrics extends EmailRedactor {
5
+ protected _counterRedactionMetric: Counter<RedactionMetric>;
6
+ constructor(metric: Counter<RedactionMetric>);
7
+ protected exportMetrics(context: ReturnType<typeof this.redact>["context"], source: PIISource, kind: StringKind): void;
8
+ }
@@ -0,0 +1,19 @@
1
+ import { EmailRedactor, } from "../../../../../sdk-core/index.js";
2
+ export class EmailRedactorWithMetrics extends EmailRedactor {
3
+ _counterRedactionMetric;
4
+ constructor(metric) {
5
+ super();
6
+ this._counterRedactionMetric = metric;
7
+ }
8
+ exportMetrics(context, source, kind) {
9
+ const { domains } = context;
10
+ for (const [domain, domainCount] of Object.entries(domains)) {
11
+ this._counterRedactionMetric.add(domainCount, {
12
+ pii_type: "email",
13
+ redaction_source: source,
14
+ pii_email_domain: domain,
15
+ pii_format: kind,
16
+ });
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,16 @@
1
+ import type { NodeSDKConfig } from "../../../index.js";
2
+ import type { Attributes } from "@opentelemetry/api";
3
+ import { BasicRedactor } from "../../../../../sdk-core/index.js";
4
+ export type PIISource = "trace" | "log" | "metric";
5
+ export 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
+ export type RedactorKeys = keyof Omit<NonNullable<NodeSDKConfig["detection"]>, "custom">;
16
+ export declare const redactors: Record<RedactorKeys, BasicRedactor>;
@@ -0,0 +1,13 @@
1
+ import { EmailRedactorWithMetrics } from "./email.js";
2
+ import { IpRedactorWithMetrics } from "./ip.js";
3
+ import { getMetric } from "../../../metrics.js";
4
+ import { PpsnRedactorWithMetrics } from "./ppsn.js";
5
+ const metricCounter = getMetric("counter", {
6
+ meterName: "o11y",
7
+ metricName: "o11y_pii_redaction",
8
+ });
9
+ export const redactors = {
10
+ email: new EmailRedactorWithMetrics(metricCounter),
11
+ ip: new IpRedactorWithMetrics(metricCounter),
12
+ ppsn: new PpsnRedactorWithMetrics(metricCounter),
13
+ };
@@ -0,0 +1,8 @@
1
+ import type { Counter } from "@opentelemetry/api";
2
+ import type { PIISource, RedactionMetric } from "./index.js";
3
+ import { IpRedactor, type StringKind } from "../../../../../sdk-core/index.js";
4
+ export declare class IpRedactorWithMetrics extends IpRedactor {
5
+ protected _counterRedactionMetric: Counter<RedactionMetric>;
6
+ constructor(metric: Counter<RedactionMetric>);
7
+ protected exportMetrics(context: ReturnType<typeof this.redact>["context"], source: PIISource, kind: StringKind): void;
8
+ }
@@ -0,0 +1,20 @@
1
+ import { IpRedactor } from "../../../../../sdk-core/index.js";
2
+ export class IpRedactorWithMetrics extends IpRedactor {
3
+ _counterRedactionMetric;
4
+ constructor(metric) {
5
+ super();
6
+ this._counterRedactionMetric = metric;
7
+ }
8
+ exportMetrics(context, source, kind) {
9
+ const { counters } = context;
10
+ Object.entries(counters).forEach(([type, counter]) => {
11
+ if (counter > 0) {
12
+ this._counterRedactionMetric.add(counter, {
13
+ pii_type: type,
14
+ redaction_source: source,
15
+ pii_format: kind,
16
+ });
17
+ }
18
+ });
19
+ }
20
+ }
@@ -0,0 +1,8 @@
1
+ import type { PIISource, RedactionMetric } from "./index.js";
2
+ import type { Counter } from "@opentelemetry/api";
3
+ import { PpsnRedactor, type StringKind } from "../../../../../sdk-core/index.js";
4
+ export declare class PpsnRedactorWithMetrics extends PpsnRedactor {
5
+ protected _counterRedactionMetric: Counter<RedactionMetric>;
6
+ constructor(metric: Counter<RedactionMetric>);
7
+ protected exportMetrics(context: ReturnType<typeof this.redact>["context"], source: PIISource, kind: StringKind): void;
8
+ }
@@ -0,0 +1,18 @@
1
+ import { PpsnRedactor, } from "../../../../../sdk-core/index.js";
2
+ export class PpsnRedactorWithMetrics extends PpsnRedactor {
3
+ _counterRedactionMetric;
4
+ constructor(metric) {
5
+ super();
6
+ this._counterRedactionMetric = metric;
7
+ }
8
+ exportMetrics(context, source, kind) {
9
+ const { redactedCounter } = context;
10
+ if (redactedCounter === 0)
11
+ return;
12
+ this._counterRedactionMetric.add(redactedCounter, {
13
+ pii_type: "ppsn",
14
+ redaction_source: source,
15
+ pii_format: kind,
16
+ });
17
+ }
18
+ }
@@ -1,4 +1,4 @@
1
- import { Counter, Gauge, Histogram, MetricOptions, ObservableCounter, ObservableGauge, ObservableUpDownCounter, UpDownCounter, Attributes } from "@opentelemetry/api";
1
+ import { type Counter, type Gauge, type Histogram, type MetricOptions, type ObservableCounter, type ObservableGauge, type ObservableUpDownCounter, type UpDownCounter, type Attributes } from "@opentelemetry/api";
2
2
  type MetricTypeMap<TAttributes extends Attributes> = {
3
3
  counter: Counter<TAttributes>;
4
4
  histogram: Histogram<TAttributes>;
@@ -1,6 +1,6 @@
1
- import { SdkLogRecord, LogRecordProcessor } from "@opentelemetry/sdk-logs";
2
- import { Context } from "@opentelemetry/api";
3
- import { SignalAttributeValue } from "../index.js";
1
+ import type { SdkLogRecord, LogRecordProcessor } from "@opentelemetry/sdk-logs";
2
+ import type { Context } from "@opentelemetry/api";
3
+ import type { SignalAttributeValue } from "../index.js";
4
4
  export declare class EnrichLogProcessor implements LogRecordProcessor {
5
5
  private _spanAttributes?;
6
6
  constructor(spanAttributes?: Record<string, SignalAttributeValue | (() => SignalAttributeValue)>);
@@ -1,6 +1,6 @@
1
- import { Context } from "@opentelemetry/api";
2
- import { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-base";
3
- import { SignalAttributeValue } from "../index.js";
1
+ import type { Context } from "@opentelemetry/api";
2
+ import type { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-base";
3
+ import type { SignalAttributeValue } from "../index.js";
4
4
  export declare class EnrichSpanProcessor implements SpanProcessor {
5
5
  private _spanAttributes;
6
6
  constructor(spanAttributes?: Record<string, SignalAttributeValue | (() => SignalAttributeValue)>);
@@ -0,0 +1,7 @@
1
+ import { Context } from "@opentelemetry/api";
2
+ import { LogRecordProcessor, SdkLogRecord } from "@opentelemetry/sdk-logs";
3
+ export declare class NextJsLogProcessor implements LogRecordProcessor {
4
+ onEmit(logRecord: SdkLogRecord, _context?: Context): void;
5
+ shutdown(): Promise<void>;
6
+ forceFlush(): Promise<void>;
7
+ }
@@ -0,0 +1,30 @@
1
+ import { SeverityNumber } from "@opentelemetry/api-logs";
2
+ export class NextJsLogProcessor {
3
+ onEmit(logRecord, _context) {
4
+ try {
5
+ if (typeof logRecord.body === "object" &&
6
+ logRecord.body !== null &&
7
+ "error" in logRecord.body) {
8
+ const error = logRecord.body.error;
9
+ if (error &&
10
+ typeof error.digest === "string" &&
11
+ error.digest.includes("NEXT_REDIRECT")) {
12
+ logRecord.setBody({
13
+ message: error.digest,
14
+ });
15
+ logRecord.setSeverityNumber(SeverityNumber.INFO);
16
+ logRecord.setSeverityText("INFO");
17
+ }
18
+ }
19
+ }
20
+ catch (err) {
21
+ console.error("Error processing log record:", err);
22
+ }
23
+ }
24
+ shutdown() {
25
+ return Promise.resolve();
26
+ }
27
+ forceFlush() {
28
+ return Promise.resolve();
29
+ }
30
+ }
@@ -0,0 +1,8 @@
1
+ import { Context } from "@opentelemetry/api";
2
+ import { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-base";
3
+ export declare class NextJsSpanProcessor implements SpanProcessor {
4
+ forceFlush(): Promise<void>;
5
+ onStart(span: Span, _context: Context): void;
6
+ onEnd(_span: ReadableSpan): void;
7
+ shutdown(): Promise<void>;
8
+ }