@effect/opentelemetry 0.60.0 → 4.0.0-beta.0
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/LICENSE +1 -1
- package/dist/{dts/Logger.d.ts → Logger.d.ts} +19 -13
- package/dist/Logger.d.ts.map +1 -0
- package/dist/Logger.js +76 -0
- package/dist/Logger.js.map +1 -0
- package/dist/Metrics.d.ts +76 -0
- package/dist/Metrics.d.ts.map +1 -0
- package/dist/Metrics.js +59 -0
- package/dist/Metrics.js.map +1 -0
- package/dist/{dts/NodeSdk.d.ts → NodeSdk.d.ts} +12 -9
- package/dist/NodeSdk.d.ts.map +1 -0
- package/dist/{esm/NodeSdk.js → NodeSdk.js} +23 -14
- package/dist/NodeSdk.js.map +1 -0
- package/dist/{dts/Resource.d.ts → Resource.d.ts} +10 -13
- package/dist/Resource.d.ts.map +1 -0
- package/dist/{esm/Resource.js → Resource.js} +12 -13
- package/dist/Resource.js.map +1 -0
- package/dist/Tracer.d.ts +129 -0
- package/dist/Tracer.d.ts.map +1 -0
- package/dist/Tracer.js +391 -0
- package/dist/Tracer.js.map +1 -0
- package/dist/{dts/WebSdk.d.ts → WebSdk.d.ts} +12 -9
- package/dist/WebSdk.d.ts.map +1 -0
- package/dist/WebSdk.js +41 -0
- package/dist/WebSdk.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/{esm/index.js → index.js} +4 -20
- package/dist/index.js.map +1 -0
- package/dist/internal/attributes.d.ts +2 -0
- package/dist/internal/attributes.d.ts.map +1 -0
- package/dist/internal/attributes.js +19 -0
- package/dist/internal/attributes.js.map +1 -0
- package/dist/{dts/internal → internal}/metrics.d.ts.map +1 -1
- package/dist/internal/metrics.js +406 -0
- package/dist/internal/metrics.js.map +1 -0
- package/dist/internal/utilities.d.ts +2 -0
- package/dist/internal/utilities.d.ts.map +1 -0
- package/dist/internal/utilities.js +3 -0
- package/dist/internal/utilities.js.map +1 -0
- package/package.json +70 -118
- package/src/Logger.ts +52 -55
- package/src/Metrics.ts +92 -18
- package/src/NodeSdk.ts +67 -64
- package/src/Resource.ts +16 -24
- package/src/Tracer.ts +469 -78
- package/src/WebSdk.ts +59 -51
- package/src/index.ts +7 -26
- package/src/internal/attributes.ts +21 -0
- package/src/internal/metrics.ts +381 -250
- package/src/internal/utilities.ts +5 -0
- package/Logger/package.json +0 -6
- package/Metrics/package.json +0 -6
- package/NodeSdk/package.json +0 -6
- package/Otlp/package.json +0 -6
- package/OtlpLogger/package.json +0 -6
- package/OtlpMetrics/package.json +0 -6
- package/OtlpResource/package.json +0 -6
- package/OtlpTracer/package.json +0 -6
- package/Resource/package.json +0 -6
- package/Tracer/package.json +0 -6
- package/WebSdk/package.json +0 -6
- package/dist/cjs/Logger.js +0 -85
- package/dist/cjs/Logger.js.map +0 -1
- package/dist/cjs/Metrics.js +0 -24
- package/dist/cjs/Metrics.js.map +0 -1
- package/dist/cjs/NodeSdk.js +0 -53
- package/dist/cjs/NodeSdk.js.map +0 -1
- package/dist/cjs/Otlp.js +0 -46
- package/dist/cjs/Otlp.js.map +0 -1
- package/dist/cjs/OtlpLogger.js +0 -158
- package/dist/cjs/OtlpLogger.js.map +0 -1
- package/dist/cjs/OtlpMetrics.js +0 -354
- package/dist/cjs/OtlpMetrics.js.map +0 -1
- package/dist/cjs/OtlpResource.js +0 -136
- package/dist/cjs/OtlpResource.js.map +0 -1
- package/dist/cjs/OtlpTracer.js +0 -229
- package/dist/cjs/OtlpTracer.js.map +0 -1
- package/dist/cjs/Resource.js +0 -75
- package/dist/cjs/Resource.js.map +0 -1
- package/dist/cjs/Tracer.js +0 -87
- package/dist/cjs/Tracer.js.map +0 -1
- package/dist/cjs/WebSdk.js +0 -42
- package/dist/cjs/WebSdk.js.map +0 -1
- package/dist/cjs/index.js +0 -30
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/internal/metrics.js +0 -288
- package/dist/cjs/internal/metrics.js.map +0 -1
- package/dist/cjs/internal/otlpExporter.js +0 -81
- package/dist/cjs/internal/otlpExporter.js.map +0 -1
- package/dist/cjs/internal/tracer.js +0 -299
- package/dist/cjs/internal/tracer.js.map +0 -1
- package/dist/cjs/internal/utils.js +0 -34
- package/dist/cjs/internal/utils.js.map +0 -1
- package/dist/dts/Logger.d.ts.map +0 -1
- package/dist/dts/Metrics.d.ts +0 -29
- package/dist/dts/Metrics.d.ts.map +0 -1
- package/dist/dts/NodeSdk.d.ts.map +0 -1
- package/dist/dts/Otlp.d.ts +0 -31
- package/dist/dts/Otlp.d.ts.map +0 -1
- package/dist/dts/OtlpLogger.d.ts +0 -46
- package/dist/dts/OtlpLogger.d.ts.map +0 -1
- package/dist/dts/OtlpMetrics.d.ts +0 -40
- package/dist/dts/OtlpMetrics.d.ts.map +0 -1
- package/dist/dts/OtlpResource.d.ts +0 -104
- package/dist/dts/OtlpResource.d.ts.map +0 -1
- package/dist/dts/OtlpTracer.d.ts +0 -49
- package/dist/dts/OtlpTracer.d.ts.map +0 -1
- package/dist/dts/Resource.d.ts.map +0 -1
- package/dist/dts/Tracer.d.ts +0 -143
- package/dist/dts/Tracer.d.ts.map +0 -1
- package/dist/dts/WebSdk.d.ts.map +0 -1
- package/dist/dts/index.d.ts +0 -45
- package/dist/dts/index.d.ts.map +0 -1
- package/dist/dts/internal/otlpExporter.d.ts +0 -2
- package/dist/dts/internal/otlpExporter.d.ts.map +0 -1
- package/dist/dts/internal/tracer.d.ts +0 -2
- package/dist/dts/internal/tracer.d.ts.map +0 -1
- package/dist/dts/internal/utils.d.ts +0 -2
- package/dist/dts/internal/utils.d.ts.map +0 -1
- package/dist/esm/Logger.js +0 -75
- package/dist/esm/Logger.js.map +0 -1
- package/dist/esm/Metrics.js +0 -17
- package/dist/esm/Metrics.js.map +0 -1
- package/dist/esm/NodeSdk.js.map +0 -1
- package/dist/esm/Otlp.js +0 -38
- package/dist/esm/Otlp.js.map +0 -1
- package/dist/esm/OtlpLogger.js +0 -150
- package/dist/esm/OtlpLogger.js.map +0 -1
- package/dist/esm/OtlpMetrics.js +0 -346
- package/dist/esm/OtlpMetrics.js.map +0 -1
- package/dist/esm/OtlpResource.js +0 -124
- package/dist/esm/OtlpResource.js.map +0 -1
- package/dist/esm/OtlpTracer.js +0 -221
- package/dist/esm/OtlpTracer.js.map +0 -1
- package/dist/esm/Resource.js.map +0 -1
- package/dist/esm/Tracer.js +0 -80
- package/dist/esm/Tracer.js.map +0 -1
- package/dist/esm/WebSdk.js +0 -33
- package/dist/esm/WebSdk.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/internal/metrics.js +0 -278
- package/dist/esm/internal/metrics.js.map +0 -1
- package/dist/esm/internal/otlpExporter.js +0 -74
- package/dist/esm/internal/otlpExporter.js.map +0 -1
- package/dist/esm/internal/tracer.js +0 -290
- package/dist/esm/internal/tracer.js.map +0 -1
- package/dist/esm/internal/utils.js +0 -23
- package/dist/esm/internal/utils.js.map +0 -1
- package/dist/esm/package.json +0 -4
- package/index/package.json +0 -6
- package/src/Otlp.ts +0 -66
- package/src/OtlpLogger.ts +0 -258
- package/src/OtlpMetrics.ts +0 -571
- package/src/OtlpResource.ts +0 -232
- package/src/OtlpTracer.ts +0 -349
- package/src/internal/otlpExporter.ts +0 -124
- package/src/internal/tracer.ts +0 -437
- package/src/internal/utils.ts +0 -31
- /package/dist/{dts/internal → internal}/metrics.d.ts +0 -0
package/src/Tracer.ts
CHANGED
|
@@ -1,133 +1,302 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import type
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
4
|
+
import * as Otel from "@opentelemetry/api"
|
|
5
|
+
import * as OtelSemConv from "@opentelemetry/semantic-conventions"
|
|
6
|
+
import * as Cause from "effect/Cause"
|
|
7
|
+
import type * as Clock from "effect/Clock"
|
|
8
|
+
import * as Effect from "effect/Effect"
|
|
9
|
+
import * as Exit from "effect/Exit"
|
|
10
|
+
import { constTrue, dual } from "effect/Function"
|
|
11
|
+
import * as Layer from "effect/Layer"
|
|
12
|
+
import * as Predicate from "effect/Predicate"
|
|
13
|
+
import * as ServiceMap from "effect/ServiceMap"
|
|
14
|
+
import * as Tracer from "effect/Tracer"
|
|
15
|
+
import { recordToAttributes, unknownToAttributeValue } from "./internal/attributes.ts"
|
|
16
|
+
import { Resource } from "./Resource.ts"
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
17
|
-
export const make: Effect<EffectTracer, never, OtelTracer> = internal.make
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Service Definitions
|
|
20
|
+
// =============================================================================
|
|
18
21
|
|
|
19
22
|
/**
|
|
20
23
|
* @since 1.0.0
|
|
21
|
-
* @category
|
|
24
|
+
* @category Services
|
|
22
25
|
*/
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
readonly traceFlags?: number | undefined
|
|
28
|
-
readonly traceState?: string | Otel.TraceState | undefined
|
|
29
|
-
}
|
|
30
|
-
) => ExternalSpan = internal.makeExternalSpan
|
|
26
|
+
export class OtelTracer extends ServiceMap.Service<
|
|
27
|
+
OtelTracer,
|
|
28
|
+
Otel.Tracer
|
|
29
|
+
>()("@effect/opentelemetry/Tracer") {}
|
|
31
30
|
|
|
32
31
|
/**
|
|
33
|
-
* Get the current OpenTelemetry span.
|
|
34
|
-
*
|
|
35
|
-
* Works with both the official OpenTelemetry API (via `Tracer.layer`, `NodeSdk.layer`, etc.)
|
|
36
|
-
* and the lightweight OTLP module (`OtlpTracer.layer`).
|
|
37
|
-
*
|
|
38
|
-
* When using OTLP, the returned span is a wrapper that conforms to the
|
|
39
|
-
* OpenTelemetry `Span` interface.
|
|
40
|
-
*
|
|
41
32
|
* @since 1.0.0
|
|
42
|
-
* @category
|
|
33
|
+
* @category Services
|
|
43
34
|
*/
|
|
44
|
-
export
|
|
35
|
+
export class OtelTracerProvider extends ServiceMap.Service<
|
|
36
|
+
OtelTracerProvider,
|
|
37
|
+
Otel.TracerProvider
|
|
38
|
+
>()("@effect/opentelemetry/Tracer/OtelTracerProvider") {}
|
|
45
39
|
|
|
46
40
|
/**
|
|
47
41
|
* @since 1.0.0
|
|
48
|
-
* @category
|
|
42
|
+
* @category Services
|
|
49
43
|
*/
|
|
50
|
-
export
|
|
44
|
+
export class OtelTraceFlags extends ServiceMap.Service<
|
|
45
|
+
OtelTraceFlags,
|
|
46
|
+
Otel.TraceFlags
|
|
47
|
+
>()("@effect/opentelemetry/Tracer/OtelTraceFlags") {}
|
|
51
48
|
|
|
52
49
|
/**
|
|
53
50
|
* @since 1.0.0
|
|
54
|
-
* @category
|
|
51
|
+
* @category Services
|
|
55
52
|
*/
|
|
56
|
-
export
|
|
53
|
+
export class OtelTraceState extends ServiceMap.Service<
|
|
54
|
+
OtelTraceState,
|
|
55
|
+
Otel.TraceState
|
|
56
|
+
>()("@effect/opentelemetry/Tracer/OtelTraceState") {}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
*/
|
|
62
|
-
export const layerGlobal: Layer<OtelTracer, never, Resource> = internal.layerGlobal
|
|
58
|
+
// =============================================================================
|
|
59
|
+
// Constructors
|
|
60
|
+
// =============================================================================
|
|
63
61
|
|
|
64
62
|
/**
|
|
65
63
|
* @since 1.0.0
|
|
66
|
-
* @category
|
|
64
|
+
* @category Constructors
|
|
67
65
|
*/
|
|
68
|
-
export const
|
|
66
|
+
export const make: Effect.Effect<Tracer.Tracer, never, OtelTracer> = Effect.map(
|
|
67
|
+
Effect.service(OtelTracer),
|
|
68
|
+
(tracer) =>
|
|
69
|
+
Tracer.make({
|
|
70
|
+
span(options) {
|
|
71
|
+
return new OtelSpan(
|
|
72
|
+
Otel.context,
|
|
73
|
+
Otel.trace,
|
|
74
|
+
tracer,
|
|
75
|
+
options
|
|
76
|
+
)
|
|
77
|
+
},
|
|
78
|
+
context(primitive, fiber) {
|
|
79
|
+
const currentSpan = fiber.currentSpan
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
if (currentSpan === undefined) {
|
|
82
|
+
return primitive["~effect/Effect/evaluate"](fiber)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return Otel.context.with(
|
|
86
|
+
populateContext(Otel.context.active(), currentSpan),
|
|
87
|
+
() => primitive["~effect/Effect/evaluate"](fiber)
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
)
|
|
75
92
|
|
|
76
93
|
/**
|
|
77
94
|
* @since 1.0.0
|
|
78
|
-
* @category
|
|
95
|
+
* @category Constructors
|
|
79
96
|
*/
|
|
80
|
-
export
|
|
81
|
-
readonly
|
|
97
|
+
export const makeExternalSpan = (options: {
|
|
98
|
+
readonly traceId: string
|
|
99
|
+
readonly spanId: string
|
|
100
|
+
readonly traceFlags?: number | undefined
|
|
101
|
+
readonly traceState?: string | Otel.TraceState | undefined
|
|
102
|
+
}): Tracer.ExternalSpan => {
|
|
103
|
+
let annotations = ServiceMap.empty()
|
|
104
|
+
|
|
105
|
+
if (options.traceFlags !== undefined) {
|
|
106
|
+
annotations = ServiceMap.add(annotations, OtelTraceFlags, options.traceFlags)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (typeof options.traceState === "string") {
|
|
110
|
+
try {
|
|
111
|
+
const traceState = Otel.createTraceState(options.traceState)
|
|
112
|
+
annotations = ServiceMap.add(annotations, OtelTraceState, traceState)
|
|
113
|
+
} catch {
|
|
114
|
+
//
|
|
115
|
+
}
|
|
116
|
+
} else if (options.traceState) {
|
|
117
|
+
annotations = ServiceMap.add(annotations, OtelTraceState, options.traceState)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
_tag: "ExternalSpan",
|
|
122
|
+
traceId: options.traceId,
|
|
123
|
+
spanId: options.spanId,
|
|
124
|
+
sampled: Predicate.isNotUndefined(options.traceFlags) ? isSampled(options.traceFlags) : true,
|
|
125
|
+
annotations
|
|
126
|
+
}
|
|
82
127
|
}
|
|
83
128
|
|
|
129
|
+
// =============================================================================
|
|
130
|
+
// Layers
|
|
131
|
+
// =============================================================================
|
|
132
|
+
|
|
84
133
|
/**
|
|
85
134
|
* @since 1.0.0
|
|
86
|
-
* @category
|
|
135
|
+
* @category Layers
|
|
87
136
|
*/
|
|
88
|
-
export const
|
|
137
|
+
export const layerGlobalProvider: Layer.Layer<OtelTracerProvider> = Layer.sync(
|
|
138
|
+
OtelTracerProvider,
|
|
139
|
+
() => Otel.trace.getTracerProvider()
|
|
140
|
+
)
|
|
89
141
|
|
|
90
142
|
/**
|
|
91
143
|
* @since 1.0.0
|
|
92
|
-
* @category
|
|
144
|
+
* @category Layers
|
|
93
145
|
*/
|
|
94
|
-
export
|
|
95
|
-
|
|
96
|
-
|
|
146
|
+
export const layerTracer: Layer.Layer<OtelTracer, never, OtelTracerProvider | Resource> = Layer.effect(
|
|
147
|
+
OtelTracer,
|
|
148
|
+
Effect.gen(function*() {
|
|
149
|
+
const resource = yield* Resource
|
|
150
|
+
const provider = yield* OtelTracerProvider
|
|
151
|
+
return provider.getTracer(
|
|
152
|
+
resource.attributes[OtelSemConv.ATTR_SERVICE_NAME] as string,
|
|
153
|
+
resource.attributes[OtelSemConv.ATTR_SERVICE_VERSION] as string
|
|
154
|
+
)
|
|
155
|
+
})
|
|
156
|
+
)
|
|
97
157
|
|
|
98
158
|
/**
|
|
99
159
|
* @since 1.0.0
|
|
100
|
-
* @category
|
|
160
|
+
* @category Layers
|
|
101
161
|
*/
|
|
102
|
-
export const
|
|
162
|
+
export const layerGlobalTracer: Layer.Layer<OtelTracer, never, Resource> = layerTracer.pipe(
|
|
163
|
+
Layer.provide(layerGlobalProvider)
|
|
164
|
+
)
|
|
103
165
|
|
|
104
166
|
/**
|
|
105
167
|
* @since 1.0.0
|
|
106
|
-
* @category
|
|
168
|
+
* @category Layers
|
|
107
169
|
*/
|
|
108
|
-
export
|
|
109
|
-
|
|
110
|
-
|
|
170
|
+
export const layerGlobal: Layer.Layer<OtelTracer, never, Resource> = Layer.effect(Tracer.Tracer, make).pipe(
|
|
171
|
+
Layer.provideMerge(layerGlobalTracer)
|
|
172
|
+
)
|
|
111
173
|
|
|
112
174
|
/**
|
|
113
175
|
* @since 1.0.0
|
|
114
|
-
* @category
|
|
176
|
+
* @category Layers
|
|
115
177
|
*/
|
|
116
|
-
export const
|
|
178
|
+
export const layerWithoutOtelTracer: Layer.Layer<never, never, OtelTracer> = Layer.effect(Tracer.Tracer, make)
|
|
117
179
|
|
|
118
180
|
/**
|
|
119
181
|
* @since 1.0.0
|
|
120
|
-
* @category
|
|
182
|
+
* @category Layers
|
|
121
183
|
*/
|
|
122
|
-
export
|
|
123
|
-
|
|
124
|
-
|
|
184
|
+
export const layer: Layer.Layer<OtelTracer, never, OtelTracerProvider | Resource> = layerWithoutOtelTracer.pipe(
|
|
185
|
+
Layer.provideMerge(layerTracer)
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
// =============================================================================
|
|
189
|
+
// Utilities / Combinators
|
|
190
|
+
// =============================================================================
|
|
191
|
+
|
|
192
|
+
const bigint1e6 = BigInt(1_000_000)
|
|
193
|
+
const bigint1e9 = BigInt(1_000_000_000)
|
|
125
194
|
|
|
126
195
|
/**
|
|
196
|
+
* Get the current OpenTelemetry span.
|
|
197
|
+
*
|
|
198
|
+
* Works with both the official OpenTelemetry API (via `Tracer.layer`,
|
|
199
|
+
* `NodeSdk.layer`, etc.) and the lightweight OTLP module (`OtlpTracer.layer`).
|
|
200
|
+
*
|
|
201
|
+
* When using OTLP, the returned span is a wrapper that conforms to the
|
|
202
|
+
* OpenTelemetry `Span` interface.
|
|
203
|
+
*
|
|
127
204
|
* @since 1.0.0
|
|
128
|
-
* @category
|
|
205
|
+
* @category accessors
|
|
129
206
|
*/
|
|
130
|
-
export const
|
|
207
|
+
export const currentOtelSpan: Effect.Effect<Otel.Span, Cause.NoSuchElementError> = Effect.clockWith((clock) =>
|
|
208
|
+
Effect.map(Effect.currentSpan, (span) =>
|
|
209
|
+
OtelSpanTypeId in span
|
|
210
|
+
? (span as OtelSpan).span
|
|
211
|
+
: makeOtelSpan(span, clock))
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
const makeOtelSpan = (span: Tracer.Span, clock: Clock.Clock): Otel.Span => {
|
|
215
|
+
const spanContext: Otel.SpanContext = {
|
|
216
|
+
traceId: span.traceId,
|
|
217
|
+
spanId: span.spanId,
|
|
218
|
+
traceFlags: span.sampled ? Otel.TraceFlags.SAMPLED : Otel.TraceFlags.NONE,
|
|
219
|
+
isRemote: false
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let exit = Exit.void
|
|
223
|
+
|
|
224
|
+
const self: Otel.Span = {
|
|
225
|
+
spanContext: () => spanContext,
|
|
226
|
+
setAttribute(key, value) {
|
|
227
|
+
span.attribute(key, value)
|
|
228
|
+
return self
|
|
229
|
+
},
|
|
230
|
+
setAttributes(attributes) {
|
|
231
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
232
|
+
span.attribute(key, value)
|
|
233
|
+
}
|
|
234
|
+
return self
|
|
235
|
+
},
|
|
236
|
+
addEvent(name) {
|
|
237
|
+
let attributes: Otel.Attributes | undefined = undefined
|
|
238
|
+
let startTime: Otel.TimeInput | undefined = undefined
|
|
239
|
+
if (arguments.length === 3) {
|
|
240
|
+
attributes = arguments[1]
|
|
241
|
+
startTime = arguments[2]
|
|
242
|
+
} else {
|
|
243
|
+
startTime = arguments[1]
|
|
244
|
+
}
|
|
245
|
+
span.event(name, convertOtelTimeInput(startTime, clock), attributes)
|
|
246
|
+
return self
|
|
247
|
+
},
|
|
248
|
+
addLink(link) {
|
|
249
|
+
span.addLinks([{
|
|
250
|
+
span: makeExternalSpan(link.context),
|
|
251
|
+
attributes: link.attributes ?? {}
|
|
252
|
+
}])
|
|
253
|
+
return self
|
|
254
|
+
},
|
|
255
|
+
addLinks(links) {
|
|
256
|
+
span.addLinks(links.map((link) => ({
|
|
257
|
+
span: makeExternalSpan(link.context),
|
|
258
|
+
attributes: link.attributes ?? {}
|
|
259
|
+
})))
|
|
260
|
+
return self
|
|
261
|
+
},
|
|
262
|
+
setStatus(status) {
|
|
263
|
+
exit = Otel.SpanStatusCode.ERROR
|
|
264
|
+
? Exit.die(status.message ?? "Unknown error")
|
|
265
|
+
: Exit.void
|
|
266
|
+
return self
|
|
267
|
+
},
|
|
268
|
+
updateName: () => self,
|
|
269
|
+
end(endTime) {
|
|
270
|
+
const time = convertOtelTimeInput(endTime, clock)
|
|
271
|
+
span.end(time, exit)
|
|
272
|
+
return self
|
|
273
|
+
},
|
|
274
|
+
isRecording: constTrue,
|
|
275
|
+
recordException(exception, timeInput) {
|
|
276
|
+
const time = convertOtelTimeInput(timeInput, clock)
|
|
277
|
+
const cause = Cause.fail(exception)
|
|
278
|
+
const error = Cause.prettyErrors(cause)[0]
|
|
279
|
+
span.event(error.message, time, {
|
|
280
|
+
"exception.type": error.name,
|
|
281
|
+
"exception.message": error.message,
|
|
282
|
+
"exception.stacktrace": error.stack ?? ""
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return self
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const convertOtelTimeInput = (input: Otel.TimeInput | undefined, clock: Clock.Clock): bigint => {
|
|
290
|
+
if (input === undefined) {
|
|
291
|
+
return clock.currentTimeNanosUnsafe()
|
|
292
|
+
} else if (typeof input === "number") {
|
|
293
|
+
return BigInt(Math.round(input * 1_000_000))
|
|
294
|
+
} else if (input instanceof Date) {
|
|
295
|
+
return BigInt(input.getTime()) * bigint1e6
|
|
296
|
+
}
|
|
297
|
+
const [seconds, nanos] = input
|
|
298
|
+
return BigInt(seconds) * bigint1e9 + BigInt(nanos)
|
|
299
|
+
}
|
|
131
300
|
|
|
132
301
|
/**
|
|
133
302
|
* Set the effect's parent span from the given opentelemetry `SpanContext`.
|
|
@@ -136,7 +305,7 @@ export const OtelTraceState: Tag<OtelTraceState, Otel.TraceState> = internal.tra
|
|
|
136
305
|
* attach to a parent span.
|
|
137
306
|
*
|
|
138
307
|
* @since 1.0.0
|
|
139
|
-
* @category
|
|
308
|
+
* @category Propagation
|
|
140
309
|
*/
|
|
141
310
|
export const withSpanContext: {
|
|
142
311
|
/**
|
|
@@ -146,11 +315,11 @@ export const withSpanContext: {
|
|
|
146
315
|
* attach to a parent span.
|
|
147
316
|
*
|
|
148
317
|
* @since 1.0.0
|
|
149
|
-
* @category
|
|
318
|
+
* @category Propagation
|
|
150
319
|
*/
|
|
151
320
|
(spanContext: Otel.SpanContext): <A, E, R>(
|
|
152
|
-
|
|
153
|
-
) => Effect<A, E, Exclude<R, ParentSpan>>
|
|
321
|
+
self: Effect.Effect<A, E, R>
|
|
322
|
+
) => Effect.Effect<A, E, Exclude<R, Tracer.ParentSpan>>
|
|
154
323
|
/**
|
|
155
324
|
* Set the effect's parent span from the given opentelemetry `SpanContext`.
|
|
156
325
|
*
|
|
@@ -158,7 +327,229 @@ export const withSpanContext: {
|
|
|
158
327
|
* attach to a parent span.
|
|
159
328
|
*
|
|
160
329
|
* @since 1.0.0
|
|
161
|
-
* @category
|
|
330
|
+
* @category Propagation
|
|
162
331
|
*/
|
|
163
|
-
<A, E, R>(
|
|
164
|
-
} =
|
|
332
|
+
<A, E, R>(self: Effect.Effect<A, E, R>, spanContext: Otel.SpanContext): Effect.Effect<A, E, Exclude<R, Tracer.ParentSpan>>
|
|
333
|
+
} = dual(2, <A, E, R>(
|
|
334
|
+
self: Effect.Effect<A, E, R>,
|
|
335
|
+
spanContext: Otel.SpanContext
|
|
336
|
+
) => Effect.withParentSpan(self, makeExternalSpan(spanContext)))
|
|
337
|
+
|
|
338
|
+
// =============================================================================
|
|
339
|
+
// Internals
|
|
340
|
+
// =============================================================================
|
|
341
|
+
|
|
342
|
+
const OtelSpanTypeId = "~@effect/opentelemetry/Tracer/OtelSpan"
|
|
343
|
+
|
|
344
|
+
const kindMap = {
|
|
345
|
+
"internal": Otel.SpanKind.INTERNAL,
|
|
346
|
+
"client": Otel.SpanKind.CLIENT,
|
|
347
|
+
"server": Otel.SpanKind.SERVER,
|
|
348
|
+
"producer": Otel.SpanKind.PRODUCER,
|
|
349
|
+
"consumer": Otel.SpanKind.CONSUMER
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** @internal */
|
|
353
|
+
export class OtelSpan implements Tracer.Span {
|
|
354
|
+
readonly [OtelSpanTypeId]: typeof OtelSpanTypeId
|
|
355
|
+
readonly _tag = "Span"
|
|
356
|
+
|
|
357
|
+
readonly name: string
|
|
358
|
+
readonly kind: Tracer.SpanKind
|
|
359
|
+
readonly annotations: ServiceMap.ServiceMap<never>
|
|
360
|
+
readonly links: Array<Tracer.SpanLink>
|
|
361
|
+
readonly span: Otel.Span
|
|
362
|
+
readonly spanId: string
|
|
363
|
+
readonly traceId: string
|
|
364
|
+
readonly attributes = new Map<string, unknown>()
|
|
365
|
+
readonly sampled: boolean
|
|
366
|
+
readonly parent: Tracer.AnySpan | undefined
|
|
367
|
+
status: Tracer.SpanStatus
|
|
368
|
+
|
|
369
|
+
constructor(
|
|
370
|
+
contextApi: Otel.ContextAPI,
|
|
371
|
+
traceApi: Otel.TraceAPI,
|
|
372
|
+
tracer: Otel.Tracer,
|
|
373
|
+
options: Parameters<Tracer.Tracer["span"]>[0]
|
|
374
|
+
) {
|
|
375
|
+
this[OtelSpanTypeId] = OtelSpanTypeId
|
|
376
|
+
this.name = options.name
|
|
377
|
+
this.annotations = options.annotations
|
|
378
|
+
this.links = options.links
|
|
379
|
+
this.kind = options.kind
|
|
380
|
+
const active = contextApi.active()
|
|
381
|
+
this.parent = options.parent ?? (options?.root !== true) ?
|
|
382
|
+
getOtelParent(traceApi, active, options.annotations) :
|
|
383
|
+
undefined
|
|
384
|
+
this.span = tracer.startSpan(
|
|
385
|
+
options.name,
|
|
386
|
+
{
|
|
387
|
+
startTime: nanosToHrTime(options.startTime),
|
|
388
|
+
links: options.links.length > 0
|
|
389
|
+
? options.links.map((link) => ({
|
|
390
|
+
context: makeSpanContext(link.span),
|
|
391
|
+
attributes: recordToAttributes(link.attributes)
|
|
392
|
+
}))
|
|
393
|
+
: undefined as any,
|
|
394
|
+
kind: kindMap[this.kind]
|
|
395
|
+
},
|
|
396
|
+
this.parent ? populateContext(active, this.parent, options.annotations) : Otel.trace.deleteSpan(active)
|
|
397
|
+
)
|
|
398
|
+
const spanContext = this.span.spanContext()
|
|
399
|
+
this.spanId = spanContext.spanId
|
|
400
|
+
this.traceId = spanContext.traceId
|
|
401
|
+
this.status = {
|
|
402
|
+
_tag: "Started",
|
|
403
|
+
startTime: options.startTime
|
|
404
|
+
}
|
|
405
|
+
this.sampled = isSampled(spanContext.traceFlags)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
attribute(key: string, value: unknown) {
|
|
409
|
+
this.span.setAttribute(key, unknownToAttributeValue(value))
|
|
410
|
+
this.attributes.set(key, value)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
addLinks(links: ReadonlyArray<Tracer.SpanLink>): void {
|
|
414
|
+
// oxlint-disable-next-line no-restricted-syntax
|
|
415
|
+
this.links.push(...links)
|
|
416
|
+
this.span.addLinks(links.map((link) => ({
|
|
417
|
+
context: makeSpanContext(link.span),
|
|
418
|
+
attributes: recordToAttributes(link.attributes)
|
|
419
|
+
})))
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
end(endTime: bigint, exit: Exit.Exit<unknown, unknown>) {
|
|
423
|
+
const hrTime = nanosToHrTime(endTime)
|
|
424
|
+
this.status = {
|
|
425
|
+
_tag: "Ended",
|
|
426
|
+
endTime,
|
|
427
|
+
exit,
|
|
428
|
+
startTime: this.status.startTime
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (exit._tag === "Success") {
|
|
432
|
+
this.span.setStatus({ code: Otel.SpanStatusCode.OK })
|
|
433
|
+
} else {
|
|
434
|
+
if (Cause.hasInterruptsOnly(exit.cause)) {
|
|
435
|
+
this.span.setStatus({
|
|
436
|
+
code: Otel.SpanStatusCode.OK,
|
|
437
|
+
message: Cause.pretty(exit.cause)
|
|
438
|
+
})
|
|
439
|
+
this.span.setAttribute("span.label", "⚠︎ Interrupted")
|
|
440
|
+
this.span.setAttribute("status.interrupted", true)
|
|
441
|
+
} else {
|
|
442
|
+
const errors = Cause.prettyErrors(exit.cause)
|
|
443
|
+
if (errors.length > 0) {
|
|
444
|
+
for (const error of errors) {
|
|
445
|
+
this.span.recordException(error, hrTime)
|
|
446
|
+
}
|
|
447
|
+
this.span.setStatus({
|
|
448
|
+
code: Otel.SpanStatusCode.ERROR,
|
|
449
|
+
message: errors[0].message
|
|
450
|
+
})
|
|
451
|
+
} else {
|
|
452
|
+
// empty cause means no error
|
|
453
|
+
this.span.setStatus({ code: Otel.SpanStatusCode.OK })
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
this.span.end(hrTime)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
event(name: string, startTime: bigint, attributes?: Record<string, unknown>) {
|
|
461
|
+
this.span.addEvent(
|
|
462
|
+
name,
|
|
463
|
+
attributes ? recordToAttributes(attributes) : undefined,
|
|
464
|
+
nanosToHrTime(startTime)
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const isSampled = (traceFlags: Otel.TraceFlags): boolean =>
|
|
470
|
+
(traceFlags & Otel.TraceFlags.SAMPLED) === Otel.TraceFlags.SAMPLED
|
|
471
|
+
|
|
472
|
+
const nanosToHrTime = (timestamp: bigint): Otel.HrTime => {
|
|
473
|
+
return [Number(timestamp / bigint1e9), Number(timestamp % bigint1e9)]
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const getOtelParent = (
|
|
477
|
+
tracer: Otel.TraceAPI,
|
|
478
|
+
context: Otel.Context,
|
|
479
|
+
annotations: ServiceMap.ServiceMap<never>
|
|
480
|
+
): Tracer.AnySpan | undefined => {
|
|
481
|
+
const active = tracer.getSpan(context)
|
|
482
|
+
const otelParent = active ? active.spanContext() : undefined
|
|
483
|
+
return otelParent
|
|
484
|
+
? Tracer.externalSpan({
|
|
485
|
+
spanId: otelParent.spanId,
|
|
486
|
+
traceId: otelParent.traceId,
|
|
487
|
+
sampled: (otelParent.traceFlags & 1) === 1,
|
|
488
|
+
annotations
|
|
489
|
+
})
|
|
490
|
+
: undefined
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const makeSpanContext = (
|
|
494
|
+
span: Tracer.AnySpan,
|
|
495
|
+
annotations?: ServiceMap.ServiceMap<never>
|
|
496
|
+
): Otel.SpanContext => {
|
|
497
|
+
const traceFlags = makeTraceFlags(span, annotations)
|
|
498
|
+
const traceState = makeTraceState(span, annotations)!
|
|
499
|
+
return ({
|
|
500
|
+
spanId: span.spanId,
|
|
501
|
+
traceId: span.traceId,
|
|
502
|
+
isRemote: span._tag === "ExternalSpan",
|
|
503
|
+
traceFlags,
|
|
504
|
+
traceState
|
|
505
|
+
})
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const makeTraceFlags = (
|
|
509
|
+
span: Tracer.AnySpan,
|
|
510
|
+
annotations: ServiceMap.ServiceMap<never> | undefined
|
|
511
|
+
): Otel.TraceFlags => {
|
|
512
|
+
let traceFlags: Otel.TraceFlags | undefined
|
|
513
|
+
if (Predicate.isNotUndefined(annotations)) {
|
|
514
|
+
traceFlags = extractTraceService(span, annotations, OtelTraceFlags)
|
|
515
|
+
if (Predicate.isUndefined(traceFlags)) {
|
|
516
|
+
traceFlags = ServiceMap.getOrUndefined(span.annotations, OtelTraceFlags)
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return traceFlags ?? Otel.TraceFlags.SAMPLED
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const makeTraceState = (
|
|
523
|
+
span: Tracer.AnySpan,
|
|
524
|
+
annotations: ServiceMap.ServiceMap<never> | undefined
|
|
525
|
+
): Otel.TraceState | undefined => {
|
|
526
|
+
let traceState: Otel.TraceState | undefined
|
|
527
|
+
if (Predicate.isNotUndefined(annotations)) {
|
|
528
|
+
traceState = extractTraceService(span, annotations, OtelTraceState)
|
|
529
|
+
if (Predicate.isUndefined(traceState)) {
|
|
530
|
+
traceState = ServiceMap.getOrUndefined(span.annotations, OtelTraceState)
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return traceState
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const extractTraceService = <I, S>(
|
|
537
|
+
parent: Tracer.AnySpan,
|
|
538
|
+
annotations: ServiceMap.ServiceMap<never>,
|
|
539
|
+
service: ServiceMap.Service<I, S>
|
|
540
|
+
) => {
|
|
541
|
+
const instance = ServiceMap.getOrUndefined(annotations, service)
|
|
542
|
+
if (Predicate.isNotUndefined(instance)) {
|
|
543
|
+
return instance
|
|
544
|
+
}
|
|
545
|
+
return ServiceMap.getOrUndefined(parent.annotations, service)
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const populateContext = (
|
|
549
|
+
context: Otel.Context,
|
|
550
|
+
span: Tracer.AnySpan,
|
|
551
|
+
annotations?: ServiceMap.ServiceMap<never> | undefined
|
|
552
|
+
): Otel.Context =>
|
|
553
|
+
span instanceof OtelSpan ?
|
|
554
|
+
Otel.trace.setSpan(context, span.span) :
|
|
555
|
+
Otel.trace.setSpanContext(context, makeSpanContext(span, annotations))
|