141 lines
5.2 KiB
TypeScript
141 lines
5.2 KiB
TypeScript
/**
|
|
* Browser OpenTelemetry setup for Effect applications.
|
|
*
|
|
* This module exports a `Configuration` type and layers for installing
|
|
* tracing, metrics, and logging in browser runtimes. The main `layer` builds
|
|
* the shared OpenTelemetry resource from explicit service metadata, then
|
|
* enables only the signal types that have processors or readers configured.
|
|
* `layerTracerProvider` creates a scoped `WebTracerProvider`.
|
|
*
|
|
* @since 4.0.0
|
|
*/
|
|
import type * as Otel from "@opentelemetry/api"
|
|
import type { LoggerProviderConfig, LogRecordProcessor } from "@opentelemetry/sdk-logs"
|
|
import type { MetricReader } from "@opentelemetry/sdk-metrics"
|
|
import type { SpanProcessor, TracerConfig } from "@opentelemetry/sdk-trace-base"
|
|
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web"
|
|
import type { NonEmptyReadonlyArray } from "effect/Array"
|
|
import * as Effect from "effect/Effect"
|
|
import { constant, type LazyArg } from "effect/Function"
|
|
import * as Layer from "effect/Layer"
|
|
import { isNonEmpty } from "./internal/utilities.ts"
|
|
import * as Logger from "./OtelLogger.ts"
|
|
import * as Metrics from "./OtelMetrics.ts"
|
|
import * as Tracer from "./OtelTracer.ts"
|
|
import * as Resource from "./Resource.ts"
|
|
|
|
/**
|
|
* Configuration for the Web OpenTelemetry layer, including resource metadata and optional tracing, metrics, and logging settings.
|
|
*
|
|
* @category models
|
|
* @since 4.0.0
|
|
*/
|
|
export interface Configuration {
|
|
readonly spanProcessor?: SpanProcessor | ReadonlyArray<SpanProcessor> | undefined
|
|
readonly tracerConfig?: Omit<TracerConfig, "resource">
|
|
readonly metricReader?: MetricReader | ReadonlyArray<MetricReader> | undefined
|
|
readonly metricTemporality?: Metrics.TemporalityPreference | undefined
|
|
readonly logRecordProcessor?: LogRecordProcessor | ReadonlyArray<LogRecordProcessor> | undefined
|
|
readonly loggerProviderConfig?: Omit<LoggerProviderConfig, "resource"> | undefined
|
|
readonly loggerMergeWithExisting?: boolean | undefined
|
|
readonly resource: {
|
|
readonly serviceName: string
|
|
readonly serviceVersion?: string
|
|
readonly attributes?: Otel.Attributes
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a scoped Web OpenTelemetry tracer provider from one or more span processors and shuts it down when the layer is released.
|
|
*
|
|
* @category layers
|
|
* @since 4.0.0
|
|
*/
|
|
export const layerTracerProvider = (
|
|
processor: SpanProcessor | NonEmptyReadonlyArray<SpanProcessor>,
|
|
config?: Omit<TracerConfig, "resource">
|
|
): Layer.Layer<Tracer.OtelTracerProvider, never, Resource.Resource> =>
|
|
Layer.effect(
|
|
Tracer.OtelTracerProvider,
|
|
Effect.gen(function*() {
|
|
const resource = yield* Resource.Resource
|
|
return yield* Effect.acquireRelease(
|
|
Effect.sync(() => {
|
|
const provider = new WebTracerProvider({
|
|
...(config ?? undefined),
|
|
resource,
|
|
spanProcessors: Array.isArray(processor) ? (processor as any) : [processor]
|
|
})
|
|
return provider
|
|
}),
|
|
(provider) =>
|
|
Effect.ignore(
|
|
Effect.promise(() => provider.forceFlush().then(() => provider.shutdown()))
|
|
)
|
|
)
|
|
})
|
|
)
|
|
|
|
/**
|
|
* Creates a Web OpenTelemetry layer from configuration, providing the resource and enabling tracing, metrics, and logging when configured.
|
|
*
|
|
* **When to use**
|
|
*
|
|
* Use to install browser OpenTelemetry support when service metadata is
|
|
* configured in code and telemetry processors or readers are supplied directly.
|
|
*
|
|
* **Details**
|
|
*
|
|
* The configuration can be provided lazily or effectfully. The layer always
|
|
* provides `Resource.Resource`; tracing, metrics, and logging are installed only
|
|
* when the corresponding processors or readers are non-empty.
|
|
*
|
|
* **Gotchas**
|
|
*
|
|
* Browser resource metadata is explicit; this layer does not read
|
|
* OpenTelemetry environment variables. Empty processor or reader arrays are
|
|
* treated as not configured.
|
|
*
|
|
* @category layers
|
|
* @since 4.0.0
|
|
*/
|
|
export const layer: {
|
|
(evaluate: LazyArg<Configuration>): Layer.Layer<Resource.Resource>
|
|
<E, R>(evaluate: Effect.Effect<Configuration, E, R>): Layer.Layer<Resource.Resource, E, R>
|
|
} = (
|
|
evaluate: LazyArg<Configuration> | Effect.Effect<Configuration, any, any>
|
|
): Layer.Layer<Resource.Resource> =>
|
|
Layer.unwrap(
|
|
Effect.gen(function*() {
|
|
const config = yield* Effect.isEffect(evaluate)
|
|
? evaluate as Effect.Effect<Configuration>
|
|
: Effect.sync(evaluate)
|
|
|
|
const ResourceLive = Resource.layer(config.resource)
|
|
|
|
const TracerLive = isNonEmpty(config.spanProcessor)
|
|
? Layer.provide(
|
|
Tracer.layer,
|
|
layerTracerProvider(config.spanProcessor, config.tracerConfig)
|
|
)
|
|
: Layer.empty
|
|
|
|
const LoggerLive = isNonEmpty(config.logRecordProcessor)
|
|
? Layer.provide(
|
|
Logger.layer({ mergeWithExisting: config.loggerMergeWithExisting }),
|
|
Logger.layerLoggerProvider(config.logRecordProcessor, config.loggerProviderConfig)
|
|
)
|
|
: Layer.empty
|
|
|
|
const MetricsLive = isNonEmpty(config.metricReader)
|
|
? Metrics.layer(constant(config.metricReader), {
|
|
temporality: config.metricTemporality
|
|
})
|
|
: Layer.empty
|
|
|
|
return Layer.mergeAll(TracerLive, MetricsLive, LoggerLive).pipe(
|
|
Layer.provideMerge(ResourceLive)
|
|
)
|
|
})
|
|
)
|