@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/resource.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ResourceDetector,
|
|
3
|
-
DetectedResource,
|
|
4
|
-
DetectedResourceAttributes,
|
|
5
|
-
} from "@opentelemetry/resources";
|
|
6
|
-
import { SignalAttributeValue } from "./index.js";
|
|
7
|
-
import packageJson from "../package.json" with { type: "json" };
|
|
8
|
-
|
|
9
|
-
export class ObservabilityResourceDetector implements ResourceDetector {
|
|
10
|
-
private _resourceAttributes: Record<string, SignalAttributeValue> | undefined;
|
|
11
|
-
|
|
12
|
-
constructor(resourceAttributes?: Record<string, SignalAttributeValue>) {
|
|
13
|
-
this._resourceAttributes = resourceAttributes;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
detect(): DetectedResource {
|
|
17
|
-
let attributes: DetectedResourceAttributes = {};
|
|
18
|
-
|
|
19
|
-
if (this._resourceAttributes) {
|
|
20
|
-
attributes = {
|
|
21
|
-
...this._resourceAttributes,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
attributes["o11y.sdk.name"] = packageJson.name;
|
|
26
|
-
attributes["o11y.sdk.version"] = packageJson.version;
|
|
27
|
-
|
|
28
|
-
return { attributes };
|
|
29
|
-
}
|
|
30
|
-
}
|
package/lib/traces.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { Span, SpanOptions, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
2
|
-
import { getNodeSdkConfig } from "./config-manager.js";
|
|
3
|
-
|
|
4
|
-
export type WithSpanParams<T> = {
|
|
5
|
-
/**
|
|
6
|
-
* The name of the trace the span should belong to.
|
|
7
|
-
* NOTE: If you want the new span to belong to an already existing trace, you should provide the same tracer name
|
|
8
|
-
*/
|
|
9
|
-
traceName?: string;
|
|
10
|
-
spanName: string;
|
|
11
|
-
spanOptions?: SpanOptions;
|
|
12
|
-
/** A function defining the task you want to be wrapped by this span */
|
|
13
|
-
fn: (span: Span) => T | Promise<T>;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Generates a function wrapping a given Callable `fn` into an error handling block.
|
|
18
|
-
* Setting Span status and recording any caught exception before bubbling it up.
|
|
19
|
-
*
|
|
20
|
-
* Marks the span as ended once the provided callable has ended or an error has been caught.
|
|
21
|
-
*
|
|
22
|
-
* @returns {Promise<T>} where T is the type returned by the Callable.
|
|
23
|
-
* @throws any error thrown by the original Callable `fn` provided.
|
|
24
|
-
*/
|
|
25
|
-
function selfContainedSpanHandlerGenerator<T>(
|
|
26
|
-
fn: (span: Span) => T | Promise<T>,
|
|
27
|
-
): (span: Span) => Promise<T> {
|
|
28
|
-
return async (span: Span) => {
|
|
29
|
-
try {
|
|
30
|
-
const fnResult = await fn(span);
|
|
31
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
32
|
-
return fnResult;
|
|
33
|
-
} catch (err) {
|
|
34
|
-
if (err instanceof Error) {
|
|
35
|
-
span.recordException(err);
|
|
36
|
-
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
|
|
37
|
-
throw err;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
span.recordException({ message: JSON.stringify(err) });
|
|
41
|
-
span.setStatus({
|
|
42
|
-
code: SpanStatusCode.ERROR,
|
|
43
|
-
message: JSON.stringify(err),
|
|
44
|
-
});
|
|
45
|
-
throw err;
|
|
46
|
-
} finally {
|
|
47
|
-
span.end();
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Gets the currently active OpenTelemetry span.
|
|
54
|
-
*
|
|
55
|
-
* @returns {Span | undefined} The active span with redaction logic applied,
|
|
56
|
-
* or `undefined` if there is no active span in context.
|
|
57
|
-
*/
|
|
58
|
-
export function getActiveSpan() {
|
|
59
|
-
return trace.getActiveSpan();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function withSpan<T>({
|
|
63
|
-
traceName,
|
|
64
|
-
spanName,
|
|
65
|
-
spanOptions = {},
|
|
66
|
-
fn,
|
|
67
|
-
}: WithSpanParams<T>) {
|
|
68
|
-
const sdkConfig = getNodeSdkConfig();
|
|
69
|
-
const tracer = trace.getTracer(
|
|
70
|
-
traceName ?? sdkConfig?.serviceName ?? "o11y-sdk",
|
|
71
|
-
sdkConfig?.serviceVersion,
|
|
72
|
-
);
|
|
73
|
-
return tracer.startActiveSpan(
|
|
74
|
-
spanName,
|
|
75
|
-
spanOptions,
|
|
76
|
-
selfContainedSpanHandlerGenerator<T>(fn),
|
|
77
|
-
);
|
|
78
|
-
}
|
package/lib/url-sampler.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { Attributes, Context, Link, SpanKind } from "@opentelemetry/api";
|
|
2
|
-
import {
|
|
3
|
-
Sampler,
|
|
4
|
-
SamplingDecision,
|
|
5
|
-
SamplingResult,
|
|
6
|
-
} from "@opentelemetry/sdk-trace-base";
|
|
7
|
-
import { SamplerCondition } from "./index.js";
|
|
8
|
-
|
|
9
|
-
export class UrlSampler implements Sampler {
|
|
10
|
-
private _samplerCondition: SamplerCondition[];
|
|
11
|
-
private _nextSampler: Sampler;
|
|
12
|
-
|
|
13
|
-
constructor(samplerCondition: SamplerCondition[] = [], nextSampler: Sampler) {
|
|
14
|
-
this._samplerCondition = samplerCondition;
|
|
15
|
-
this._nextSampler = nextSampler;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
shouldSample(
|
|
19
|
-
_context: Context,
|
|
20
|
-
traceId: string,
|
|
21
|
-
_spanName: string,
|
|
22
|
-
_spanKind: SpanKind,
|
|
23
|
-
attributes: Attributes,
|
|
24
|
-
_links: Link[],
|
|
25
|
-
): SamplingResult {
|
|
26
|
-
const url: string | undefined = attributes["http.target"]?.toString();
|
|
27
|
-
|
|
28
|
-
if (url) {
|
|
29
|
-
for (const condition of this._samplerCondition) {
|
|
30
|
-
if (
|
|
31
|
-
(condition.type === "equals" && url === condition.url) ||
|
|
32
|
-
(condition.type === "endsWith" && url.endsWith(condition.url)) ||
|
|
33
|
-
(condition.type === "includes" && url.includes(condition.url))
|
|
34
|
-
) {
|
|
35
|
-
return { decision: SamplingDecision.NOT_RECORD };
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return this._nextSampler.shouldSample(
|
|
41
|
-
_context,
|
|
42
|
-
traceId,
|
|
43
|
-
_spanName,
|
|
44
|
-
_spanKind,
|
|
45
|
-
attributes,
|
|
46
|
-
_links,
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
toString(): string {
|
|
50
|
-
return "UrlSampler";
|
|
51
|
-
}
|
|
52
|
-
}
|
package/lib/utils.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BatchLogRecordProcessor,
|
|
3
|
-
SimpleLogRecordProcessor,
|
|
4
|
-
} from "@opentelemetry/sdk-logs";
|
|
5
|
-
import { SDKCollectorMode } from "./index.js";
|
|
6
|
-
import { tracing } from "@opentelemetry/sdk-node";
|
|
7
|
-
|
|
8
|
-
export const LogRecordProcessorMap: Record<
|
|
9
|
-
SDKCollectorMode,
|
|
10
|
-
typeof SimpleLogRecordProcessor | typeof BatchLogRecordProcessor
|
|
11
|
-
> = {
|
|
12
|
-
single: SimpleLogRecordProcessor,
|
|
13
|
-
batch: BatchLogRecordProcessor,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const SpanProcessorMap: Record<
|
|
17
|
-
SDKCollectorMode,
|
|
18
|
-
typeof tracing.SimpleSpanProcessor | typeof tracing.BatchSpanProcessor
|
|
19
|
-
> = {
|
|
20
|
-
single: tracing.SimpleSpanProcessor,
|
|
21
|
-
batch: tracing.BatchSpanProcessor,
|
|
22
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { getNodeSdkConfig, setNodeSdkConfig } from "../lib/config-manager";
|
|
3
|
-
import { NodeSDKConfig } from "../lib";
|
|
4
|
-
|
|
5
|
-
describe("Config Manager", () => {
|
|
6
|
-
it("does not throw if getConfig is called before initialization", () => {
|
|
7
|
-
expect(() => getNodeSdkConfig()).not.toThrow();
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("sdk defined config is not pollutable", () => {
|
|
11
|
-
const config: NodeSDKConfig = {
|
|
12
|
-
collectorUrl: "http://example.com",
|
|
13
|
-
serviceName: "MyService",
|
|
14
|
-
spanAttributes: {
|
|
15
|
-
"my.attribute": "value",
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
setNodeSdkConfig(config);
|
|
20
|
-
|
|
21
|
-
const cfg = getNodeSdkConfig();
|
|
22
|
-
|
|
23
|
-
// Top level
|
|
24
|
-
cfg.collectorUrl = "http://example.com/changed";
|
|
25
|
-
// Subfield
|
|
26
|
-
cfg.spanAttributes["my.attribute"] = "another-attribute";
|
|
27
|
-
|
|
28
|
-
// Ensure config values remain unchanged
|
|
29
|
-
expect(getNodeSdkConfig().collectorUrl).toStrictEqual(config.collectorUrl);
|
|
30
|
-
expect(getNodeSdkConfig().spanAttributes["my.attribute"]).toStrictEqual(
|
|
31
|
-
config.spanAttributes["my.attribute"],
|
|
32
|
-
);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { ReadableLogRecord } from "@opentelemetry/sdk-logs";
|
|
2
|
-
import { ResourceMetrics } from "@opentelemetry/sdk-metrics";
|
|
3
|
-
import { ReadableSpan } from "@opentelemetry/sdk-trace-base";
|
|
4
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { PIIExporterDecorator } from "../../lib/exporter/pii-exporter-decorator";
|
|
6
|
-
|
|
7
|
-
describe("PIIExporterDecorator", () => {
|
|
8
|
-
let exporterMock: any;
|
|
9
|
-
let config: any;
|
|
10
|
-
let piiExporter: PIIExporterDecorator;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
exporterMock = {
|
|
14
|
-
export: vi.fn(),
|
|
15
|
-
shutdown: vi.fn(() => Promise.resolve()),
|
|
16
|
-
forceFlush: vi.fn(() => Promise.resolve()),
|
|
17
|
-
_delegate: {},
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
config = { detection: { email: true } };
|
|
21
|
-
piiExporter = new PIIExporterDecorator(exporterMock, config);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("should redact emails in span name and attributes", () => {
|
|
25
|
-
const items: ReadableSpan[] = [
|
|
26
|
-
{
|
|
27
|
-
name: "user@example.com",
|
|
28
|
-
kind: 0,
|
|
29
|
-
spanContext: () => ({}),
|
|
30
|
-
attributes: { email: "user@example.com" },
|
|
31
|
-
resource: { attributes: { owner: "user@example.com" } },
|
|
32
|
-
events: [
|
|
33
|
-
{
|
|
34
|
-
name: "Login from user@example.com",
|
|
35
|
-
attributes: { email: "user@example.com" },
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
} as any,
|
|
39
|
-
];
|
|
40
|
-
|
|
41
|
-
const callback = vi.fn();
|
|
42
|
-
piiExporter.export(items, callback);
|
|
43
|
-
|
|
44
|
-
const exportedSpan = exporterMock.export.mock.calls[0][0][0];
|
|
45
|
-
expect(exportedSpan.name).toBe("[REDACTED EMAIL]");
|
|
46
|
-
expect(exportedSpan.attributes.email).toBe("[REDACTED EMAIL]");
|
|
47
|
-
expect(exportedSpan.resource.attributes.owner).toBe("[REDACTED EMAIL]");
|
|
48
|
-
expect(exportedSpan.events[0].name).toBe("Login from [REDACTED EMAIL]");
|
|
49
|
-
expect(exportedSpan.events[0].attributes.email).toBe("[REDACTED EMAIL]");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("should redact emails in log records", () => {
|
|
53
|
-
const items: ReadableLogRecord[] = [
|
|
54
|
-
{
|
|
55
|
-
body: "Error from user@example.com",
|
|
56
|
-
attributes: { email: "user@example.com" },
|
|
57
|
-
severityText: "INFO",
|
|
58
|
-
severityNumber: 1,
|
|
59
|
-
resource: { attributes: { owner: "user@example.com" } },
|
|
60
|
-
} as any,
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
const callback = vi.fn();
|
|
64
|
-
piiExporter.export(items, callback);
|
|
65
|
-
|
|
66
|
-
const exportedLog = exporterMock.export.mock.calls[0][0][0];
|
|
67
|
-
expect(exportedLog.body).toBe("Error from [REDACTED EMAIL]");
|
|
68
|
-
expect(exportedLog.attributes.email).toBe("[REDACTED EMAIL]");
|
|
69
|
-
expect(exportedLog.resource.attributes.owner).toBe("[REDACTED EMAIL]");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("should redact emails in resource metrics", () => {
|
|
73
|
-
const metrics: ResourceMetrics = {
|
|
74
|
-
resource: {
|
|
75
|
-
attributes: { maintainer: "user@example.com" },
|
|
76
|
-
},
|
|
77
|
-
scopeMetrics: [],
|
|
78
|
-
} as any;
|
|
79
|
-
|
|
80
|
-
const callback = vi.fn();
|
|
81
|
-
piiExporter.export(metrics, callback);
|
|
82
|
-
|
|
83
|
-
const exportedMetric = exporterMock.export.mock.calls[0][0];
|
|
84
|
-
expect(exportedMetric.resource.attributes.maintainer).toBe(
|
|
85
|
-
"[REDACTED EMAIL]",
|
|
86
|
-
);
|
|
87
|
-
});
|
|
88
|
-
});
|
package/test/index.test.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { NodeSDKConfig } from "../index";
|
|
3
|
-
import { instrumentNode } from "../index";
|
|
4
|
-
import * as buildNodeInstrumentationModule from "../lib/instrumentation.node";
|
|
5
|
-
import { metrics } from "@opentelemetry/sdk-node";
|
|
6
|
-
|
|
7
|
-
describe("instrumentNode", () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
// @ts-ignore Avoid actually running exporters at any time in tests (overriding private method)
|
|
10
|
-
vi.spyOn(
|
|
11
|
-
metrics.PeriodicExportingMetricReader.prototype,
|
|
12
|
-
"_doRun",
|
|
13
|
-
).mockImplementation(vi.fn());
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
afterEach(() => {
|
|
17
|
-
vi.restoreAllMocks();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test("should call buildNodeInstrumentation with the provided config", async () => {
|
|
21
|
-
const instrumentationMock = vi
|
|
22
|
-
.spyOn(buildNodeInstrumentationModule, "default")
|
|
23
|
-
.mockImplementation(vi.fn());
|
|
24
|
-
|
|
25
|
-
const config: NodeSDKConfig = {
|
|
26
|
-
serviceName: "custom-service",
|
|
27
|
-
collectorUrl: "http://custom-collector.com",
|
|
28
|
-
protocol: "grpc",
|
|
29
|
-
resourceAttributes: {
|
|
30
|
-
"team.infra.cluster": "dev-01",
|
|
31
|
-
"team.infra.pod": "01",
|
|
32
|
-
"team.service.type": "fastify",
|
|
33
|
-
},
|
|
34
|
-
spanAttributes: {
|
|
35
|
-
"signal.namespace": "example",
|
|
36
|
-
"signal.number": () => "callback",
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
await instrumentNode(config);
|
|
41
|
-
|
|
42
|
-
expect(instrumentationMock).toHaveBeenCalledWith(config);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("should not throw when called without arguments", async () => {
|
|
46
|
-
await expect(instrumentNode()).resolves.not.toThrow();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("should invoke instrumentation shutdown on SIGTERM", async () => {
|
|
50
|
-
const config: NodeSDKConfig = {
|
|
51
|
-
serviceName: "custom-service",
|
|
52
|
-
collectorUrl: "http://custom-collector.com",
|
|
53
|
-
protocol: "grpc",
|
|
54
|
-
resourceAttributes: {
|
|
55
|
-
"team.infra.cluster": "dev-01",
|
|
56
|
-
"team.infra.pod": "01",
|
|
57
|
-
"team.service.type": "fastify",
|
|
58
|
-
},
|
|
59
|
-
spanAttributes: {
|
|
60
|
-
"signal.namespace": "example",
|
|
61
|
-
"signal.number": () => "callback",
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const sdk = await instrumentNode(config);
|
|
66
|
-
const shutdownMock = vi.spyOn(sdk, "shutdown");
|
|
67
|
-
process.emit("SIGTERM");
|
|
68
|
-
expect(shutdownMock).toHaveBeenCalled();
|
|
69
|
-
});
|
|
70
|
-
});
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
# Integration Test Runner
|
|
2
|
-
|
|
3
|
-
This script is a **Bash-based test runner** that discovers and executes integration test scripts for the Node SDK (`./packages/sdk-node/test/integration`).
|
|
4
|
-
It ensures tests run inside an isolated Docker network and provides a summary of results.
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Usage
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
./run-tests.sh <build-id> <root-path> <is-ci?>
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
**Arguments**
|
|
15
|
-
|
|
16
|
-
<build-id>
|
|
17
|
-
A unique identifier for the test run. Used to name the Docker network (<build-id>_testnetwork).
|
|
18
|
-
|
|
19
|
-
<root-path>
|
|
20
|
-
The root path for the project or build environment.
|
|
21
|
-
|
|
22
|
-
[ci-flag] (optional)
|
|
23
|
-
Defaults to false.
|
|
24
|
-
If set to true, the script will remove all Docker images after the tests complete (intended for CI pipelines to save disk space).
|
|
25
|
-
|
|
26
|
-
## How It Works
|
|
27
|
-
|
|
28
|
-
- Ensures at least two arguments are provided. Exits with usage instructions otherwise.
|
|
29
|
-
- Creates a temporary Docker network named <build-id>\_testnetwork.
|
|
30
|
-
- Finds all integration test scripts under: ./packages/sdk-node/test/integration/test\*/run.sh
|
|
31
|
-
- Runs each test script.
|
|
32
|
-
- Captures exit codes and tracks failures.
|
|
33
|
-
- Prints results per test.
|
|
34
|
-
- Removes the Docker network after all tests finish.
|
|
35
|
-
- If running in CI mode (ci-flag = true), removes all Docker images.
|
|
36
|
-
- Reports whether all tests passed or how many failed.
|
|
37
|
-
Exits with:
|
|
38
|
-
- 0 if all tests succeeded
|
|
39
|
-
- 1 if one or more tests failed
|
|
40
|
-
|
|
41
|
-
**Example**
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
# Run tests locally
|
|
45
|
-
./run-tests.sh my-build-id /path/to/project
|
|
46
|
-
|
|
47
|
-
# Run tests in CI mode (cleanup Docker images after)
|
|
48
|
-
./run-tests.sh my-build-id /path/to/project true
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Tests
|
|
52
|
-
|
|
53
|
-
### test_fastify-o11y-pii-enabled
|
|
54
|
-
|
|
55
|
-
The `run.sh` script performs the following steps:
|
|
56
|
-
|
|
57
|
-
- build a docker image of a fastify app `/examples/fastify`
|
|
58
|
-
- run grafana alloy inside a docker container with a test configuration `/alloy/integration-test.alloy`
|
|
59
|
-
- ensure is running otherwise exit process
|
|
60
|
-
- run fastify app in a docker container
|
|
61
|
-
- ensure is running otherwise exit process
|
|
62
|
-
- execute some curl to the fastify microservice
|
|
63
|
-
- persist alloy log to a file and save to following path `/packages/sdk-node/test/integration/`
|
|
64
|
-
- verify pii data are removed and metrics are increased
|
|
65
|
-
- docker turn down process (containers/network/image)
|
|
66
|
-
|
|
67
|
-
### test_without-o11y
|
|
68
|
-
|
|
69
|
-
The `run.sh` script performs the following steps:
|
|
70
|
-
|
|
71
|
-
- build a docker image of a fastify app `/examples/fastify`
|
|
72
|
-
- verify service execution with o11y sdk disabled (otel collector empty)
|
|
73
|
-
- execute http requests for dummy route with otel trace customization function calls
|
|
74
|
-
- assert 200 http status without any error
|
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
: '
|
|
4
|
-
Build a new docker image of a given Dockerfile, if already exists skip build.
|
|
5
|
-
|
|
6
|
-
@param container_name docker container name
|
|
7
|
-
|
|
8
|
-
@return int error code
|
|
9
|
-
0. success
|
|
10
|
-
1. validation error
|
|
11
|
-
'
|
|
12
|
-
build_image() {
|
|
13
|
-
if [ "$#" -lt 3 ] || [ "$#" -gt 4 ]; then
|
|
14
|
-
echo "Error: build_image expected 3 or 4 parameters, got $#." >&2
|
|
15
|
-
echo "Usage: build_image <image_name> <dockerfile_path> <context_path> [max_age_minutes]" >&2
|
|
16
|
-
return 1
|
|
17
|
-
fi
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
local image_name="$1"
|
|
21
|
-
local dockerfile_path="$2"
|
|
22
|
-
local context_path="$3"
|
|
23
|
-
local max_age_minutes="${4:-30}" # default threshold = 30 minutes
|
|
24
|
-
|
|
25
|
-
if docker image inspect "$image_name" > /dev/null 2>&1; then
|
|
26
|
-
echo "Image $image_name already exists. Checking age..."
|
|
27
|
-
|
|
28
|
-
# Extract the Created timestamp from docker inspect (ISO 8601 format)
|
|
29
|
-
local created_at
|
|
30
|
-
created_at=$(docker image inspect --format '{{.Created}}' "$image_name")
|
|
31
|
-
if [ -z "$created_at" ]; then
|
|
32
|
-
echo "Warning: Could not determine creation date. Proceeding without rebuild check." >&2
|
|
33
|
-
return 0
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
# Convert created_at to epoch seconds
|
|
37
|
-
local created_epoch
|
|
38
|
-
created_epoch=$(date -d "$created_at" +%s 2>/dev/null)
|
|
39
|
-
|
|
40
|
-
# Current time in epoch seconds
|
|
41
|
-
local now_epoch
|
|
42
|
-
now_epoch=$(date +%s)
|
|
43
|
-
|
|
44
|
-
# Calculate age in minutes
|
|
45
|
-
local age_minutes=$(( (now_epoch - created_epoch) / 60 ))
|
|
46
|
-
|
|
47
|
-
echo "Image $image_name is $age_minutes minutes old (threshold: $max_age_minutes minutes)."
|
|
48
|
-
|
|
49
|
-
if [ "$age_minutes" -ge "$max_age_minutes" ]; then
|
|
50
|
-
echo "Image is too old. Rebuilding..."
|
|
51
|
-
docker build -t $image_name -f $dockerfile_path $context_path
|
|
52
|
-
return $?
|
|
53
|
-
else
|
|
54
|
-
echo "Image is recent. Skipping rebuild."
|
|
55
|
-
return 0
|
|
56
|
-
fi
|
|
57
|
-
else
|
|
58
|
-
echo "Image $image_name not found. Building..."
|
|
59
|
-
docker build -t "$image_name" -f "$dockerfile_path" "$context_path"
|
|
60
|
-
return $?
|
|
61
|
-
fi
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
: '
|
|
65
|
-
Stop and remove a docker container for the given input container name.
|
|
66
|
-
|
|
67
|
-
@param container_name docker container name
|
|
68
|
-
|
|
69
|
-
@return int error code
|
|
70
|
-
0. success
|
|
71
|
-
1. validation error
|
|
72
|
-
'
|
|
73
|
-
container_stop_and_rm() {
|
|
74
|
-
local container_name="$1"
|
|
75
|
-
|
|
76
|
-
if [[ -z "$container_name" ]]; then
|
|
77
|
-
echo "Error: container_name must have a value."
|
|
78
|
-
return 1
|
|
79
|
-
fi
|
|
80
|
-
|
|
81
|
-
docker container stop $container_name
|
|
82
|
-
docker container rm -f $container_name
|
|
83
|
-
|
|
84
|
-
return $?
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
: '
|
|
88
|
-
Run a new alloy container and check if the status is running.
|
|
89
|
-
|
|
90
|
-
@param root_path volume root path for alloy file config
|
|
91
|
-
@param network_name docker network name
|
|
92
|
-
@param container_name docker container name
|
|
93
|
-
|
|
94
|
-
@optional max_retries
|
|
95
|
-
@default 10
|
|
96
|
-
|
|
97
|
-
@return int error code
|
|
98
|
-
0. success
|
|
99
|
-
1. validation error
|
|
100
|
-
2. execution failed
|
|
101
|
-
'
|
|
102
|
-
run_and_check_alloy() {
|
|
103
|
-
|
|
104
|
-
if [ "$#" -lt 3 ]; then
|
|
105
|
-
echo "Error: run_and_check_alloy expected 3 parameters, got $#." >&2
|
|
106
|
-
return 1
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
|
-
local root_path="$1"
|
|
110
|
-
local network_name="$2"
|
|
111
|
-
local container_name="$3"
|
|
112
|
-
local max_retries="${4:-10}"
|
|
113
|
-
local counter_retries=0
|
|
114
|
-
|
|
115
|
-
if [[ -z "$root_path" || -z "$network_name" || -z "$container_name" ]]; then
|
|
116
|
-
echo "Error: root_path, network_name, and container_name must all have values."
|
|
117
|
-
return 1
|
|
118
|
-
fi
|
|
119
|
-
|
|
120
|
-
if ! [[ "$max_retries" =~ ^[0-9]+$ ]]; then
|
|
121
|
-
echo "Error: max_retries parameter must be a number." >&2
|
|
122
|
-
return 1
|
|
123
|
-
fi
|
|
124
|
-
|
|
125
|
-
docker run -d \
|
|
126
|
-
-v "$root_path/alloy/integration-test.alloy:/etc/alloy/config.alloy:ro" \
|
|
127
|
-
--network $network_name \
|
|
128
|
-
--name $container_name \
|
|
129
|
-
-p 4317:4317 \
|
|
130
|
-
-p 4318:4318 \
|
|
131
|
-
grafana/alloy:v1.9.2 \
|
|
132
|
-
run --server.http.listen-addr=0.0.0.0:12345 --stability.level=experimental /etc/alloy/config.alloy
|
|
133
|
-
|
|
134
|
-
echo "$container_name container status"
|
|
135
|
-
until [ "$(docker inspect -f {{.State.Running}} $container_name)" = true ]; do
|
|
136
|
-
sleep 2
|
|
137
|
-
docker inspect -f {{.State.Running}} $container_name
|
|
138
|
-
counter_retries=$((counter_retries + 1))
|
|
139
|
-
if [ $counter_retries -ge $max_retries ]; then
|
|
140
|
-
echo "Exceeded maximum retries. Exiting."
|
|
141
|
-
return 2
|
|
142
|
-
fi
|
|
143
|
-
done
|
|
144
|
-
|
|
145
|
-
return 0
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
: '
|
|
149
|
-
Run a new fastify microservice container and check if the status is running.
|
|
150
|
-
|
|
151
|
-
@param network_name docker network name
|
|
152
|
-
@param collector_url otel collector grpc endpoint
|
|
153
|
-
@param container_name docker container name
|
|
154
|
-
@param build_id current pipeline build id
|
|
155
|
-
|
|
156
|
-
@optional max_retries
|
|
157
|
-
@default 10
|
|
158
|
-
|
|
159
|
-
@return int error code
|
|
160
|
-
0. success
|
|
161
|
-
1. validation error
|
|
162
|
-
2. execution failed
|
|
163
|
-
'
|
|
164
|
-
run_and_check_fastify() {
|
|
165
|
-
|
|
166
|
-
if [ "$#" -lt 4 ]; then
|
|
167
|
-
echo "Error: run_and_check_alloy expected 3 parameters, got $#." >&2
|
|
168
|
-
return 1
|
|
169
|
-
fi
|
|
170
|
-
|
|
171
|
-
local network_name="$1"
|
|
172
|
-
local collector_url="$2"
|
|
173
|
-
local container_name="$3"
|
|
174
|
-
local build_id="$4"
|
|
175
|
-
local max_retries="${5:-10}"
|
|
176
|
-
local counter_retries=0
|
|
177
|
-
|
|
178
|
-
if [[ -z "$build_id" || -z "$network_name" || -z "$container_name" ]]; then
|
|
179
|
-
echo "Error: build_id, network_name, and container_name must all have values."
|
|
180
|
-
return 1
|
|
181
|
-
fi
|
|
182
|
-
|
|
183
|
-
if ! [[ "$max_retries" =~ ^[0-9]+$ ]]; then
|
|
184
|
-
echo "Error: max_retries parameter must be a number." >&2
|
|
185
|
-
return 1
|
|
186
|
-
fi
|
|
187
|
-
|
|
188
|
-
docker run --detach \
|
|
189
|
-
--network $network_name \
|
|
190
|
-
--name $container_name \
|
|
191
|
-
-e DB_DISABLED="true" \
|
|
192
|
-
-e SERVER_HOST="0.0.0.0" \
|
|
193
|
-
-e OTEL_COLLECTOR_URL=$collector_url \
|
|
194
|
-
--health-cmd="curl -f http://${container_name}:9091/api/health > /dev/null || exit 1" \
|
|
195
|
-
--health-start-period=1s \
|
|
196
|
-
--health-retries=10 \
|
|
197
|
-
--health-interval=1s \
|
|
198
|
-
-p 9091:9091 \
|
|
199
|
-
$container_name:$build_id
|
|
200
|
-
|
|
201
|
-
counter_retries=0
|
|
202
|
-
echo "$container_name container status"
|
|
203
|
-
until [ "$(docker inspect -f {{.State.Health.Status}} $container_name)" = "healthy" ]; do
|
|
204
|
-
sleep 1
|
|
205
|
-
docker inspect -f {{.State.Health.Status}} $container_name
|
|
206
|
-
counter_retries=$((counter_retries + 1))
|
|
207
|
-
if [ $counter_retries -ge $max_retries ]; then
|
|
208
|
-
echo "Exceeded maximum retries. Exiting."
|
|
209
|
-
return 2
|
|
210
|
-
fi
|
|
211
|
-
done
|
|
212
|
-
|
|
213
|
-
return 0
|
|
214
|
-
}
|