@equipe-tech/observability 0.2.0 → 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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { DateTime, Option, Schema } from "effect";
|
|
2
|
+
export const EventSeverity = Schema.Literals(["debug", "info", "warn", "error", "fatal"]);
|
|
3
|
+
export const EventOutcome = Schema.Literals(["success", "failure", "cancelled"]);
|
|
4
|
+
export const EventKind = Schema.Literals(["request", "operation", "domain", "defect", "audit"]);
|
|
5
|
+
export const HttpContext = Schema.Struct({
|
|
6
|
+
method: Schema.NonEmptyString,
|
|
7
|
+
route: Schema.NonEmptyString,
|
|
8
|
+
statusCode: Schema.Int.check(Schema.makeFilter((statusCode) => statusCode >= 100 && statusCode <= 599, {
|
|
9
|
+
expected: "an HTTP status code from 100 to 599",
|
|
10
|
+
})),
|
|
11
|
+
});
|
|
12
|
+
export const ErrorContext = Schema.Struct({
|
|
13
|
+
type: Schema.NonEmptyString,
|
|
14
|
+
message: Schema.String,
|
|
15
|
+
retryable: Schema.Boolean,
|
|
16
|
+
});
|
|
17
|
+
export const maxOtlpUnixTimestampMillis = 18_446_744_073_709;
|
|
18
|
+
const rfc3339UtcPattern = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?Z$/;
|
|
19
|
+
const isLeapYear = (year) => year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
20
|
+
const daysInMonth = (year, month) => {
|
|
21
|
+
if (month === 2) {
|
|
22
|
+
return isLeapYear(year) ? 29 : 28;
|
|
23
|
+
}
|
|
24
|
+
return [4, 6, 9, 11].includes(month) ? 30 : 31;
|
|
25
|
+
};
|
|
26
|
+
const isValidTimestamp = (timestamp) => {
|
|
27
|
+
const parts = rfc3339UtcPattern.exec(timestamp);
|
|
28
|
+
if (parts === null) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const year = Number(parts[1]);
|
|
32
|
+
const month = Number(parts[2]);
|
|
33
|
+
const day = Number(parts[3]);
|
|
34
|
+
const hour = Number(parts[4]);
|
|
35
|
+
const minute = Number(parts[5]);
|
|
36
|
+
const second = Number(parts[6]);
|
|
37
|
+
const milliseconds = Date.parse(timestamp);
|
|
38
|
+
return (month >= 1 &&
|
|
39
|
+
month <= 12 &&
|
|
40
|
+
day >= 1 &&
|
|
41
|
+
day <= daysInMonth(year, month) &&
|
|
42
|
+
hour <= 23 &&
|
|
43
|
+
minute <= 59 &&
|
|
44
|
+
second <= 59 &&
|
|
45
|
+
Option.isSome(DateTime.make(timestamp)) &&
|
|
46
|
+
milliseconds >= 0 &&
|
|
47
|
+
milliseconds <= maxOtlpUnixTimestampMillis);
|
|
48
|
+
};
|
|
49
|
+
export const EventTimestamp = Schema.String.check(Schema.makeFilter(isValidTimestamp, {
|
|
50
|
+
expected: `an RFC 3339 UTC timestamp from 1970-01-01T00:00:00.000Z through ${new Date(maxOtlpUnixTimestampMillis).toISOString()}`,
|
|
51
|
+
})).pipe(Schema.brand("EventTimestamp"));
|
|
52
|
+
export const EventDuration = Schema.Finite.check(Schema.makeFilter((durationMs) => durationMs >= 0, {
|
|
53
|
+
expected: "a finite non-negative duration",
|
|
54
|
+
})).pipe(Schema.brand("EventDuration"));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./ContractCompatibility.js";
|
|
2
|
+
export * from "./ContractIndex.js";
|
|
3
|
+
export * from "./ContractSurface.js";
|
|
4
|
+
export * from "./EventName.js";
|
|
5
|
+
export * from "./EventProducer.js";
|
|
6
|
+
export * from "./MetricContractError.js";
|
|
7
|
+
export * from "./MetricProducer.js";
|
|
8
|
+
export * from "./OrganizationEvents.js";
|
|
9
|
+
export * from "./TelemetryContract.js";
|
|
10
|
+
export * from "./TelemetryContractError.js";
|
|
11
|
+
export * from "./TelemetryEvent.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./ContractCompatibility.js";
|
|
2
|
+
export * from "./ContractIndex.js";
|
|
3
|
+
export * from "./ContractSurface.js";
|
|
4
|
+
export * from "./EventName.js";
|
|
5
|
+
export * from "./EventProducer.js";
|
|
6
|
+
export * from "./MetricContractError.js";
|
|
7
|
+
export * from "./MetricProducer.js";
|
|
8
|
+
export * from "./OrganizationEvents.js";
|
|
9
|
+
export * from "./TelemetryContract.js";
|
|
10
|
+
export * from "./TelemetryContractError.js";
|
|
11
|
+
export * from "./TelemetryEvent.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { sanitizeEventName } from "../policy/BrowserFieldPolicy.js";
|
|
3
|
+
import { CurrentDataPolicy } from "../policy/DataPolicy.js";
|
|
4
|
+
import { transformSignalFields } from "../policy/PolicyTransform.js";
|
|
5
|
+
import { effectDroppedAttributesKey } from "../policy/PolicyVocabulary.js";
|
|
6
|
+
export const emit = (name, fields) => Effect.flatMap(CurrentDataPolicy, (policy) => {
|
|
7
|
+
const applicationFields = { ...fields };
|
|
8
|
+
delete applicationFields["event.name"];
|
|
9
|
+
delete applicationFields["event.kind"];
|
|
10
|
+
const eventName = sanitizeEventName(name);
|
|
11
|
+
const decision = transformSignalFields(policy, "log", {
|
|
12
|
+
"event.name": eventName,
|
|
13
|
+
"event.kind": "wide",
|
|
14
|
+
...applicationFields,
|
|
15
|
+
});
|
|
16
|
+
return Effect.logInfo(eventName).pipe(Effect.annotateLogs({
|
|
17
|
+
...decision.value,
|
|
18
|
+
[effectDroppedAttributesKey]: decision.dropped,
|
|
19
|
+
}));
|
|
20
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Effect, Layer, Option, Tracer } from "effect";
|
|
2
|
+
import { TelemetryEventSink } from "../contract/EventProducer.js";
|
|
3
|
+
import * as WideEvent from "./WideEvent.js";
|
|
4
|
+
const fieldsForEvent = (event) => {
|
|
5
|
+
const fields = {
|
|
6
|
+
"event.type": event.kind,
|
|
7
|
+
"event.severity": event.severity,
|
|
8
|
+
"event.outcome": event.outcome,
|
|
9
|
+
"event.timestamp": event.timestamp,
|
|
10
|
+
};
|
|
11
|
+
for (const [name, value] of Object.entries(event.attributes)) {
|
|
12
|
+
fields[name] = value;
|
|
13
|
+
}
|
|
14
|
+
if (Option.isSome(event.correlation.requestId)) {
|
|
15
|
+
fields["request.id"] = event.correlation.requestId.value;
|
|
16
|
+
}
|
|
17
|
+
if (Option.isSome(event.correlation.runId)) {
|
|
18
|
+
fields["run.id"] = event.correlation.runId.value;
|
|
19
|
+
}
|
|
20
|
+
switch (event.kind) {
|
|
21
|
+
case "request":
|
|
22
|
+
fields["event.duration_ms"] = event.durationMs;
|
|
23
|
+
fields["http.request.method"] = event.http.method;
|
|
24
|
+
fields["http.route"] = event.http.route;
|
|
25
|
+
fields["http.response.status_code"] = event.http.statusCode;
|
|
26
|
+
break;
|
|
27
|
+
case "operation":
|
|
28
|
+
fields["event.duration_ms"] = event.durationMs;
|
|
29
|
+
break;
|
|
30
|
+
case "defect":
|
|
31
|
+
fields["error.type"] = event.error.type;
|
|
32
|
+
fields["error.message"] = event.error.message;
|
|
33
|
+
fields["error.retryable"] = event.error.retryable;
|
|
34
|
+
break;
|
|
35
|
+
case "audit":
|
|
36
|
+
fields["event.outcome"] = event.outcome === "success" ? "success" : "failure";
|
|
37
|
+
fields["audit.outcome"] = event.outcome;
|
|
38
|
+
fields["audit.action"] = event.audit.action;
|
|
39
|
+
fields["audit.actor.kind"] = event.audit.actor.kind;
|
|
40
|
+
fields["audit.actor.id"] =
|
|
41
|
+
event.audit.actor.kind === "system" ? "system" : event.audit.actor.id;
|
|
42
|
+
fields["audit.resource.type"] = event.audit.resourceType;
|
|
43
|
+
fields["audit.resource.id"] = event.audit.resourceId;
|
|
44
|
+
if (event.audit.reasonCode !== undefined)
|
|
45
|
+
fields["audit.reason_code"] = event.audit.reasonCode;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
return fields;
|
|
49
|
+
};
|
|
50
|
+
export const layerWideEvent = Layer.succeed(TelemetryEventSink, TelemetryEventSink.of({
|
|
51
|
+
record: (event, admission) => WideEvent.emit(event.name, {
|
|
52
|
+
...fieldsForEvent(event),
|
|
53
|
+
"event.policy_dropped_attributes": admission.policyDroppedAttributes,
|
|
54
|
+
}),
|
|
55
|
+
admitBrowserBatch: (events) => Effect.succeed({
|
|
56
|
+
commit: Effect.forEach(events, (event) => {
|
|
57
|
+
const fields = {
|
|
58
|
+
...event.attributes,
|
|
59
|
+
"event.source": "browser",
|
|
60
|
+
"event.outcome": event.error === undefined ? "success" : "failure",
|
|
61
|
+
"browser.event.id": event.id,
|
|
62
|
+
"browser.event.occurred_at": event.occurredAt,
|
|
63
|
+
"event.policy_dropped_attributes": event.admission.policyDroppedAttributes,
|
|
64
|
+
};
|
|
65
|
+
if (event.error !== undefined) {
|
|
66
|
+
fields["error.type"] = event.error.type;
|
|
67
|
+
fields["error.message"] = event.error.message;
|
|
68
|
+
fields["error.retryable"] = event.error.retryable;
|
|
69
|
+
}
|
|
70
|
+
const emission = WideEvent.emit(event.name, fields);
|
|
71
|
+
return event.trace === undefined
|
|
72
|
+
? emission
|
|
73
|
+
: emission.pipe(Effect.withParentSpan(Tracer.externalSpan({
|
|
74
|
+
traceId: event.trace.traceId,
|
|
75
|
+
spanId: event.trace.spanId,
|
|
76
|
+
})));
|
|
77
|
+
}, { discard: true }),
|
|
78
|
+
}),
|
|
79
|
+
}));
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
export * as BrowserEvents from "./BrowserEvents.js";
|
|
2
|
+
export * from "./audit/index.js";
|
|
3
|
+
export * as Contract from "./contract/index.js";
|
|
4
|
+
export { CorrelationContext, CorrelationField, CurrentCorrelation, generateRunId, InvalidCorrelationContext, parseRequestId, parseRunId, parseSpanId, parseTraceId, RequestId, RunId, SpanId, TraceId, TraceLinkage, withBackgroundCorrelation, withCorrelation, } from "./Correlation.js";
|
|
5
|
+
export { defineTelemetryContract, validateContractEvent } from "./contract/TelemetryContract.js";
|
|
6
|
+
export type { BrowserTelemetryEvent, EventPayloadOf, EventProducer, EventAdmissionMetadata, EmitReceipt, } from "./contract/EventProducer.js";
|
|
7
|
+
export { makeEventProducer, TelemetryEventSink } from "./contract/EventProducer.js";
|
|
8
|
+
export { makeMetricProducer } from "./contract/MetricProducer.js";
|
|
9
|
+
export type { ContractCounter, ContractGaugeObservation, ContractHistogram, MetricAttributesOf, MetricAttributeValueOf, MetricProducer, } from "./contract/MetricProducer.js";
|
|
10
|
+
export { InvalidMetricMeasurement } from "./contract/MetricContractError.js";
|
|
11
|
+
export type { MetricMeasurementErrorCode } from "./contract/MetricContractError.js";
|
|
12
|
+
export type { AuditActionDefinition, AuditActionDefinitionInput, TelemetryContract, TelemetryContractInput, } from "./contract/TelemetryContract.js";
|
|
13
|
+
export type { EventAttributes, TelemetryEvent } from "./contract/TelemetryEvent.js";
|
|
2
14
|
export * as Telemetry from "./Telemetry.js";
|
|
3
|
-
export
|
|
4
|
-
export *
|
|
15
|
+
export * from "./profile/index.js";
|
|
16
|
+
export * from "./policy/index.js";
|
|
17
|
+
export { EnvironmentAliasPolicy, EnvironmentName, InvalidResourceIdentity, instanceResourceAttributes, parseResourceIdentity, ResourceIdentity, ResourceIdentityField, ServiceInstanceId, ServiceName, serviceNamespace, serviceResourceAttributes, ServiceVersion, } from "./ResourceIdentity.js";
|
|
18
|
+
export type { ResourceAttributes, ResourceIdentityInput } from "./ResourceIdentity.js";
|
|
19
|
+
export { InvalidTelemetryEnvironment, OtlpEndpoint, TelemetryConfig, telemetryConfigFromEnv, } from "./TelemetryConfig.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
export * as BrowserEvents from "./BrowserEvents.js";
|
|
2
|
+
export * from "./audit/index.js";
|
|
3
|
+
export * as Contract from "./contract/index.js";
|
|
4
|
+
export { CorrelationContext, CorrelationField, CurrentCorrelation, generateRunId, InvalidCorrelationContext, parseRequestId, parseRunId, parseSpanId, parseTraceId, RequestId, RunId, SpanId, TraceId, TraceLinkage, withBackgroundCorrelation, withCorrelation, } from "./Correlation.js";
|
|
5
|
+
export { defineTelemetryContract, validateContractEvent } from "./contract/TelemetryContract.js";
|
|
6
|
+
export { makeEventProducer, TelemetryEventSink } from "./contract/EventProducer.js";
|
|
7
|
+
export { makeMetricProducer } from "./contract/MetricProducer.js";
|
|
8
|
+
export { InvalidMetricMeasurement } from "./contract/MetricContractError.js";
|
|
2
9
|
export * as Telemetry from "./Telemetry.js";
|
|
3
|
-
export
|
|
4
|
-
export *
|
|
10
|
+
export * from "./profile/index.js";
|
|
11
|
+
export * from "./policy/index.js";
|
|
12
|
+
export { EnvironmentAliasPolicy, EnvironmentName, InvalidResourceIdentity, instanceResourceAttributes, parseResourceIdentity, ResourceIdentity, ResourceIdentityField, ServiceInstanceId, ServiceName, serviceNamespace, serviceResourceAttributes, ServiceVersion, } from "./ResourceIdentity.js";
|
|
13
|
+
export { InvalidTelemetryEnvironment, OtlpEndpoint, TelemetryConfig, telemetryConfigFromEnv, } from "./TelemetryConfig.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const instrumentNamePattern: RegExp;
|
|
2
|
+
export declare const unitPattern: RegExp;
|
|
3
|
+
export declare const maximumMetricAttributes = 16;
|
|
4
|
+
export declare const maximumMetricAttributeCardinality = 100;
|
|
5
|
+
export declare const containsControlCharacter: (value: string) => boolean;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const instrumentNamePattern = /^[A-Za-z][A-Za-z0-9_.\-/]{0,254}$/;
|
|
2
|
+
export const unitPattern = /^(?:1|%|[A-Za-z][A-Za-z0-9]*(?:[./*^][A-Za-z0-9]+)*)$/;
|
|
3
|
+
export const maximumMetricAttributes = 16;
|
|
4
|
+
export const maximumMetricAttributeCardinality = 100;
|
|
5
|
+
export const containsControlCharacter = (value) => {
|
|
6
|
+
for (const character of value) {
|
|
7
|
+
const codePoint = character.codePointAt(0);
|
|
8
|
+
if (codePoint !== undefined && (codePoint <= 31 || codePoint === 127))
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Predicate, Schema } from "effect";
|
|
2
|
+
const metricScalarSchema = Schema.Union([Schema.String, Schema.Number, Schema.Boolean]);
|
|
3
|
+
const isMetricScalar = Schema.is(metricScalarSchema);
|
|
4
|
+
export const isFiniteMetricScalar = (value) => isMetricScalar(value) && (!Predicate.isNumber(value) || Number.isFinite(value));
|
|
5
|
+
export const metricScalarIdentity = (value) => `${Predicate.isString(value) ? "string" : Predicate.isNumber(value) ? "number" : "boolean"}:${String(value)}`;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { Effect, Layer, Schema } from "effect";
|
|
3
|
+
import { AuditDigest, AuditHash } from "../audit/AuditDigest.js";
|
|
4
|
+
export const layerNodeAuditDigest = Layer.succeed(AuditDigest, AuditDigest.of({
|
|
5
|
+
hash: (payload) => Effect.sync(() => Schema.decodeUnknownSync(AuditHash)(createHash("sha256").update(payload).digest("hex"))),
|
|
6
|
+
}));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import { BrowserEventBatch } from "../BrowserEvents.js";
|
|
3
|
+
import { TelemetryEventSink } from "../contract/EventProducer.js";
|
|
3
4
|
declare const InvalidBrowserEventBatch_base: Schema.Class<InvalidBrowserEventBatch, Schema.TaggedStruct<"InvalidBrowserEventBatch", {
|
|
4
5
|
readonly code: Schema.Literal<"OBS_BROWSER_EVENTS_INVALID_BATCH">;
|
|
5
6
|
readonly message: Schema.String;
|
|
@@ -10,7 +11,11 @@ export declare class InvalidBrowserEventBatch extends InvalidBrowserEventBatch_b
|
|
|
10
11
|
export declare const parseBrowserEventBatch: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) => Effect.Effect<BrowserEventBatch, InvalidBrowserEventBatch, never>;
|
|
11
12
|
export type BrowserEventIngestReceipt = {
|
|
12
13
|
readonly accepted: number;
|
|
14
|
+
readonly redacted: number;
|
|
15
|
+
readonly dropped: number;
|
|
16
|
+
readonly spans?: number;
|
|
17
|
+
readonly metrics?: number;
|
|
13
18
|
};
|
|
14
|
-
export declare const ingestBrowserEventBatch: (batch: BrowserEventBatch) => Effect.Effect<BrowserEventIngestReceipt,
|
|
15
|
-
export declare const ingestBrowserEvents: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) => Effect.Effect<BrowserEventIngestReceipt, InvalidBrowserEventBatch,
|
|
19
|
+
export declare const ingestBrowserEventBatch: (batch: BrowserEventBatch) => Effect.Effect<BrowserEventIngestReceipt, InvalidBrowserEventBatch | import("../contract/TelemetryContractError.js").InvalidTelemetryEvent, TelemetryEventSink>;
|
|
20
|
+
export declare const ingestBrowserEvents: (input: unknown, options?: import("effect/SchemaAST").ParseOptions | undefined) => Effect.Effect<BrowserEventIngestReceipt, InvalidBrowserEventBatch | import("../contract/TelemetryContractError.js").InvalidTelemetryEvent, TelemetryEventSink>;
|
|
16
21
|
export {};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { Effect, flow, Schema } from "effect";
|
|
1
|
+
import { Effect, flow, Option, Schema } from "effect";
|
|
2
2
|
import { BrowserEventBatch } from "../BrowserEvents.js";
|
|
3
|
-
import
|
|
3
|
+
import { parseResourceIdentity } from "../ResourceIdentity.js";
|
|
4
|
+
import { BrowserSignalExporter } from "../trace/HttpServerOtlpTracer.js";
|
|
5
|
+
import { TelemetryEventSink } from "../contract/EventProducer.js";
|
|
6
|
+
import { CurrentDataPolicy } from "../policy/DataPolicy.js";
|
|
7
|
+
import { transformSignalFields } from "../policy/PolicyTransform.js";
|
|
8
|
+
import { BrowserMetricRecorder, unavailableBrowserMetricRecorder, } from "../browser/BrowserMetricRecorder.js";
|
|
4
9
|
export class InvalidBrowserEventBatch extends Schema.TaggedError()("InvalidBrowserEventBatch", {
|
|
5
10
|
code: Schema.Literal("OBS_BROWSER_EVENTS_INVALID_BATCH"),
|
|
6
11
|
message: Schema.String,
|
|
@@ -10,29 +15,116 @@ export class InvalidBrowserEventBatch extends Schema.TaggedError()("InvalidBrows
|
|
|
10
15
|
const decodeBrowserEventBatch = Schema.decodeUnknownEffect(BrowserEventBatch);
|
|
11
16
|
export const parseBrowserEventBatch = flow(decodeBrowserEventBatch, Effect.mapError((cause) => new InvalidBrowserEventBatch({
|
|
12
17
|
code: "OBS_BROWSER_EVENTS_INVALID_BATCH",
|
|
13
|
-
message: "The browser event batch is invalid. Send a version 1
|
|
18
|
+
message: "The browser event batch is invalid. Send a batch with a positive safe integer version (version 1 or newer), bounded events, and scalar fields.",
|
|
14
19
|
cause,
|
|
15
20
|
})));
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const invalidSignalBatch = (cause) => new InvalidBrowserEventBatch({
|
|
22
|
+
code: "OBS_BROWSER_EVENTS_INVALID_BATCH",
|
|
23
|
+
message: "The browser event batch has invalid signal correlation. Send completed spans with valid parent and event references in the same trace.",
|
|
24
|
+
cause,
|
|
25
|
+
});
|
|
26
|
+
export const ingestBrowserEventBatch = Effect.fn("ingestBrowserEventBatch")(function* (batch) {
|
|
27
|
+
const policy = yield* CurrentDataPolicy;
|
|
28
|
+
const sink = yield* TelemetryEventSink;
|
|
29
|
+
const signalExporter = yield* BrowserSignalExporter;
|
|
30
|
+
const metricRecorder = yield* Effect.serviceOption(BrowserMetricRecorder).pipe(Effect.map(Option.getOrElse(() => unavailableBrowserMetricRecorder)));
|
|
31
|
+
const spans = batch.spans ?? [];
|
|
32
|
+
const metrics = batch.metrics ?? [];
|
|
33
|
+
const resource = batch.resource === undefined
|
|
34
|
+
? undefined
|
|
35
|
+
: yield* parseResourceIdentity(batch.resource).pipe(Effect.mapError(() => invalidSignalBatch("invalid browser resource identity")));
|
|
36
|
+
const spanById = new Map(spans.map((span) => [span.spanId, span]));
|
|
37
|
+
if (spanById.size !== spans.length)
|
|
38
|
+
return yield* invalidSignalBatch("duplicate span id");
|
|
39
|
+
for (const span of spans) {
|
|
40
|
+
if (span.endedAt < span.startedAt)
|
|
41
|
+
return yield* invalidSignalBatch("span ended before start");
|
|
42
|
+
if (span.parentSpanId !== undefined) {
|
|
43
|
+
const parent = spanById.get(span.parentSpanId);
|
|
44
|
+
if (parent === undefined ||
|
|
45
|
+
parent.traceId !== span.traceId ||
|
|
46
|
+
parent.spanId === span.spanId) {
|
|
47
|
+
return yield* invalidSignalBatch("invalid span parent");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const span of spans) {
|
|
52
|
+
const ancestors = new Set([span.spanId]);
|
|
53
|
+
let parentId = span.parentSpanId;
|
|
54
|
+
while (parentId !== undefined) {
|
|
55
|
+
if (ancestors.has(parentId))
|
|
56
|
+
return yield* invalidSignalBatch("cyclic span parent");
|
|
57
|
+
ancestors.add(parentId);
|
|
58
|
+
parentId = spanById.get(parentId)?.parentSpanId;
|
|
22
59
|
}
|
|
23
|
-
sanitized[key] = value;
|
|
24
60
|
}
|
|
25
|
-
return sanitized;
|
|
26
|
-
};
|
|
27
|
-
export const ingestBrowserEventBatch = Effect.fn("ingestBrowserEventBatch")(function* (batch) {
|
|
28
61
|
for (const event of batch.events) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
62
|
+
if (event.trace !== undefined) {
|
|
63
|
+
const span = spanById.get(event.trace.spanId);
|
|
64
|
+
if (span === undefined || span.traceId !== event.trace.traceId) {
|
|
65
|
+
return yield* invalidSignalBatch("invalid event trace reference");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
let redacted = 0;
|
|
70
|
+
let dropped = 0;
|
|
71
|
+
const events = batch.events.map((event) => {
|
|
72
|
+
const decision = transformSignalFields(policy, "browser-ingest", event.fields);
|
|
73
|
+
const errorDecision = event.error === undefined
|
|
74
|
+
? undefined
|
|
75
|
+
: transformSignalFields(policy, "browser-ingest", {
|
|
76
|
+
"error.type": event.error.type,
|
|
77
|
+
"error.message": event.error.message,
|
|
78
|
+
});
|
|
79
|
+
const eventDropped = decision.dropped + (errorDecision?.dropped ?? 0);
|
|
80
|
+
dropped += eventDropped;
|
|
81
|
+
redacted += [...decision.redactions, ...(errorDecision?.redactions ?? [])].filter((redaction) => redaction.action !== "dropped").length;
|
|
82
|
+
const ingested = {
|
|
83
|
+
id: event.id,
|
|
84
|
+
name: event.name,
|
|
85
|
+
occurredAt: event.occurredAt,
|
|
86
|
+
attributes: decision.value,
|
|
87
|
+
admission: { policyDroppedAttributes: eventDropped },
|
|
88
|
+
};
|
|
89
|
+
if (event.trace !== undefined)
|
|
90
|
+
Object.assign(ingested, { trace: event.trace });
|
|
91
|
+
if (event.error === undefined || errorDecision === undefined)
|
|
92
|
+
return ingested;
|
|
93
|
+
return {
|
|
94
|
+
...ingested,
|
|
95
|
+
error: {
|
|
96
|
+
type: String(errorDecision.value["error.type"] ?? "Error"),
|
|
97
|
+
message: String(errorDecision.value["error.message"] ?? ""),
|
|
98
|
+
retryable: event.error.retryable,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
const eventAdmission = yield* sink.admitBrowserBatch(events);
|
|
103
|
+
let commitMetrics = () => undefined;
|
|
104
|
+
if (metrics.length > 0) {
|
|
105
|
+
commitMetrics = yield* Effect.try({
|
|
106
|
+
try: () => metricRecorder.admit(metrics).commit,
|
|
107
|
+
catch: (cause) => invalidSignalBatch(cause),
|
|
34
108
|
});
|
|
35
109
|
}
|
|
36
|
-
|
|
110
|
+
yield* eventAdmission.commit;
|
|
111
|
+
commitMetrics();
|
|
112
|
+
const signals = { spans };
|
|
113
|
+
yield* signalExporter.export(resource === undefined
|
|
114
|
+
? signals
|
|
115
|
+
: {
|
|
116
|
+
...signals,
|
|
117
|
+
resource: {
|
|
118
|
+
serviceName: resource.serviceName,
|
|
119
|
+
serviceVersion: resource.serviceVersion,
|
|
120
|
+
environment: resource.environment,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
const receipt = { accepted: events.length, redacted, dropped };
|
|
124
|
+
if (batch.spans !== undefined)
|
|
125
|
+
Object.assign(receipt, { spans: spans.length });
|
|
126
|
+
if (batch.metrics !== undefined)
|
|
127
|
+
Object.assign(receipt, { metrics: metrics.length });
|
|
128
|
+
return receipt;
|
|
37
129
|
});
|
|
38
130
|
export const ingestBrowserEvents = flow(parseBrowserEventBatch, Effect.flatMap(ingestBrowserEventBatch));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import type { ConformanceTargetBinding } from "../testing/conformance/ConformanceModel.js";
|
|
3
|
+
import { type TelemetryDestinationReceipt } from "../testing/conformance/TelemetryEvidence.js";
|
|
4
|
+
import { type CapturedTelemetry, type OtlpCaptureServer } from "../testing/index.js";
|
|
5
|
+
declare const LocalCollectorError_base: Schema.Class<LocalCollectorError, Schema.TaggedStruct<"LocalCollectorError", {
|
|
6
|
+
readonly code: Schema.Literal<"OBS_CONFORMANCE_LOCAL_COLLECTOR_FAILED">;
|
|
7
|
+
readonly message: Schema.String;
|
|
8
|
+
readonly cause: Schema.Defect;
|
|
9
|
+
}>, import("effect/Cause").YieldableError>;
|
|
10
|
+
export declare class LocalCollectorError extends LocalCollectorError_base {
|
|
11
|
+
}
|
|
12
|
+
export type LocalCollectorDestination = OtlpCaptureServer & {
|
|
13
|
+
readonly destinationTelemetry: () => CapturedTelemetry;
|
|
14
|
+
readonly destinationEndpoint: URL;
|
|
15
|
+
readonly collectorInstance: string;
|
|
16
|
+
readonly awaitDestination: (runId: string, eventRunIdAttribute?: string) => Promise<void>;
|
|
17
|
+
readonly destinationReceipt: (runId: string, binding: ConformanceTargetBinding) => TelemetryDestinationReceipt;
|
|
18
|
+
};
|
|
19
|
+
export declare const startLocalCollectorDestination: () => Promise<LocalCollectorDestination>;
|
|
20
|
+
export {};
|