| /** | |
| * Exports Effect log entries over OTLP/HTTP. | |
| * | |
| * The logger turns Effect log entries into OTLP log records and sends them to a | |
| * logs endpoint, such as an OpenTelemetry Collector or vendor OTLP endpoint. It | |
| * includes log levels, messages, annotations, causes, fiber ids, optional log | |
| * spans, and current trace/span ids when they are present. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| import * as Arr from "../../Array.ts" | |
| import * as Cause from "../../Cause.ts" | |
| import { Clock } from "../../Clock.ts" | |
| import * as Config from "../../Config.ts" | |
| import * as Duration from "../../Duration.ts" | |
| import * as Effect from "../../Effect.ts" | |
| import * as Layer from "../../Layer.ts" | |
| import * as Logger from "../../Logger.ts" | |
| import type * as LogLevel from "../../LogLevel.ts" | |
| import * as Option from "../../Option.ts" | |
| import { CurrentLogAnnotations, CurrentLogSpans } from "../../References.ts" | |
| import type * as Scope from "../../Scope.ts" | |
| import type * as Headers from "../http/Headers.ts" | |
| import type * as HttpClient from "../http/HttpClient.ts" | |
| import * as OtlpEnv from "./internal/otlpEnv.ts" | |
| import * as Exporter from "./OtlpExporter.ts" | |
| import type { AnyValue, Fixed64, KeyValue, Resource } from "./OtlpResource.ts" | |
| import * as OtlpResource from "./OtlpResource.ts" | |
| import { OtlpSerialization } from "./OtlpSerialization.ts" | |
| /** | |
| * Creates an Effect `Logger` that exports log records through OTLP. | |
| * | |
| * **Details** | |
| * | |
| * The logger serializes records with the configured resource, sends them | |
| * through the OTLP exporter, and requires `Scope` so pending records can be | |
| * flushed on shutdown. | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const make: ( | |
| options: { | |
| readonly url: string | |
| readonly resource?: { | |
| readonly serviceName?: string | undefined | |
| readonly serviceVersion?: string | undefined | |
| readonly attributes?: Record<string, unknown> | |
| } | undefined | |
| readonly headers?: Headers.Input | undefined | |
| readonly exportInterval?: Duration.Input | undefined | |
| readonly maxBatchSize?: number | undefined | |
| readonly shutdownTimeout?: Duration.Input | undefined | |
| readonly excludeLogSpans?: boolean | undefined | |
| } | |
| ) => Effect.Effect< | |
| Logger.Logger<unknown, void>, | |
| never, | |
| OtlpSerialization | HttpClient.HttpClient | Scope.Scope | |
| > = Effect.fnUntraced(function*(options) { | |
| const serialization = yield* OtlpSerialization | |
| const otelResource = yield* OtlpResource.fromConfig(options.resource) | |
| const scope: IInstrumentationScope = { | |
| name: OtlpResource.serviceNameUnsafe(otelResource) | |
| } | |
| const exporter = yield* Exporter.make({ | |
| label: "OtlpLogger", | |
| url: options.url, | |
| headers: options.headers, | |
| maxBatchSize: options.maxBatchSize ?? 1000, | |
| exportInterval: options.exportInterval ?? Duration.seconds(1), | |
| body: (data) => | |
| serialization.logs({ | |
| resourceLogs: [{ | |
| resource: otelResource, | |
| scopeLogs: [{ | |
| scope, | |
| logRecords: data | |
| }] | |
| }] | |
| }), | |
| shutdownTimeout: options.shutdownTimeout ?? Duration.seconds(3) | |
| }) | |
| const opts = { | |
| excludeLogSpans: options.excludeLogSpans ?? false, | |
| clock: yield* Clock | |
| } | |
| return Logger.make((options) => { | |
| exporter.push(makeLogRecord(options, opts)) | |
| }) | |
| }) | |
| /** | |
| * Layer that installs the OTLP logger created by `make`. | |
| * | |
| * **Details** | |
| * | |
| * By default the OTLP logger is merged with any existing loggers. | |
| * | |
| * @category layers | |
| * @since 4.0.0 | |
| */ | |
| export const layer = (options: { | |
| readonly url: string | |
| readonly resource?: { | |
| readonly serviceName?: string | undefined | |
| readonly serviceVersion?: string | undefined | |
| readonly attributes?: Record<string, unknown> | |
| } | undefined | |
| readonly headers?: Headers.Input | undefined | |
| readonly exportInterval?: Duration.Input | undefined | |
| readonly maxBatchSize?: number | undefined | |
| readonly shutdownTimeout?: Duration.Input | undefined | |
| readonly excludeLogSpans?: boolean | undefined | |
| readonly mergeWithExisting?: boolean | undefined | |
| }): Layer.Layer<never, never, HttpClient.HttpClient | OtlpSerialization> => | |
| Logger.layer([make(options)], { | |
| mergeWithExisting: options.mergeWithExisting ?? true | |
| }) | |
| /** | |
| * Creates an OTLP logs layer from OpenTelemetry configuration. | |
| * | |
| * @category layers | |
| * @since 4.0.0 | |
| */ | |
| export const layerFromConfig = (options?: { | |
| readonly resource?: { | |
| readonly serviceName?: string | undefined | |
| readonly serviceVersion?: string | undefined | |
| readonly attributes?: Record<string, unknown> | |
| } | undefined | |
| readonly headers?: Headers.Input | undefined | |
| readonly excludeLogSpans?: boolean | undefined | |
| readonly mergeWithExisting?: boolean | undefined | |
| }): Layer.Layer<never, never, HttpClient.HttpClient | OtlpSerialization> => | |
| Effect.gen(function*() { | |
| const { disabled, endpoint, exporters } = yield* Config.all({ | |
| disabled: Config.boolean("OTEL_SDK_DISABLED").pipe(Config.withDefault(false)), | |
| endpoint: OtlpEnv.endpoint("LOGS"), | |
| exporters: OtlpEnv.exporters("LOGS") | |
| }) | |
| if (disabled || !endpoint || !exporters.includes("otlp")) { | |
| return Layer.empty | |
| } | |
| const { baseTimeout, logsTimeout, exportTimeout, scheduleDelay, maxBatchSize } = yield* Config.all({ | |
| baseTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_TIMEOUT")), | |
| logsTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_LOGS_TIMEOUT")), | |
| exportTimeout: Config.option(Config.int("OTEL_BLRP_EXPORT_TIMEOUT")), | |
| scheduleDelay: Config.option(Config.int("OTEL_BLRP_SCHEDULE_DELAY")), | |
| maxBatchSize: Config.option(Config.int("OTEL_BLRP_MAX_EXPORT_BATCH_SIZE")) | |
| }) | |
| const shutdownTimeout = Option.firstSomeOf([logsTimeout, baseTimeout, exportTimeout]).pipe( | |
| Option.map((_) => Duration.millis(_)) | |
| ) | |
| const exportInterval = Option.map(scheduleDelay, (_) => Duration.millis(_)) | |
| return layer({ | |
| url: endpoint.toString(), | |
| resource: options?.resource, | |
| headers: options?.headers ?? (yield* OtlpEnv.headers("LOGS")), | |
| exportInterval: Option.getOrUndefined(exportInterval), | |
| maxBatchSize: Option.getOrUndefined(maxBatchSize), | |
| shutdownTimeout: Option.getOrUndefined(shutdownTimeout), | |
| excludeLogSpans: options?.excludeLogSpans, | |
| mergeWithExisting: options?.mergeWithExisting | |
| }) | |
| }).pipe(Effect.orDie, Layer.unwrap) | |
| /** | |
| * OTLP logs payload serialized by `OtlpLogger`. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface LogsData { | |
| resourceLogs: ReadonlyArray<IResourceLogs> | |
| } | |
| // internal | |
| const makeLogRecord = (options: Logger.Options<unknown>, opts: { | |
| readonly excludeLogSpans: boolean | |
| readonly clock: Clock | |
| }): ILogRecord => { | |
| const now = opts.clock.currentTimeNanosUnsafe() | |
| const nanosString = now.toString() | |
| const nowMillis = options.date.getTime() | |
| const attributes = OtlpResource.entriesToAttributes(Object.entries(options.fiber.getRef(CurrentLogAnnotations))) | |
| attributes.push({ | |
| key: "fiberId", | |
| value: { intValue: options.fiber.id } | |
| }) | |
| if (!opts.excludeLogSpans) { | |
| for (const [label, startTime] of options.fiber.getRef(CurrentLogSpans)) { | |
| attributes.push({ | |
| key: `logSpan.${label}`, | |
| value: { stringValue: `${nowMillis - startTime}ms` } | |
| }) | |
| } | |
| } | |
| if (options.cause.reasons.length > 0) { | |
| attributes.push({ | |
| key: "log.error", | |
| value: { stringValue: Cause.pretty(options.cause) } | |
| }) | |
| } | |
| const message = Arr.ensure(options.message) | |
| const logRecord: ILogRecord = { | |
| severityNumber: logLevelToSeverityNumber(options.logLevel), | |
| severityText: options.logLevel, | |
| timeUnixNano: nanosString, | |
| observedTimeUnixNano: nanosString, | |
| attributes, | |
| body: OtlpResource.unknownToAttributeValue(message.length === 1 ? message[0] : message), | |
| droppedAttributesCount: 0 | |
| } | |
| if (options.fiber.currentSpan) { | |
| logRecord.traceId = options.fiber.currentSpan.traceId | |
| logRecord.spanId = options.fiber.currentSpan.spanId | |
| } | |
| return logRecord | |
| } | |
| /** Properties of an InstrumentationScope. */ | |
| interface IInstrumentationScope { | |
| /** InstrumentationScope name */ | |
| name: string | |
| /** InstrumentationScope version */ | |
| version?: string | |
| /** InstrumentationScope attributes */ | |
| attributes?: Array<KeyValue> | |
| /** InstrumentationScope droppedAttributesCount */ | |
| droppedAttributesCount?: number | |
| } | |
| /** Properties of a ResourceLogs. */ | |
| interface IResourceLogs { | |
| /** ResourceLogs resource */ | |
| resource?: Resource | |
| /** ResourceLogs scopeLogs */ | |
| scopeLogs: Array<IScopeLogs> | |
| /** ResourceLogs schemaUrl */ | |
| schemaUrl?: string | |
| } | |
| /** Properties of an ScopeLogs. */ | |
| interface IScopeLogs { | |
| /** IScopeLogs scope */ | |
| scope?: IInstrumentationScope | |
| /** IScopeLogs logRecords */ | |
| logRecords?: Array<ILogRecord> | |
| /** IScopeLogs schemaUrl */ | |
| schemaUrl?: string | null | |
| } | |
| /** Properties of a LogRecord. */ | |
| interface ILogRecord { | |
| /** LogRecord timeUnixNano */ | |
| timeUnixNano: Fixed64 | |
| /** LogRecord observedTimeUnixNano */ | |
| observedTimeUnixNano: Fixed64 | |
| /** LogRecord severityNumber */ | |
| severityNumber?: ESeverityNumber | |
| /** LogRecord severityText */ | |
| severityText?: string | |
| /** LogRecord body */ | |
| body?: AnyValue | |
| /** LogRecord attributes */ | |
| attributes: Array<KeyValue> | |
| /** LogRecord droppedAttributesCount */ | |
| droppedAttributesCount: number | |
| /** LogRecord flags */ | |
| flags?: number | |
| /** LogRecord traceId */ | |
| traceId?: string | Uint8Array | |
| /** LogRecord spanId */ | |
| spanId?: string | Uint8Array | |
| } | |
| const logLevelToSeverityNumber = (logLevel: LogLevel.LogLevel): ESeverityNumber => { | |
| switch (logLevel) { | |
| case "Trace": | |
| return ESeverityNumber.SEVERITY_NUMBER_TRACE | |
| case "Debug": | |
| return ESeverityNumber.SEVERITY_NUMBER_DEBUG | |
| case "Info": | |
| return ESeverityNumber.SEVERITY_NUMBER_INFO | |
| case "Warn": | |
| return ESeverityNumber.SEVERITY_NUMBER_WARN | |
| case "Error": | |
| return ESeverityNumber.SEVERITY_NUMBER_ERROR | |
| case "Fatal": | |
| return ESeverityNumber.SEVERITY_NUMBER_FATAL | |
| default: | |
| return ESeverityNumber.SEVERITY_NUMBER_UNSPECIFIED | |
| } | |
| } | |
| /** | |
| * Numerical value of the severity, normalized to values described in Log Data Model. | |
| */ | |
| const ESeverityNumber = { | |
| /** Unspecified. Do NOT use as default */ | |
| SEVERITY_NUMBER_UNSPECIFIED: 0, | |
| SEVERITY_NUMBER_TRACE: 1, | |
| SEVERITY_NUMBER_TRACE2: 2, | |
| SEVERITY_NUMBER_TRACE3: 3, | |
| SEVERITY_NUMBER_TRACE4: 4, | |
| SEVERITY_NUMBER_DEBUG: 5, | |
| SEVERITY_NUMBER_DEBUG2: 6, | |
| SEVERITY_NUMBER_DEBUG3: 7, | |
| SEVERITY_NUMBER_DEBUG4: 8, | |
| SEVERITY_NUMBER_INFO: 9, | |
| SEVERITY_NUMBER_INFO2: 10, | |
| SEVERITY_NUMBER_INFO3: 11, | |
| SEVERITY_NUMBER_INFO4: 12, | |
| SEVERITY_NUMBER_WARN: 13, | |
| SEVERITY_NUMBER_WARN2: 14, | |
| SEVERITY_NUMBER_WARN3: 15, | |
| SEVERITY_NUMBER_WARN4: 16, | |
| SEVERITY_NUMBER_ERROR: 17, | |
| SEVERITY_NUMBER_ERROR2: 18, | |
| SEVERITY_NUMBER_ERROR3: 19, | |
| SEVERITY_NUMBER_ERROR4: 20, | |
| SEVERITY_NUMBER_FATAL: 21, | |
| SEVERITY_NUMBER_FATAL2: 22, | |
| SEVERITY_NUMBER_FATAL3: 23, | |
| SEVERITY_NUMBER_FATAL4: 24 | |
| } as const | |
| type ESeverityNumber = typeof ESeverityNumber[keyof typeof ESeverityNumber] | |
Xet Storage Details
- Size:
- 11 kB
- Xet hash:
- 9a20145e4947a6f38777818d3c2d4859f7adc2fa07b0dafe0ecfe5c79a50724c
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.