@equipe-tech/observability 0.2.1 → 0.3.5
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/README.md +11 -9
- package/dist/BrowserEvents.d.ts +89 -12
- package/dist/BrowserEvents.js +67 -13
- package/dist/Correlation.d.ts +51 -0
- package/dist/Correlation.js +95 -0
- package/dist/Metrics.d.ts +19 -3
- package/dist/Metrics.js +16 -0
- package/dist/MetricsRuntime.d.ts +45 -3
- package/dist/MetricsRuntime.js +478 -120
- package/dist/PolicyOtlpLogger.d.ts +18 -0
- package/dist/PolicyOtlpLogger.js +142 -0
- package/dist/RedactionPolicy.d.ts +2 -9
- package/dist/RedactionPolicy.js +2 -246
- package/dist/ResourceIdentity.d.ts +47 -0
- package/dist/ResourceIdentity.js +82 -0
- package/dist/Telemetry.d.ts +18 -3
- package/dist/Telemetry.js +43 -25
- package/dist/TelemetryConfig.d.ts +11 -4
- package/dist/TelemetryConfig.js +38 -10
- package/dist/audit/AuditCommitDocument.d.ts +41 -0
- package/dist/audit/AuditCommitDocument.js +58 -0
- package/dist/audit/AuditDigest.d.ts +11 -0
- package/dist/audit/AuditDigest.js +25 -0
- package/dist/audit/AuditOutbox.d.ts +75 -0
- package/dist/audit/AuditOutbox.js +104 -0
- package/dist/audit/AuditPublisher.d.ts +49 -0
- package/dist/audit/AuditPublisher.js +60 -0
- package/dist/audit/AuditRecord.d.ts +128 -0
- package/dist/audit/AuditRecord.js +172 -0
- package/dist/audit/CommittedAuditRecordInternal.d.ts +11 -0
- package/dist/audit/CommittedAuditRecordInternal.js +13 -0
- package/dist/audit/InvalidAuditRecord.d.ts +12 -0
- package/dist/audit/InvalidAuditRecord.js +16 -0
- package/dist/audit/index.d.ts +7 -0
- package/dist/audit/index.js +7 -0
- package/dist/browser/BrowserClient.d.ts +82 -3
- package/dist/browser/BrowserClient.js +322 -28
- package/dist/browser/BrowserEventLimits.d.ts +8 -0
- package/dist/browser/BrowserEventLimits.js +8 -0
- package/dist/browser/BrowserMetricRecorder.d.ts +17 -0
- package/dist/browser/BrowserMetricRecorder.js +35 -0
- package/dist/browser/BrowserTelemetry.d.ts +40 -0
- package/dist/browser/BrowserTelemetry.js +105 -0
- package/dist/browser/ClientPolicy.d.ts +3 -0
- package/dist/browser/ClientPolicy.js +52 -0
- package/dist/browser/client.d.ts +3 -2
- package/dist/browser/client.js +2 -1
- package/dist/browser/index.d.ts +4 -37
- package/dist/browser/index.js +3 -95
- package/dist/contract/ContractCompatibility.d.ts +31 -0
- package/dist/contract/ContractCompatibility.js +274 -0
- package/dist/contract/ContractIndex.d.ts +54 -0
- package/dist/contract/ContractIndex.js +178 -0
- package/dist/contract/ContractSurface.d.ts +111 -0
- package/dist/contract/ContractSurface.js +116 -0
- package/dist/contract/EventName.d.ts +6 -0
- package/dist/contract/EventName.js +55 -0
- package/dist/contract/EventProducer.d.ts +123 -0
- package/dist/contract/EventProducer.js +209 -0
- package/dist/contract/MetricContractError.d.ts +19 -0
- package/dist/contract/MetricContractError.js +19 -0
- package/dist/contract/MetricDefinition.d.ts +61 -0
- package/dist/contract/MetricDefinition.js +91 -0
- package/dist/contract/MetricProducer.d.ts +42 -0
- package/dist/contract/MetricProducer.js +123 -0
- package/dist/contract/OrganizationEvents.d.ts +165 -0
- package/dist/contract/OrganizationEvents.js +97 -0
- package/dist/contract/TelemetryContract.d.ts +95 -0
- package/dist/contract/TelemetryContract.js +451 -0
- package/dist/contract/TelemetryContractError.d.ts +44 -0
- package/dist/contract/TelemetryContractError.js +64 -0
- package/dist/contract/TelemetryEvent.d.ts +59 -0
- package/dist/contract/TelemetryEvent.js +54 -0
- package/dist/contract/index.d.ts +11 -0
- package/dist/contract/index.js +11 -0
- package/dist/effect/WideEvent.d.ts +3 -0
- package/dist/effect/WideEvent.js +20 -0
- package/dist/effect/WideEventSink.d.ts +3 -0
- package/dist/effect/WideEventSink.js +79 -0
- package/dist/effect/index.d.ts +2 -0
- package/dist/effect/index.js +2 -0
- package/dist/index.d.ts +17 -2
- package/dist/index.js +11 -2
- package/dist/metrics/InstrumentGrammar.d.ts +5 -0
- package/dist/metrics/InstrumentGrammar.js +12 -0
- package/dist/metrics/MetricScalar.d.ts +3 -0
- package/dist/metrics/MetricScalar.js +5 -0
- package/dist/node/AuditDigest.d.ts +3 -0
- package/dist/node/AuditDigest.js +6 -0
- package/dist/node/BrowserEventIngest.d.ts +7 -2
- package/dist/node/BrowserEventIngest.js +111 -19
- package/dist/node/LocalCollector.d.ts +20 -0
- package/dist/node/LocalCollector.js +188 -0
- package/dist/node/Observability.d.ts +49 -0
- package/dist/node/Observability.js +190 -0
- package/dist/node/Runtime.d.ts +4 -1
- package/dist/node/index.d.ts +4 -0
- package/dist/node/index.js +3 -0
- package/dist/policy/BrowserFieldPolicy.d.ts +7 -0
- package/dist/policy/BrowserFieldPolicy.js +298 -0
- package/dist/policy/DataPolicy.d.ts +19 -0
- package/dist/policy/DataPolicy.js +254 -0
- package/dist/policy/DataPolicyError.d.ts +22 -0
- package/dist/policy/DataPolicyError.js +25 -0
- package/dist/policy/Deduplication.d.ts +13 -0
- package/dist/policy/Deduplication.js +44 -0
- package/dist/policy/DefectEnvelope.d.ts +25 -0
- package/dist/policy/DefectEnvelope.js +52 -0
- package/dist/policy/MetricLabelPolicy.d.ts +5 -0
- package/dist/policy/MetricLabelPolicy.js +65 -0
- package/dist/policy/PolicyTransform.d.ts +16 -0
- package/dist/policy/PolicyTransform.js +136 -0
- package/dist/policy/PolicyVocabulary.d.ts +10 -0
- package/dist/policy/PolicyVocabulary.js +84 -0
- package/dist/policy/ResourceAttributePolicy.d.ts +9 -0
- package/dist/policy/ResourceAttributePolicy.js +33 -0
- package/dist/policy/entrypoint.d.ts +2 -0
- package/dist/policy/entrypoint.js +2 -0
- package/dist/policy/index.d.ts +17 -0
- package/dist/policy/index.js +9 -0
- package/dist/profile/DataPolicy.d.ts +2 -0
- package/dist/profile/DataPolicy.js +1 -0
- package/dist/profile/EnvironmentPolicy.d.ts +22 -0
- package/dist/profile/EnvironmentPolicy.js +41 -0
- package/dist/profile/LifecycleRegistry.d.ts +24 -0
- package/dist/profile/LifecycleRegistry.js +240 -0
- package/dist/profile/ObservabilityAdapter.d.ts +115 -0
- package/dist/profile/ObservabilityAdapter.js +50 -0
- package/dist/profile/ObservabilityConfig.d.ts +68 -0
- package/dist/profile/ObservabilityConfig.js +125 -0
- package/dist/profile/ObservabilityConfigError.d.ts +23 -0
- package/dist/profile/ObservabilityConfigError.js +39 -0
- package/dist/profile/ObservabilityProfile.d.ts +49 -0
- package/dist/profile/ObservabilityProfile.js +121 -0
- package/dist/profile/ReactWebProfile.d.ts +8 -0
- package/dist/profile/ReactWebProfile.js +7 -0
- package/dist/profile/index.d.ts +5 -0
- package/dist/profile/index.js +5 -0
- package/dist/testing/conformance/ConformanceAssertions.d.ts +5 -0
- package/dist/testing/conformance/ConformanceAssertions.js +31 -0
- package/dist/testing/conformance/ConformanceFailure.d.ts +35 -0
- package/dist/testing/conformance/ConformanceFailure.js +56 -0
- package/dist/testing/conformance/ConformanceModel.d.ts +97 -0
- package/dist/testing/conformance/ConformanceModel.js +8 -0
- package/dist/testing/conformance/ConformanceRegistry.d.ts +13 -0
- package/dist/testing/conformance/ConformanceRegistry.js +190 -0
- package/dist/testing/conformance/ConformanceRunner.d.ts +5 -0
- package/dist/testing/conformance/ConformanceRunner.js +198 -0
- package/dist/testing/conformance/TelemetryEvidence.d.ts +91 -0
- package/dist/testing/conformance/TelemetryEvidence.js +370 -0
- package/dist/testing/conformance/index.d.ts +10 -0
- package/dist/testing/conformance/index.js +6 -0
- package/dist/testing/contract.d.ts +81 -0
- package/dist/testing/contract.js +93 -0
- package/dist/testing/deployedCanary.d.ts +1 -0
- package/dist/testing/deployedCanary.js +1 -0
- package/dist/testing/index.d.ts +40 -3
- package/dist/testing/index.js +122 -5
- package/dist/trace/HttpServerOtlpTracer.d.ts +42 -0
- package/dist/trace/HttpServerOtlpTracer.js +301 -0
- package/package.json +16 -29
- package/dist/WideEvent.d.ts +0 -5
- package/dist/WideEvent.js +0 -6
- package/dist/nestjs/BrowserEventsController.d.ts +0 -21
- package/dist/nestjs/BrowserEventsController.js +0 -52
- package/dist/nestjs/HttpRoutePolicy.d.ts +0 -23
- package/dist/nestjs/HttpRoutePolicy.js +0 -179
- package/dist/nestjs/HttpServerOtlpTracer.d.ts +0 -15
- package/dist/nestjs/HttpServerOtlpTracer.js +0 -154
- package/dist/nestjs/RequestWideEventTraceCorrelation.d.ts +0 -15
- package/dist/nestjs/RequestWideEventTraceCorrelation.js +0 -13
- package/dist/nestjs/TelemetryInterceptor.d.ts +0 -31
- package/dist/nestjs/TelemetryInterceptor.js +0 -268
- package/dist/nestjs/TelemetryModule.d.ts +0 -53
- package/dist/nestjs/TelemetryModule.js +0 -428
- package/dist/nestjs/index.d.ts +0 -5
- package/dist/nestjs/index.js +0 -4
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Duration, Layer } from "effect";
|
|
2
|
-
import type { HttpClient } from "effect/unstable/http";
|
|
3
|
-
import { OtlpExporter, OtlpSerialization } from "effect/unstable/observability";
|
|
4
|
-
export type HttpServerOtlpTracerOptions = {
|
|
5
|
-
readonly url: string;
|
|
6
|
-
readonly resource: {
|
|
7
|
-
readonly serviceName: string;
|
|
8
|
-
readonly serviceVersion: string;
|
|
9
|
-
readonly attributes: {
|
|
10
|
-
readonly "deployment.environment.name": string;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
13
|
-
readonly shutdownTimeout?: Duration.Input | undefined;
|
|
14
|
-
};
|
|
15
|
-
export declare const layerHttpServerOtlpTracer: (options: HttpServerOtlpTracerOptions) => Layer.Layer<OtlpExporter.Flusher, never, HttpClient.HttpClient | OtlpSerialization.OtlpSerialization>;
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { Cause, Duration, Effect, Layer, Option, Schema, Tracer } from "effect";
|
|
2
|
-
import { OtlpExporter, OtlpResource, OtlpSerialization } from "effect/unstable/observability";
|
|
3
|
-
const HttpStatusCode = Schema.Number.check(Schema.isInt(), Schema.isBetween({ minimum: 100, maximum: 599 }));
|
|
4
|
-
const decodeHttpStatusCode = Schema.decodeUnknownOption(HttpStatusCode);
|
|
5
|
-
const statusCodeUnset = 0;
|
|
6
|
-
const statusCodeOk = 1;
|
|
7
|
-
const statusCodeError = 2;
|
|
8
|
-
const spanKindCode = (kind) => {
|
|
9
|
-
switch (kind) {
|
|
10
|
-
case "internal":
|
|
11
|
-
return 1;
|
|
12
|
-
case "server":
|
|
13
|
-
return 2;
|
|
14
|
-
case "client":
|
|
15
|
-
return 3;
|
|
16
|
-
case "producer":
|
|
17
|
-
return 4;
|
|
18
|
-
case "consumer":
|
|
19
|
-
return 5;
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
class ExportingSpan extends Tracer.NativeSpan {
|
|
23
|
-
#exportSpan;
|
|
24
|
-
#ended = false;
|
|
25
|
-
constructor(options, exportSpan) {
|
|
26
|
-
super(options);
|
|
27
|
-
this.#exportSpan = exportSpan;
|
|
28
|
-
}
|
|
29
|
-
end(endTime, exit) {
|
|
30
|
-
if (this.#ended) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
this.#ended = true;
|
|
34
|
-
super.end(endTime, exit);
|
|
35
|
-
if (this.sampled) {
|
|
36
|
-
this.#exportSpan(this);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const makeEvents = (span) => span.events.map(([name, startTime, attributes]) => ({
|
|
41
|
-
name,
|
|
42
|
-
timeUnixNano: String(startTime),
|
|
43
|
-
attributes: OtlpResource.entriesToAttributes(Object.entries(attributes)),
|
|
44
|
-
droppedAttributesCount: 0,
|
|
45
|
-
}));
|
|
46
|
-
const makeNonHttpStatus = (span, attributes, events) => {
|
|
47
|
-
if (span.status._tag !== "Ended") {
|
|
48
|
-
return { code: statusCodeUnset };
|
|
49
|
-
}
|
|
50
|
-
if (span.status.exit._tag === "Success") {
|
|
51
|
-
return { code: statusCodeOk };
|
|
52
|
-
}
|
|
53
|
-
if (Cause.hasInterruptsOnly(span.status.exit.cause)) {
|
|
54
|
-
attributes.push({
|
|
55
|
-
key: "span.label",
|
|
56
|
-
value: { stringValue: "⚠︎ Interrupted" },
|
|
57
|
-
}, {
|
|
58
|
-
key: "status.interrupted",
|
|
59
|
-
value: { boolValue: true },
|
|
60
|
-
});
|
|
61
|
-
return { code: statusCodeOk, message: "Interrupted" };
|
|
62
|
-
}
|
|
63
|
-
const errors = Cause.prettyErrors(span.status.exit.cause, { includeCauseInStack: true });
|
|
64
|
-
for (const error of errors) {
|
|
65
|
-
events.push({
|
|
66
|
-
name: "exception",
|
|
67
|
-
timeUnixNano: String(span.status.endTime),
|
|
68
|
-
droppedAttributesCount: 0,
|
|
69
|
-
attributes: OtlpResource.entriesToAttributes([
|
|
70
|
-
["exception.type", error.name],
|
|
71
|
-
["exception.message", error.message],
|
|
72
|
-
["exception.stacktrace", error.stack ?? "No stack trace available"],
|
|
73
|
-
]),
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
return errors.length === 0
|
|
77
|
-
? { code: statusCodeError }
|
|
78
|
-
: { code: statusCodeError, message: errors[0]?.message };
|
|
79
|
-
};
|
|
80
|
-
const makeHttpStatus = (span) => {
|
|
81
|
-
if (span.attributes.has("error.type")) {
|
|
82
|
-
return { code: statusCodeError };
|
|
83
|
-
}
|
|
84
|
-
return decodeHttpStatusCode(span.attributes.get("http.response.status_code")).pipe(Option.match({
|
|
85
|
-
onNone: () => ({ code: statusCodeUnset }),
|
|
86
|
-
onSome: (status) => ({ code: status >= 500 ? statusCodeError : statusCodeUnset }),
|
|
87
|
-
}));
|
|
88
|
-
};
|
|
89
|
-
const makeOtlpSpan = (span) => {
|
|
90
|
-
if (span.status._tag !== "Ended") {
|
|
91
|
-
return Option.none();
|
|
92
|
-
}
|
|
93
|
-
const attributes = OtlpResource.entriesToAttributes(span.attributes.entries());
|
|
94
|
-
const events = makeEvents(span);
|
|
95
|
-
const isHttpServer = span.kind === "server" && span.attributes.has("http.request.method");
|
|
96
|
-
return Option.some({
|
|
97
|
-
traceId: span.traceId,
|
|
98
|
-
spanId: span.spanId,
|
|
99
|
-
parentSpanId: Option.getOrUndefined(Option.map(span.parent, (parent) => parent.spanId)),
|
|
100
|
-
name: span.name,
|
|
101
|
-
kind: spanKindCode(span.kind),
|
|
102
|
-
startTimeUnixNano: String(span.status.startTime),
|
|
103
|
-
endTimeUnixNano: String(span.status.endTime),
|
|
104
|
-
attributes,
|
|
105
|
-
droppedAttributesCount: 0,
|
|
106
|
-
events,
|
|
107
|
-
droppedEventsCount: 0,
|
|
108
|
-
status: isHttpServer ? makeHttpStatus(span) : makeNonHttpStatus(span, attributes, events),
|
|
109
|
-
links: span.links.map((link) => ({
|
|
110
|
-
traceId: link.span.traceId,
|
|
111
|
-
spanId: link.span.spanId,
|
|
112
|
-
attributes: OtlpResource.entriesToAttributes(Object.entries(link.attributes)),
|
|
113
|
-
droppedAttributesCount: 0,
|
|
114
|
-
})),
|
|
115
|
-
droppedLinksCount: 0,
|
|
116
|
-
});
|
|
117
|
-
};
|
|
118
|
-
const makeHttpServerOtlpTracer = Effect.fn("makeHttpServerOtlpTracer")(function* (options) {
|
|
119
|
-
const resource = yield* OtlpResource.fromConfig(options.resource);
|
|
120
|
-
const serialization = yield* OtlpSerialization.OtlpSerialization;
|
|
121
|
-
const exporter = yield* OtlpExporter.make({
|
|
122
|
-
label: "HttpServerOtlpTracer",
|
|
123
|
-
url: options.url,
|
|
124
|
-
headers: undefined,
|
|
125
|
-
exportInterval: Duration.seconds(5),
|
|
126
|
-
maxBatchSize: 1000,
|
|
127
|
-
body: (spans) => [
|
|
128
|
-
serialization.traces({
|
|
129
|
-
resourceSpans: [
|
|
130
|
-
{
|
|
131
|
-
resource,
|
|
132
|
-
scopeSpans: [
|
|
133
|
-
{
|
|
134
|
-
scope: { name: OtlpResource.serviceNameUnsafe(resource) },
|
|
135
|
-
spans,
|
|
136
|
-
},
|
|
137
|
-
],
|
|
138
|
-
},
|
|
139
|
-
],
|
|
140
|
-
}),
|
|
141
|
-
Effect.void,
|
|
142
|
-
],
|
|
143
|
-
shutdownTimeout: options.shutdownTimeout ?? Duration.seconds(3),
|
|
144
|
-
});
|
|
145
|
-
return Tracer.make({
|
|
146
|
-
span: (spanOptions) => new ExportingSpan(spanOptions, (span) => {
|
|
147
|
-
const exported = makeOtlpSpan(span);
|
|
148
|
-
if (Option.isSome(exported)) {
|
|
149
|
-
exporter.push(exported.value);
|
|
150
|
-
}
|
|
151
|
-
}),
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
export const layerHttpServerOtlpTracer = (options) => Layer.effect(Tracer.Tracer, makeHttpServerOtlpTracer(options)).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export type RequestReference = WeakKey;
|
|
2
|
-
export type ServerSpanCorrelation = {
|
|
3
|
-
readonly traceId: string;
|
|
4
|
-
readonly spanId: string;
|
|
5
|
-
};
|
|
6
|
-
export type RequestWideEventLogger = {
|
|
7
|
-
readonly set: (correlation: ServerSpanCorrelation) => void;
|
|
8
|
-
};
|
|
9
|
-
export type RequestWideEventLoggerResolver = (request: RequestReference) => RequestWideEventLogger | undefined;
|
|
10
|
-
export declare class RequestWideEventTraceCorrelation {
|
|
11
|
-
#private;
|
|
12
|
-
constructor(resolveLogger: RequestWideEventLoggerResolver);
|
|
13
|
-
correlate(request: RequestReference, correlation: ServerSpanCorrelation): void;
|
|
14
|
-
}
|
|
15
|
-
export declare const createRequestWideEventTraceCorrelation: (resolveLogger: RequestWideEventLoggerResolver) => RequestWideEventTraceCorrelation;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export class RequestWideEventTraceCorrelation {
|
|
2
|
-
#resolveLogger;
|
|
3
|
-
constructor(resolveLogger) {
|
|
4
|
-
this.#resolveLogger = resolveLogger;
|
|
5
|
-
}
|
|
6
|
-
correlate(request, correlation) {
|
|
7
|
-
try {
|
|
8
|
-
this.#resolveLogger(request)?.set(correlation);
|
|
9
|
-
}
|
|
10
|
-
catch { }
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
export const createRequestWideEventTraceCorrelation = (resolveLogger) => new RequestWideEventTraceCorrelation(resolveLogger);
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { CallHandler, ExecutionContext, NestInterceptor } from "@nestjs/common";
|
|
2
|
-
import { Effect, Option, Tracer } from "effect";
|
|
3
|
-
import type { ManagedRuntime } from "effect";
|
|
4
|
-
import { Observable } from "rxjs";
|
|
5
|
-
import { type ProxyPolicy } from "./HttpRoutePolicy.js";
|
|
6
|
-
import type { RequestReference, RequestWideEventTraceCorrelation } from "./RequestWideEventTraceCorrelation.js";
|
|
7
|
-
export declare const requestSpan: (request: RequestReference) => Option.Option<Tracer.Span>;
|
|
8
|
-
export declare const withRequestSpan: (request: RequestReference) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
9
|
-
type ActiveRequest = {
|
|
10
|
-
readonly interrupt: () => void;
|
|
11
|
-
};
|
|
12
|
-
export declare class TelemetryRequestTracker {
|
|
13
|
-
#private;
|
|
14
|
-
get accepting(): boolean;
|
|
15
|
-
register(activeRequest: ActiveRequest): Option.Option<() => void>;
|
|
16
|
-
closeAdmission(): void;
|
|
17
|
-
waitForIdle(): Promise<void>;
|
|
18
|
-
interruptActive(): void;
|
|
19
|
-
}
|
|
20
|
-
export type TelemetryInterceptorOptions = {
|
|
21
|
-
readonly healthRouteTemplates?: ReadonlyArray<string> | undefined;
|
|
22
|
-
readonly proxyPolicy?: ProxyPolicy | undefined;
|
|
23
|
-
readonly requestTracker?: TelemetryRequestTracker | undefined;
|
|
24
|
-
readonly requestWideEventTraceCorrelation?: RequestWideEventTraceCorrelation | undefined;
|
|
25
|
-
};
|
|
26
|
-
export declare class TelemetryInterceptor<RuntimeError> implements NestInterceptor {
|
|
27
|
-
#private;
|
|
28
|
-
constructor(runtime: ManagedRuntime.ManagedRuntime<never, RuntimeError>, options?: TelemetryInterceptorOptions);
|
|
29
|
-
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
30
|
-
}
|
|
31
|
-
export {};
|
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
import { Context, Effect, Exit, Option, Predicate, Schema, Tracer } from "effect";
|
|
2
|
-
import { EventEmitter } from "node:events";
|
|
3
|
-
import { Observable } from "rxjs";
|
|
4
|
-
import { telemetryRoutePolicy, } from "./HttpRoutePolicy.js";
|
|
5
|
-
const TraceparentRequest = Schema.Struct({
|
|
6
|
-
headers: Schema.Struct({
|
|
7
|
-
traceparent: Schema.String.check(Schema.isPattern(/^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/), Schema.makeFilter((traceparent) => traceparent.slice(3, 35) !== "00000000000000000000000000000000" &&
|
|
8
|
-
traceparent.slice(36, 52) !== "0000000000000000")),
|
|
9
|
-
}),
|
|
10
|
-
});
|
|
11
|
-
const parseTraceparentRequest = Schema.decodeUnknownOption(TraceparentRequest);
|
|
12
|
-
const HttpResponseBoundary = Schema.Struct({
|
|
13
|
-
statusCode: Schema.Number.check(Schema.isInt(), Schema.isBetween({ minimum: 100, maximum: 599 })),
|
|
14
|
-
});
|
|
15
|
-
const decodeHttpResponseBoundary = Schema.decodeUnknownOption(HttpResponseBoundary);
|
|
16
|
-
const HttpErrorBoundary = Schema.Struct({
|
|
17
|
-
status: Schema.Number.check(Schema.isInt(), Schema.isBetween({ minimum: 100, maximum: 599 })).pipe(Schema.optionalKey),
|
|
18
|
-
});
|
|
19
|
-
const ErrorType = Schema.NonEmptyString.check(Schema.isMaxLength(128));
|
|
20
|
-
const decodeHttpErrorBoundary = Schema.decodeUnknownOption(HttpErrorBoundary);
|
|
21
|
-
const decodeErrorType = Schema.decodeUnknownOption(ErrorType);
|
|
22
|
-
const decodeHeadersSent = Schema.decodeUnknownOption(Schema.Boolean);
|
|
23
|
-
const decodeResponseEmitter = Schema.decodeUnknownOption(Schema.instanceOf(EventEmitter));
|
|
24
|
-
const requestSpans = new WeakMap();
|
|
25
|
-
export const requestSpan = (request) => Option.fromNullishOr(requestSpans.get(request));
|
|
26
|
-
export const withRequestSpan = (request) => (effect) => Option.match(requestSpan(request), {
|
|
27
|
-
onNone: () => effect,
|
|
28
|
-
onSome: (span) => Effect.withParentSpan(effect, span),
|
|
29
|
-
});
|
|
30
|
-
export class TelemetryRequestTracker {
|
|
31
|
-
#accepting = true;
|
|
32
|
-
#active = new Set();
|
|
33
|
-
#idleWaiters = new Set();
|
|
34
|
-
get accepting() {
|
|
35
|
-
return this.#accepting;
|
|
36
|
-
}
|
|
37
|
-
register(activeRequest) {
|
|
38
|
-
if (!this.#accepting) {
|
|
39
|
-
return Option.none();
|
|
40
|
-
}
|
|
41
|
-
this.#active.add(activeRequest);
|
|
42
|
-
return Option.some(() => {
|
|
43
|
-
if (!this.#active.delete(activeRequest) || this.#active.size !== 0) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
for (const resolve of this.#idleWaiters) {
|
|
47
|
-
resolve();
|
|
48
|
-
}
|
|
49
|
-
this.#idleWaiters.clear();
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
closeAdmission() {
|
|
53
|
-
this.#accepting = false;
|
|
54
|
-
}
|
|
55
|
-
waitForIdle() {
|
|
56
|
-
if (this.#active.size === 0) {
|
|
57
|
-
return Promise.resolve();
|
|
58
|
-
}
|
|
59
|
-
return new Promise((resolve) => this.#idleWaiters.add(resolve));
|
|
60
|
-
}
|
|
61
|
-
interruptActive() {
|
|
62
|
-
for (const activeRequest of this.#active) {
|
|
63
|
-
activeRequest.interrupt();
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
export class TelemetryInterceptor {
|
|
68
|
-
#runtime;
|
|
69
|
-
#routePolicy;
|
|
70
|
-
#requestTracker;
|
|
71
|
-
#requestWideEventTraceCorrelation;
|
|
72
|
-
#tracer;
|
|
73
|
-
#clock;
|
|
74
|
-
constructor(runtime, options = {}) {
|
|
75
|
-
this.#runtime = runtime;
|
|
76
|
-
this.#routePolicy = telemetryRoutePolicy({
|
|
77
|
-
healthRouteTemplates: options.healthRouteTemplates,
|
|
78
|
-
proxyPolicy: options.proxyPolicy,
|
|
79
|
-
});
|
|
80
|
-
this.#requestTracker = options.requestTracker ?? new TelemetryRequestTracker();
|
|
81
|
-
this.#requestWideEventTraceCorrelation = options.requestWideEventTraceCorrelation;
|
|
82
|
-
}
|
|
83
|
-
intercept(context, next) {
|
|
84
|
-
if (context.getType() !== "http" || !this.#requestTracker.accepting) {
|
|
85
|
-
return next.handle();
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
return this.#instrument(context, next);
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
return next.handle();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
#instrument(context, next) {
|
|
95
|
-
const httpContext = context.switchToHttp();
|
|
96
|
-
const request = httpContext.getRequest();
|
|
97
|
-
if (requestSpans.has(request)) {
|
|
98
|
-
return next.handle();
|
|
99
|
-
}
|
|
100
|
-
const requestDetails = this.#routePolicy.inspect(request);
|
|
101
|
-
if (Option.isNone(requestDetails)) {
|
|
102
|
-
return next.handle();
|
|
103
|
-
}
|
|
104
|
-
const tracer = (this.#tracer ??= this.#runtime.runSync(Effect.tracer));
|
|
105
|
-
const clock = (this.#clock ??= this.#runtime.runSync(Effect.clockWith(Effect.succeed)));
|
|
106
|
-
const traceparentRequest = httpContext.getRequest();
|
|
107
|
-
const parent = parseTraceparentRequest({ headers: traceparentRequest.headers }).pipe(Option.map(({ headers }) => {
|
|
108
|
-
const traceparent = headers.traceparent;
|
|
109
|
-
return Tracer.externalSpan({
|
|
110
|
-
traceId: traceparent.slice(3, 35),
|
|
111
|
-
spanId: traceparent.slice(36, 52),
|
|
112
|
-
sampled: Number.parseInt(traceparent.slice(53, 55), 16) % 2 === 1,
|
|
113
|
-
});
|
|
114
|
-
}));
|
|
115
|
-
const details = requestDetails.value;
|
|
116
|
-
const span = tracer.span({
|
|
117
|
-
name: details.spanName,
|
|
118
|
-
parent,
|
|
119
|
-
annotations: Context.empty(),
|
|
120
|
-
links: [],
|
|
121
|
-
startTime: clock.currentTimeNanosUnsafe(),
|
|
122
|
-
kind: "server",
|
|
123
|
-
root: Option.isNone(parent),
|
|
124
|
-
sampled: Option.match(parent, {
|
|
125
|
-
onNone: () => true,
|
|
126
|
-
onSome: (remoteParent) => remoteParent.sampled,
|
|
127
|
-
}),
|
|
128
|
-
});
|
|
129
|
-
span.attribute("http.request.method", details.method);
|
|
130
|
-
if (Option.isSome(details.methodOriginal)) {
|
|
131
|
-
span.attribute("http.request.method_original", details.methodOriginal.value);
|
|
132
|
-
}
|
|
133
|
-
if (Option.isSome(details.route)) {
|
|
134
|
-
span.attribute("http.route", details.route.value);
|
|
135
|
-
}
|
|
136
|
-
if (Option.isSome(details.urlPath)) {
|
|
137
|
-
span.attribute("url.path", details.urlPath.value);
|
|
138
|
-
}
|
|
139
|
-
if (Option.isSome(details.urlScheme)) {
|
|
140
|
-
span.attribute("url.scheme", details.urlScheme.value);
|
|
141
|
-
}
|
|
142
|
-
if (Option.isSome(details.clientAddress)) {
|
|
143
|
-
span.attribute("client.address", details.clientAddress.value);
|
|
144
|
-
}
|
|
145
|
-
if (Option.isSome(details.networkPeerAddress)) {
|
|
146
|
-
span.attribute("network.peer.address", details.networkPeerAddress.value);
|
|
147
|
-
}
|
|
148
|
-
if (Option.isSome(details.networkPeerPort)) {
|
|
149
|
-
span.attribute("network.peer.port", details.networkPeerPort.value);
|
|
150
|
-
}
|
|
151
|
-
if (Option.isSome(details.serverAddress)) {
|
|
152
|
-
span.attribute("server.address", details.serverAddress.value);
|
|
153
|
-
}
|
|
154
|
-
requestSpans.set(request, span);
|
|
155
|
-
this.#requestWideEventTraceCorrelation?.correlate(request, {
|
|
156
|
-
traceId: span.traceId,
|
|
157
|
-
spanId: span.spanId,
|
|
158
|
-
});
|
|
159
|
-
const response = httpContext.getResponse();
|
|
160
|
-
const responseEmitter = decodeResponseEmitter(response);
|
|
161
|
-
return new Observable((subscriber) => {
|
|
162
|
-
let ended = false;
|
|
163
|
-
let responseFinished = false;
|
|
164
|
-
let observableSettled = false;
|
|
165
|
-
let pendingExit = Exit.succeed(undefined);
|
|
166
|
-
let pendingErrorType = Option.none();
|
|
167
|
-
let release = () => { };
|
|
168
|
-
const responseStatus = (requireSent = false) => {
|
|
169
|
-
if (requireSent) {
|
|
170
|
-
const headersSent = Predicate.hasProperty(response, "headersSent")
|
|
171
|
-
? decodeHeadersSent(response.headersSent)
|
|
172
|
-
: Option.none();
|
|
173
|
-
if (!Option.getOrElse(headersSent, () => false)) {
|
|
174
|
-
return Option.none();
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return decodeHttpResponseBoundary(response).pipe(Option.map((boundary) => boundary.statusCode));
|
|
178
|
-
};
|
|
179
|
-
const removeResponseListeners = () => {
|
|
180
|
-
if (Option.isSome(responseEmitter)) {
|
|
181
|
-
responseEmitter.value.off("finish", onResponseFinish);
|
|
182
|
-
responseEmitter.value.off("close", onResponseClose);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
const finish = (exit, status, errorType) => {
|
|
186
|
-
if (ended) {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
ended = true;
|
|
190
|
-
removeResponseListeners();
|
|
191
|
-
if (Option.isSome(status)) {
|
|
192
|
-
span.attribute("http.response.status_code", status.value);
|
|
193
|
-
}
|
|
194
|
-
const finalErrorType = Option.contains(errorType, "connection_closed")
|
|
195
|
-
? errorType
|
|
196
|
-
: Option.match(status, {
|
|
197
|
-
onNone: () => errorType,
|
|
198
|
-
onSome: (statusCode) => {
|
|
199
|
-
if (statusCode >= 500) {
|
|
200
|
-
return Option.some(String(statusCode));
|
|
201
|
-
}
|
|
202
|
-
if (statusCode >= 400) {
|
|
203
|
-
return Option.none();
|
|
204
|
-
}
|
|
205
|
-
return errorType;
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
if (Option.isSome(finalErrorType)) {
|
|
209
|
-
span.attribute("error.type", finalErrorType.value);
|
|
210
|
-
}
|
|
211
|
-
requestSpans.delete(request);
|
|
212
|
-
release();
|
|
213
|
-
span.end(clock.currentTimeNanosUnsafe(), exit);
|
|
214
|
-
};
|
|
215
|
-
const onResponseFinish = () => {
|
|
216
|
-
responseFinished = true;
|
|
217
|
-
finish(pendingExit, responseStatus(), pendingErrorType);
|
|
218
|
-
};
|
|
219
|
-
const onResponseClose = () => {
|
|
220
|
-
if (!responseFinished) {
|
|
221
|
-
finish(Exit.interrupt(), responseStatus(true), Option.some("connection_closed"));
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
const registered = this.#requestTracker.register({
|
|
225
|
-
interrupt: () => finish(Exit.interrupt(), responseStatus(true), Option.none()),
|
|
226
|
-
});
|
|
227
|
-
if (Option.isNone(registered)) {
|
|
228
|
-
requestSpans.delete(request);
|
|
229
|
-
span.end(clock.currentTimeNanosUnsafe(), Exit.interrupt());
|
|
230
|
-
return next.handle().subscribe(subscriber);
|
|
231
|
-
}
|
|
232
|
-
release = registered.value;
|
|
233
|
-
if (Option.isSome(responseEmitter)) {
|
|
234
|
-
responseEmitter.value.on("finish", onResponseFinish);
|
|
235
|
-
responseEmitter.value.on("close", onResponseClose);
|
|
236
|
-
}
|
|
237
|
-
const subscription = next.handle().subscribe({
|
|
238
|
-
next: (value) => subscriber.next(value),
|
|
239
|
-
error: (cause) => {
|
|
240
|
-
observableSettled = true;
|
|
241
|
-
const errorBoundary = decodeHttpErrorBoundary(cause);
|
|
242
|
-
pendingExit = Exit.die(cause);
|
|
243
|
-
pendingErrorType = Predicate.hasProperty(cause, "name")
|
|
244
|
-
? decodeErrorType(cause.name).pipe(Option.orElse(() => Option.some("exception")))
|
|
245
|
-
: Option.some("exception");
|
|
246
|
-
if (Option.isNone(responseEmitter)) {
|
|
247
|
-
const status = errorBoundary.pipe(Option.flatMap((boundary) => Option.fromNullishOr(boundary.status)));
|
|
248
|
-
finish(pendingExit, status, pendingErrorType);
|
|
249
|
-
}
|
|
250
|
-
subscriber.error(cause);
|
|
251
|
-
},
|
|
252
|
-
complete: () => {
|
|
253
|
-
observableSettled = true;
|
|
254
|
-
if (Option.isNone(responseEmitter)) {
|
|
255
|
-
finish(Exit.succeed(undefined), responseStatus(), Option.none());
|
|
256
|
-
}
|
|
257
|
-
subscriber.complete();
|
|
258
|
-
},
|
|
259
|
-
});
|
|
260
|
-
return () => {
|
|
261
|
-
if (!observableSettled && Option.isNone(responseEmitter)) {
|
|
262
|
-
finish(Exit.interrupt(), responseStatus(), Option.none());
|
|
263
|
-
}
|
|
264
|
-
subscription.unsubscribe();
|
|
265
|
-
};
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { DynamicModule, InjectionToken, ModuleMetadata, OptionalFactoryDependency } from "@nestjs/common";
|
|
2
|
-
import { type ProxyPolicy } from "./HttpRoutePolicy.js";
|
|
3
|
-
import { RequestWideEventTraceCorrelation } from "./RequestWideEventTraceCorrelation.js";
|
|
4
|
-
export type TelemetryModuleOptions = {
|
|
5
|
-
readonly enabled: false;
|
|
6
|
-
} | {
|
|
7
|
-
readonly enabled: true;
|
|
8
|
-
readonly serviceName: string;
|
|
9
|
-
readonly serviceVersion: string;
|
|
10
|
-
readonly environment: string;
|
|
11
|
-
readonly otlpEndpoint: string;
|
|
12
|
-
readonly healthRouteTemplates?: ReadonlyArray<string> | undefined;
|
|
13
|
-
readonly proxyPolicy?: ProxyPolicy | undefined;
|
|
14
|
-
readonly requestWideEventTraceCorrelation?: RequestWideEventTraceCorrelation | undefined;
|
|
15
|
-
readonly shutdownTimeoutMilliseconds?: number | undefined;
|
|
16
|
-
};
|
|
17
|
-
export interface TelemetryModuleAsyncOptions {
|
|
18
|
-
readonly imports?: ModuleMetadata["imports"] | undefined;
|
|
19
|
-
readonly inject?: Array<InjectionToken | OptionalFactoryDependency> | undefined;
|
|
20
|
-
readonly useFactory: (...dependencies: Array<never>) => TelemetryModuleOptions | Promise<TelemetryModuleOptions>;
|
|
21
|
-
}
|
|
22
|
-
export declare class InvalidTelemetryModuleOptions extends Error {
|
|
23
|
-
readonly _tag = "InvalidTelemetryModuleOptions";
|
|
24
|
-
readonly code = "OBS_TELEMETRY_INVALID_MODULE_OPTIONS";
|
|
25
|
-
readonly cause: unknown;
|
|
26
|
-
constructor(cause: unknown);
|
|
27
|
-
}
|
|
28
|
-
export declare class TelemetryStartupError extends Error {
|
|
29
|
-
readonly _tag = "TelemetryStartupError";
|
|
30
|
-
readonly code = "OBS_TELEMETRY_STARTUP_FAILED";
|
|
31
|
-
readonly cause: unknown;
|
|
32
|
-
constructor(cause: unknown);
|
|
33
|
-
}
|
|
34
|
-
export declare class TelemetryShutdownError extends Error {
|
|
35
|
-
readonly _tag = "TelemetryShutdownError";
|
|
36
|
-
readonly code = "OBS_TELEMETRY_SHUTDOWN_FAILED";
|
|
37
|
-
readonly cause: unknown;
|
|
38
|
-
constructor(cause: unknown);
|
|
39
|
-
}
|
|
40
|
-
export interface TelemetryModuleTestingOverrides {
|
|
41
|
-
readonly scopedResource?: {
|
|
42
|
-
readonly acquire: () => void | Promise<void>;
|
|
43
|
-
readonly release: () => void | Promise<void>;
|
|
44
|
-
} | undefined;
|
|
45
|
-
readonly startupProbe?: (() => void | Promise<void>) | undefined;
|
|
46
|
-
readonly beforeRequestDrain?: (() => void | Promise<void>) | undefined;
|
|
47
|
-
readonly beforeRuntimeDispose?: (() => void | Promise<void>) | undefined;
|
|
48
|
-
readonly onRuntimeDisposed?: (() => void) | undefined;
|
|
49
|
-
}
|
|
50
|
-
export declare const telemetryModuleForTesting: (options: TelemetryModuleAsyncOptions, overrides: TelemetryModuleTestingOverrides) => DynamicModule;
|
|
51
|
-
export declare class TelemetryModule {
|
|
52
|
-
static forRootAsync(options: TelemetryModuleAsyncOptions): DynamicModule;
|
|
53
|
-
}
|