| /** | |
| * Exports Effect tracing spans over OTLP/HTTP. | |
| * | |
| * This module creates a `Tracer.Tracer` backed by the shared OTLP batch | |
| * exporter, so spans created by Effect tracing APIs can be sent to an | |
| * OpenTelemetry Collector, vendor endpoint, or local collector. Exported spans | |
| * include identifiers, parent links, attributes, events, timing, kind, and | |
| * status information. Use the constructor directly or install it through the | |
| * provided layer. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| import * as Cause from "../../Cause.js"; | |
| import * as Config from "../../Config.js"; | |
| import * as Duration from "../../Duration.js"; | |
| import * as Effect from "../../Effect.js"; | |
| import { flow } from "../../Function.js"; | |
| import * as Layer from "../../Layer.js"; | |
| import * as Option from "../../Option.js"; | |
| import * as Tracer from "../../Tracer.js"; | |
| import * as OtlpEnv from "./internal/otlpEnv.js"; | |
| import * as Exporter from "./OtlpExporter.js"; | |
| import { entriesToAttributes } from "./OtlpResource.js"; | |
| import * as OtlpResource from "./OtlpResource.js"; | |
| import { OtlpSerialization } from "./OtlpSerialization.js"; | |
| /** | |
| * Creates a `Tracer` that exports ended sampled spans to an OTLP traces endpoint. | |
| * | |
| * **Details** | |
| * | |
| * Spans are batched using the configured interval and batch size, serialized | |
| * with `OtlpSerialization`, and flushed when the surrounding `Scope` closes. | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const make = /*#__PURE__*/Effect.fnUntraced(function* (options) { | |
| const otelResource = yield* OtlpResource.fromConfig(options.resource); | |
| const serialization = yield* OtlpSerialization; | |
| const scope = { | |
| name: OtlpResource.serviceNameUnsafe(otelResource) | |
| }; | |
| const exporter = yield* Exporter.make({ | |
| label: "OtlpTracer", | |
| url: options.url, | |
| headers: options.headers, | |
| exportInterval: options.exportInterval ?? Duration.seconds(5), | |
| maxBatchSize: options.maxBatchSize ?? 1000, | |
| body(spans) { | |
| const data = { | |
| resourceSpans: [{ | |
| resource: otelResource, | |
| scopeSpans: [{ | |
| scope, | |
| spans | |
| }] | |
| }] | |
| }; | |
| return serialization.traces(data); | |
| }, | |
| shutdownTimeout: options.shutdownTimeout ?? Duration.seconds(3) | |
| }); | |
| function exportFn(span) { | |
| if (!span.sampled) return; | |
| exporter.push(makeOtlpSpan(span)); | |
| } | |
| return Tracer.make({ | |
| span(options) { | |
| return makeSpan({ | |
| ...options, | |
| status: { | |
| _tag: "Started", | |
| startTime: options.startTime | |
| }, | |
| attributes: new Map(), | |
| export: exportFn | |
| }); | |
| }, | |
| context: options.context ? function (primitive, fiber) { | |
| if (fiber.currentSpan === undefined) { | |
| return primitive["~effect/Effect/evaluate"](fiber); | |
| } | |
| return options.context(primitive, fiber.currentSpan); | |
| } : undefined | |
| }); | |
| }); | |
| /** | |
| * Provides `Tracer.Tracer` using the OTLP tracer created by `make`. | |
| * | |
| * @category layers | |
| * @since 4.0.0 | |
| */ | |
| export const layer = /*#__PURE__*/flow(make, /*#__PURE__*/Layer.effect(Tracer.Tracer)); | |
| /** | |
| * Creates an OTLP traces layer from OpenTelemetry configuration. | |
| * | |
| * @category layers | |
| * @since 4.0.0 | |
| */ | |
| export const layerFromConfig = options => Effect.gen(function* () { | |
| const { | |
| disabled, | |
| endpoint, | |
| exporters | |
| } = yield* Config.all({ | |
| disabled: Config.boolean("OTEL_SDK_DISABLED").pipe(Config.withDefault(false)), | |
| endpoint: OtlpEnv.endpoint("TRACES"), | |
| exporters: OtlpEnv.exporters("TRACES") | |
| }); | |
| if (disabled || !endpoint || !exporters.includes("otlp")) { | |
| return Layer.empty; | |
| } | |
| const { | |
| baseTimeout, | |
| tracesTimeout, | |
| exportTimeout, | |
| scheduleDelay, | |
| maxBatchSize | |
| } = yield* Config.all({ | |
| baseTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_TIMEOUT")), | |
| tracesTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_TRACES_TIMEOUT")), | |
| exportTimeout: Config.option(Config.int("OTEL_BSP_EXPORT_TIMEOUT")), | |
| scheduleDelay: Config.option(Config.int("OTEL_BSP_SCHEDULE_DELAY").pipe(Config.map(Duration.millis))), | |
| maxBatchSize: Config.option(Config.int("OTEL_BSP_MAX_EXPORT_BATCH_SIZE")) | |
| }); | |
| const shutdownTimeout = Option.firstSomeOf([tracesTimeout, baseTimeout, exportTimeout]).pipe(Option.map(_ => Duration.millis(_))); | |
| return layer({ | |
| url: endpoint.toString(), | |
| resource: options?.resource, | |
| headers: options?.headers ?? (yield* OtlpEnv.headers("TRACES")), | |
| exportInterval: Option.getOrUndefined(scheduleDelay), | |
| maxBatchSize: Option.getOrUndefined(maxBatchSize), | |
| context: options?.context, | |
| shutdownTimeout: Option.getOrUndefined(shutdownTimeout) | |
| }); | |
| }).pipe(Effect.orDie, Layer.unwrap); | |
| const SpanProto = { | |
| _tag: "Span", | |
| end(endTime, exit) { | |
| this.status = { | |
| _tag: "Ended", | |
| startTime: this.status.startTime, | |
| endTime, | |
| exit | |
| }; | |
| this.export(this); | |
| }, | |
| attribute(key, value) { | |
| this.attributes.set(key, value); | |
| }, | |
| event(name, startTime, attributes) { | |
| this.events.push([name, startTime, attributes]); | |
| }, | |
| addLinks(links) { | |
| this.links.push(...links); | |
| } | |
| }; | |
| const makeSpan = options => { | |
| const self = Object.assign(Object.create(SpanProto), options); | |
| if (Option.isSome(self.parent)) { | |
| self.traceId = self.parent.value.traceId; | |
| } else { | |
| self.traceId = generateId(32); | |
| } | |
| self.spanId = generateId(16); | |
| self.events = []; | |
| return self; | |
| }; | |
| const generateId = len => { | |
| const chars = "0123456789abcdef"; | |
| let result = ""; | |
| for (let i = 0; i < len; i++) { | |
| result += chars[Math.floor(Math.random() * chars.length)]; | |
| } | |
| return result; | |
| }; | |
| const makeOtlpSpan = self => { | |
| const status = self.status; | |
| const attributes = entriesToAttributes(self.attributes.entries()); | |
| const events = self.events.map(([name, startTime, attributes]) => ({ | |
| name, | |
| timeUnixNano: String(startTime), | |
| attributes: attributes ? entriesToAttributes(Object.entries(attributes)) : [], | |
| droppedAttributesCount: 0 | |
| })); | |
| let otelStatus; | |
| if (status.exit._tag === "Success") { | |
| otelStatus = constOtelStatusSuccess; | |
| } else if (Cause.hasInterruptsOnly(status.exit.cause)) { | |
| otelStatus = { | |
| code: StatusCode.Ok, | |
| message: "Interrupted" | |
| }; | |
| attributes.push({ | |
| key: "span.label", | |
| value: { | |
| stringValue: "⚠︎ Interrupted" | |
| } | |
| }, { | |
| key: "status.interrupted", | |
| value: { | |
| boolValue: true | |
| } | |
| }); | |
| } else { | |
| const errors = Cause.prettyErrors(status.exit.cause); | |
| otelStatus = { | |
| code: StatusCode.Error | |
| }; | |
| if (errors.length > 0) { | |
| otelStatus.message = errors[0].message; | |
| for (const error of errors) { | |
| events.push({ | |
| name: "exception", | |
| timeUnixNano: String(status.endTime), | |
| droppedAttributesCount: 0, | |
| attributes: [{ | |
| "key": "exception.type", | |
| "value": { | |
| "stringValue": error.name | |
| } | |
| }, { | |
| "key": "exception.message", | |
| "value": { | |
| "stringValue": error.message | |
| } | |
| }, { | |
| "key": "exception.stacktrace", | |
| "value": { | |
| "stringValue": error.stack ?? "No stack trace available" | |
| } | |
| }] | |
| }); | |
| } | |
| } | |
| } | |
| return { | |
| traceId: self.traceId, | |
| spanId: self.spanId, | |
| parentSpanId: Option.match(self.parent, { | |
| onNone: () => undefined, | |
| onSome: parent => parent.spanId | |
| }), | |
| name: self.name, | |
| kind: SpanKind[self.kind], | |
| startTimeUnixNano: String(status.startTime), | |
| endTimeUnixNano: String(status.endTime), | |
| attributes, | |
| droppedAttributesCount: 0, | |
| events, | |
| droppedEventsCount: 0, | |
| status: otelStatus, | |
| links: self.links.map(link => ({ | |
| traceId: link.span.traceId, | |
| spanId: link.span.spanId, | |
| attributes: entriesToAttributes(Object.entries(link.attributes)), | |
| droppedAttributesCount: 0 | |
| })), | |
| droppedLinksCount: 0 | |
| }; | |
| }; | |
| const StatusCode = { | |
| Unset: 0, | |
| Ok: 1, | |
| Error: 2 | |
| }; | |
| const SpanKind = { | |
| unspecified: 0, | |
| internal: 1, | |
| server: 2, | |
| client: 3, | |
| producer: 4, | |
| consumer: 5 | |
| }; | |
| const constOtelStatusSuccess = { | |
| code: StatusCode.Ok | |
| }; | |
| //# sourceMappingURL=OtlpTracer.js.map |
Xet Storage Details
- Size:
- 8.19 kB
- Xet hash:
- 4f48bc356c17912abb11551f3a1c5a55ad83fbd6ca8d7016db884713fe869c1e
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.