effect 4.0.0-beta.76 → 4.0.0-beta.77

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.
Files changed (36) hide show
  1. package/dist/Config.d.ts +25 -1
  2. package/dist/Config.d.ts.map +1 -1
  3. package/dist/Config.js +32 -0
  4. package/dist/Config.js.map +1 -1
  5. package/dist/unstable/observability/Otlp.d.ts +18 -0
  6. package/dist/unstable/observability/Otlp.d.ts.map +1 -1
  7. package/dist/unstable/observability/Otlp.js +20 -0
  8. package/dist/unstable/observability/Otlp.js.map +1 -1
  9. package/dist/unstable/observability/OtlpLogger.d.ts +17 -1
  10. package/dist/unstable/observability/OtlpLogger.d.ts.map +1 -1
  11. package/dist/unstable/observability/OtlpLogger.js +49 -0
  12. package/dist/unstable/observability/OtlpLogger.js.map +1 -1
  13. package/dist/unstable/observability/OtlpMetrics.d.ts +14 -0
  14. package/dist/unstable/observability/OtlpMetrics.d.ts.map +1 -1
  15. package/dist/unstable/observability/OtlpMetrics.js +45 -0
  16. package/dist/unstable/observability/OtlpMetrics.js.map +1 -1
  17. package/dist/unstable/observability/OtlpResource.d.ts +3 -3
  18. package/dist/unstable/observability/OtlpResource.d.ts.map +1 -1
  19. package/dist/unstable/observability/OtlpResource.js +12 -11
  20. package/dist/unstable/observability/OtlpResource.js.map +1 -1
  21. package/dist/unstable/observability/OtlpTracer.d.ts +15 -0
  22. package/dist/unstable/observability/OtlpTracer.d.ts.map +1 -1
  23. package/dist/unstable/observability/OtlpTracer.js +46 -0
  24. package/dist/unstable/observability/OtlpTracer.js.map +1 -1
  25. package/dist/unstable/observability/internal/otlpEnv.d.ts +8 -0
  26. package/dist/unstable/observability/internal/otlpEnv.d.ts.map +1 -0
  27. package/dist/unstable/observability/internal/otlpEnv.js +30 -0
  28. package/dist/unstable/observability/internal/otlpEnv.js.map +1 -0
  29. package/package.json +1 -1
  30. package/src/Config.ts +44 -0
  31. package/src/unstable/observability/Otlp.ts +36 -0
  32. package/src/unstable/observability/OtlpLogger.ts +56 -1
  33. package/src/unstable/observability/OtlpMetrics.ts +51 -0
  34. package/src/unstable/observability/OtlpResource.ts +27 -16
  35. package/src/unstable/observability/OtlpTracer.ts +52 -0
  36. package/src/unstable/observability/internal/otlpEnv.ts +58 -0
@@ -25,14 +25,17 @@
25
25
  */
26
26
  import * as Arr from "../../Array.ts"
27
27
  import { Clock } from "../../Clock.ts"
28
+ import * as Config from "../../Config.ts"
28
29
  import * as Duration from "../../Duration.ts"
29
30
  import * as Effect from "../../Effect.ts"
30
31
  import * as Layer from "../../Layer.ts"
31
32
  import * as Metric from "../../Metric.ts"
33
+ import * as Option from "../../Option.ts"
32
34
  import type * as Scope from "../../Scope.ts"
33
35
  import type * as Headers from "../http/Headers.ts"
34
36
  import type { HttpBody } from "../http/HttpBody.ts"
35
37
  import type * as HttpClient from "../http/HttpClient.ts"
38
+ import * as OtlpEnv from "./internal/otlpEnv.ts"
36
39
  import * as Exporter from "./OtlpExporter.ts"
37
40
  import type { Fixed64, KeyValue } from "./OtlpResource.ts"
38
41
  import * as OtlpResource from "./OtlpResource.ts"
@@ -475,6 +478,54 @@ export const layer = (options: {
475
478
  readonly temporality?: AggregationTemporality | undefined
476
479
  }): Layer.Layer<never, never, HttpClient.HttpClient | OtlpSerialization> => Layer.effectDiscard(make(options))
477
480
 
481
+ /**
482
+ * Creates an OTLP metrics layer from OpenTelemetry configuration.
483
+ *
484
+ * @category layers
485
+ * @since 4.0.0
486
+ */
487
+ export const layerFromConfig = (options?: {
488
+ readonly resource?: {
489
+ readonly serviceName?: string | undefined
490
+ readonly serviceVersion?: string | undefined
491
+ readonly attributes?: Record<string, unknown>
492
+ } | undefined
493
+ readonly headers?: Headers.Input | undefined
494
+ }): Layer.Layer<never, never, HttpClient.HttpClient | OtlpSerialization> =>
495
+ Effect.gen(function*() {
496
+ const { disabled, endpoint, exporters } = yield* Config.all({
497
+ disabled: Config.boolean("OTEL_SDK_DISABLED").pipe(Config.withDefault(false)),
498
+ endpoint: OtlpEnv.endpoint("METRICS"),
499
+ exporters: OtlpEnv.exporters("METRICS")
500
+ })
501
+ if (disabled || !endpoint || !exporters.includes("otlp")) {
502
+ return Layer.empty
503
+ }
504
+
505
+ const { baseTimeout, metricsTimeout, exportTimeout, exportInterval, temporalityPreference } = yield* Config.all({
506
+ baseTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_TIMEOUT")),
507
+ metricsTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_METRICS_TIMEOUT")),
508
+ exportTimeout: Config.option(Config.int("OTEL_METRIC_EXPORT_TIMEOUT")),
509
+ exportInterval: Config.option(Config.int("OTEL_METRIC_EXPORT_INTERVAL")),
510
+ temporalityPreference: Config.option(
511
+ Config.literals(["delta", "cumulative"], "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE")
512
+ )
513
+ })
514
+
515
+ const shutdownTimeout = Option.firstSomeOf([metricsTimeout, baseTimeout, exportTimeout]).pipe(
516
+ Option.map((_) => Duration.millis(_))
517
+ )
518
+
519
+ return layer({
520
+ url: endpoint,
521
+ resource: options?.resource,
522
+ headers: options?.headers ?? (yield* OtlpEnv.headers("METRICS")),
523
+ exportInterval: Option.getOrUndefined(Option.map(exportInterval, (_) => Duration.millis(_))),
524
+ shutdownTimeout: Option.getOrUndefined(shutdownTimeout),
525
+ temporality: Option.getOrUndefined(temporalityPreference)
526
+ })
527
+ }).pipe(Effect.orDie, Layer.unwrap)
528
+
478
529
  /**
479
530
  * OTLP metrics payload serialized by `OtlpMetrics`.
480
531
  *
@@ -26,10 +26,10 @@
26
26
  * **Gotchas**
27
27
  *
28
28
  * `service.name` is required because the signal exporters also use it as the
29
- * instrumentation scope name. Explicit options take precedence over
30
- * environment variables. `service.name` and `service.version` are normalized
31
- * into canonical OTLP attributes instead of being left in the custom attribute
32
- * map. Unsupported runtime values are formatted as strings.
29
+ * instrumentation scope name. OpenTelemetry environment variables take
30
+ * precedence over explicit options. `service.name` and `service.version` are
31
+ * normalized into canonical OTLP attributes instead of being left in the custom
32
+ * attribute map. Unsupported runtime values are formatted as strings.
33
33
  *
34
34
  * **See also**
35
35
  *
@@ -102,9 +102,9 @@ export const make = (options: {
102
102
  *
103
103
  * **Details**
104
104
  *
105
- * Explicit options override `OTEL_RESOURCE_ATTRIBUTES`, `OTEL_SERVICE_NAME`,
106
- * and `OTEL_SERVICE_VERSION`; missing required configuration is converted to a
107
- * defect.
105
+ * `OTEL_RESOURCE_ATTRIBUTES`, `OTEL_SERVICE_NAME`, and
106
+ * `OTEL_SERVICE_VERSION` override explicit options; missing required
107
+ * configuration is converted to a defect.
108
108
  *
109
109
  * @category constructors
110
110
  * @since 4.0.0
@@ -120,19 +120,30 @@ export const fromConfig: (
120
120
  readonly serviceVersion?: string | undefined
121
121
  readonly attributes?: Record<string, unknown> | undefined
122
122
  }) {
123
+ const env = yield* Config.schema(
124
+ Schema.UndefinedOr(Config.Record(Schema.String, Schema.String)),
125
+ "OTEL_RESOURCE_ATTRIBUTES"
126
+ )
127
+
128
+ const serviceName = (yield* Config.schema(Schema.UndefinedOr(Schema.String), "OTEL_SERVICE_NAME"))
129
+ ?? env?.["service.name"] as string | undefined
130
+ ?? options?.attributes?.["service.name"] as string | undefined
131
+ ?? options?.serviceName
132
+ ?? (yield* Config.string("OTEL_SERVICE_NAME"))
133
+
134
+ const serviceVersion = (yield* Config.schema(Schema.UndefinedOr(Schema.String), "OTEL_SERVICE_VERSION"))
135
+ ?? env?.["service.version"] as string | undefined
136
+ ?? options?.attributes?.["service.version"] as string | undefined
137
+ ?? options?.serviceVersion
138
+
123
139
  const attributes = {
124
- ...(yield* Config.schema(
125
- Schema.UndefinedOr(Config.Record(Schema.String, Schema.String)),
126
- "OTEL_RESOURCE_ATTRIBUTES"
127
- )),
128
- ...options?.attributes
140
+ ...options?.attributes,
141
+ ...env
129
142
  }
130
- const serviceName = options?.serviceName ?? attributes["service.name"] as string ??
131
- (yield* Config.schema(Schema.String, "OTEL_SERVICE_NAME"))
143
+
132
144
  delete attributes["service.name"]
133
- const serviceVersion = options?.serviceVersion ?? attributes["service.version"] as string ??
134
- (yield* Config.schema(Schema.UndefinedOr(Schema.String), "OTEL_SERVICE_VERSION"))
135
145
  delete attributes["service.version"]
146
+
136
147
  return make({
137
148
  serviceName,
138
149
  serviceVersion,
@@ -24,6 +24,7 @@
24
24
  * @since 4.0.0
25
25
  */
26
26
  import * as Cause from "../../Cause.ts"
27
+ import * as Config from "../../Config.ts"
27
28
  import type * as Context from "../../Context.ts"
28
29
  import * as Duration from "../../Duration.ts"
29
30
  import * as Effect from "../../Effect.ts"
@@ -36,6 +37,7 @@ import * as Tracer from "../../Tracer.ts"
36
37
  import type { ExtractTag, Mutable } from "../../Types.ts"
37
38
  import type * as Headers from "../http/Headers.ts"
38
39
  import type * as HttpClient from "../http/HttpClient.ts"
40
+ import * as OtlpEnv from "./internal/otlpEnv.ts"
39
41
  import * as Exporter from "./OtlpExporter.ts"
40
42
  import type { KeyValue, Resource } from "./OtlpResource.ts"
41
43
  import { entriesToAttributes } from "./OtlpResource.ts"
@@ -147,6 +149,56 @@ export const layer: (options: {
147
149
  readonly shutdownTimeout?: Duration.Input | undefined
148
150
  }) => Layer.Layer<never, never, OtlpSerialization | HttpClient.HttpClient> = flow(make, Layer.effect(Tracer.Tracer))
149
151
 
152
+ /**
153
+ * Creates an OTLP traces layer from OpenTelemetry configuration.
154
+ *
155
+ * @category layers
156
+ * @since 4.0.0
157
+ */
158
+ export const layerFromConfig = (options?: {
159
+ readonly resource?: {
160
+ readonly serviceName?: string | undefined
161
+ readonly serviceVersion?: string | undefined
162
+ readonly attributes?: Record<string, unknown>
163
+ } | undefined
164
+ readonly headers?: Headers.Input | undefined
165
+ readonly context?: (<X>(primitive: Tracer.EffectPrimitive<X>, span: Tracer.AnySpan) => X) | undefined
166
+ }): Layer.Layer<never, never, HttpClient.HttpClient | OtlpSerialization> =>
167
+ Effect.gen(function*() {
168
+ const { disabled, endpoint, exporters } = yield* Config.all({
169
+ disabled: Config.boolean("OTEL_SDK_DISABLED").pipe(Config.withDefault(false)),
170
+ endpoint: OtlpEnv.endpoint("TRACES"),
171
+ exporters: OtlpEnv.exporters("TRACES")
172
+ })
173
+
174
+ if (disabled || !endpoint || !exporters.includes("otlp")) {
175
+ return Layer.empty
176
+ }
177
+
178
+ const { baseTimeout, tracesTimeout, exportTimeout, scheduleDelay, maxBatchSize } = yield* Config.all({
179
+ baseTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_TIMEOUT")),
180
+ tracesTimeout: Config.option(Config.int("OTEL_EXPORTER_OTLP_TRACES_TIMEOUT")),
181
+ exportTimeout: Config.option(Config.int("OTEL_BSP_EXPORT_TIMEOUT")),
182
+ scheduleDelay: Config.option(Config.int("OTEL_BSP_SCHEDULE_DELAY")),
183
+ maxBatchSize: Config.option(Config.int("OTEL_BSP_MAX_EXPORT_BATCH_SIZE"))
184
+ })
185
+
186
+ const shutdownTimeout = Option.firstSomeOf([tracesTimeout, baseTimeout, exportTimeout]).pipe(
187
+ Option.map((_) => Duration.millis(_))
188
+ )
189
+ const exportInterval = Option.map(scheduleDelay, (_) => Duration.millis(_))
190
+
191
+ return layer({
192
+ url: endpoint,
193
+ resource: options?.resource,
194
+ headers: options?.headers ?? (yield* OtlpEnv.headers("TRACES")),
195
+ exportInterval: Option.getOrUndefined(exportInterval),
196
+ maxBatchSize: Option.getOrUndefined(maxBatchSize),
197
+ context: options?.context,
198
+ shutdownTimeout: Option.getOrUndefined(shutdownTimeout)
199
+ })
200
+ }).pipe(Effect.orDie, Layer.unwrap)
201
+
150
202
  // internal
151
203
 
152
204
  interface SpanImpl extends Tracer.Span {
@@ -0,0 +1,58 @@
1
+ import * as Config from "../../../Config.ts"
2
+ import * as Effect from "../../../Effect.ts"
3
+ import * as Option from "../../../Option.ts"
4
+ import * as Schema from "../../../Schema.ts"
5
+ import * as SchemaGetter from "../../../SchemaGetter.ts"
6
+ import * as SchemaTransformation from "../../../SchemaTransformation.ts"
7
+ import * as HttpClientRequest from "../../http/HttpClientRequest.ts"
8
+
9
+ export type Signal = "LOGS" | "METRICS" | "TRACES"
10
+
11
+ const ExporterList = Config.Array(
12
+ Schema.String.pipe(
13
+ Schema.decode(SchemaTransformation.trim()),
14
+ Schema.decode(SchemaTransformation.toLowerCase())
15
+ )
16
+ ).pipe(
17
+ Schema.decode({
18
+ decode: SchemaGetter.transform((_: ReadonlyArray<string>) => _.filter((_) => _ !== "")),
19
+ encode: SchemaGetter.passthrough()
20
+ })
21
+ )
22
+
23
+ const HeadersRecord = Config.Record(Schema.String, Schema.String)
24
+
25
+ export const headers = (signal: Signal) =>
26
+ Config.make((provider) =>
27
+ Effect.gen(function*() {
28
+ const headers = yield* Config.option(Config.schema(HeadersRecord, `OTEL_EXPORTER_OTLP_${signal}_HEADERS`)).parse(
29
+ provider
30
+ )
31
+ if (Option.isSome(headers)) {
32
+ return headers.value
33
+ }
34
+
35
+ const fallback = yield* Config.option(Config.schema(HeadersRecord, "OTEL_EXPORTER_OTLP_HEADERS")).parse(provider)
36
+ return Option.getOrUndefined(fallback)
37
+ })
38
+ )
39
+
40
+ export const endpoint = (signal: Signal) =>
41
+ Config.make((provider) =>
42
+ Effect.gen(function*() {
43
+ const endpoint = yield* Config.option(Config.string(`OTEL_EXPORTER_OTLP_${signal}_ENDPOINT`)).parse(provider)
44
+ if (Option.isSome(endpoint)) {
45
+ return endpoint.value
46
+ }
47
+
48
+ const fallback = yield* Config.option(Config.string("OTEL_EXPORTER_OTLP_ENDPOINT")).parse(provider)
49
+ return Option.isSome(fallback) && fallback.value !== ""
50
+ ? HttpClientRequest.appendUrl(HttpClientRequest.get(fallback.value), `/v1/${signal.toLowerCase()}`).url
51
+ : undefined
52
+ })
53
+ )
54
+
55
+ export const exporters = (signal: Signal) =>
56
+ Config.option(Config.schema(ExporterList, `OTEL_${signal}_EXPORTER`)).pipe(
57
+ Config.map(Option.getOrElse<ReadonlyArray<string>>(() => []))
58
+ )