@effect/opentelemetry 0.61.0 → 4.0.0-beta.1

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 (183) hide show
  1. package/LICENSE +1 -1
  2. package/dist/{dts/Logger.d.ts → Logger.d.ts} +19 -13
  3. package/dist/Logger.d.ts.map +1 -0
  4. package/dist/Logger.js +76 -0
  5. package/dist/Logger.js.map +1 -0
  6. package/dist/Metrics.d.ts +76 -0
  7. package/dist/Metrics.d.ts.map +1 -0
  8. package/dist/Metrics.js +59 -0
  9. package/dist/Metrics.js.map +1 -0
  10. package/dist/{dts/NodeSdk.d.ts → NodeSdk.d.ts} +12 -9
  11. package/dist/NodeSdk.d.ts.map +1 -0
  12. package/dist/{esm/NodeSdk.js → NodeSdk.js} +23 -14
  13. package/dist/NodeSdk.js.map +1 -0
  14. package/dist/{dts/Resource.d.ts → Resource.d.ts} +10 -13
  15. package/dist/Resource.d.ts.map +1 -0
  16. package/dist/{esm/Resource.js → Resource.js} +12 -13
  17. package/dist/Resource.js.map +1 -0
  18. package/dist/Tracer.d.ts +129 -0
  19. package/dist/Tracer.d.ts.map +1 -0
  20. package/dist/Tracer.js +391 -0
  21. package/dist/Tracer.js.map +1 -0
  22. package/dist/{dts/WebSdk.d.ts → WebSdk.d.ts} +12 -9
  23. package/dist/WebSdk.d.ts.map +1 -0
  24. package/dist/WebSdk.js +41 -0
  25. package/dist/WebSdk.js.map +1 -0
  26. package/dist/index.d.ts +28 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +29 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/internal/attributes.d.ts +2 -0
  31. package/dist/internal/attributes.d.ts.map +1 -0
  32. package/dist/internal/attributes.js +19 -0
  33. package/dist/internal/attributes.js.map +1 -0
  34. package/dist/{dts/internal → internal}/metrics.d.ts.map +1 -1
  35. package/dist/internal/metrics.js +406 -0
  36. package/dist/internal/metrics.js.map +1 -0
  37. package/dist/internal/utilities.d.ts +2 -0
  38. package/dist/internal/utilities.d.ts.map +1 -0
  39. package/dist/internal/utilities.js +3 -0
  40. package/dist/internal/utilities.js.map +1 -0
  41. package/package.json +86 -119
  42. package/src/Logger.ts +52 -55
  43. package/src/Metrics.ts +92 -18
  44. package/src/NodeSdk.ts +67 -64
  45. package/src/Resource.ts +16 -24
  46. package/src/Tracer.ts +469 -78
  47. package/src/WebSdk.ts +59 -51
  48. package/src/index.ts +7 -36
  49. package/src/internal/attributes.ts +21 -0
  50. package/src/internal/metrics.ts +381 -250
  51. package/src/internal/utilities.ts +5 -0
  52. package/Logger/package.json +0 -6
  53. package/Metrics/package.json +0 -6
  54. package/NodeSdk/package.json +0 -6
  55. package/Otlp/package.json +0 -6
  56. package/OtlpLogger/package.json +0 -6
  57. package/OtlpMetrics/package.json +0 -6
  58. package/OtlpResource/package.json +0 -6
  59. package/OtlpSerialization/package.json +0 -6
  60. package/OtlpTracer/package.json +0 -6
  61. package/Resource/package.json +0 -6
  62. package/Tracer/package.json +0 -6
  63. package/WebSdk/package.json +0 -6
  64. package/dist/cjs/Logger.js +0 -85
  65. package/dist/cjs/Logger.js.map +0 -1
  66. package/dist/cjs/Metrics.js +0 -24
  67. package/dist/cjs/Metrics.js.map +0 -1
  68. package/dist/cjs/NodeSdk.js +0 -53
  69. package/dist/cjs/NodeSdk.js.map +0 -1
  70. package/dist/cjs/Otlp.js +0 -64
  71. package/dist/cjs/Otlp.js.map +0 -1
  72. package/dist/cjs/OtlpLogger.js +0 -163
  73. package/dist/cjs/OtlpLogger.js.map +0 -1
  74. package/dist/cjs/OtlpMetrics.js +0 -357
  75. package/dist/cjs/OtlpMetrics.js.map +0 -1
  76. package/dist/cjs/OtlpResource.js +0 -136
  77. package/dist/cjs/OtlpResource.js.map +0 -1
  78. package/dist/cjs/OtlpSerialization.js +0 -55
  79. package/dist/cjs/OtlpSerialization.js.map +0 -1
  80. package/dist/cjs/OtlpTracer.js +0 -231
  81. package/dist/cjs/OtlpTracer.js.map +0 -1
  82. package/dist/cjs/Resource.js +0 -75
  83. package/dist/cjs/Resource.js.map +0 -1
  84. package/dist/cjs/Tracer.js +0 -87
  85. package/dist/cjs/Tracer.js.map +0 -1
  86. package/dist/cjs/WebSdk.js +0 -42
  87. package/dist/cjs/WebSdk.js.map +0 -1
  88. package/dist/cjs/index.js +0 -32
  89. package/dist/cjs/index.js.map +0 -1
  90. package/dist/cjs/internal/metrics.js +0 -288
  91. package/dist/cjs/internal/metrics.js.map +0 -1
  92. package/dist/cjs/internal/otlpExporter.js +0 -83
  93. package/dist/cjs/internal/otlpExporter.js.map +0 -1
  94. package/dist/cjs/internal/otlpProtobuf.js +0 -430
  95. package/dist/cjs/internal/otlpProtobuf.js.map +0 -1
  96. package/dist/cjs/internal/protobuf.js +0 -183
  97. package/dist/cjs/internal/protobuf.js.map +0 -1
  98. package/dist/cjs/internal/tracer.js +0 -306
  99. package/dist/cjs/internal/tracer.js.map +0 -1
  100. package/dist/cjs/internal/utils.js +0 -34
  101. package/dist/cjs/internal/utils.js.map +0 -1
  102. package/dist/dts/Logger.d.ts.map +0 -1
  103. package/dist/dts/Metrics.d.ts +0 -29
  104. package/dist/dts/Metrics.d.ts.map +0 -1
  105. package/dist/dts/NodeSdk.d.ts.map +0 -1
  106. package/dist/dts/Otlp.d.ts +0 -80
  107. package/dist/dts/Otlp.d.ts.map +0 -1
  108. package/dist/dts/OtlpLogger.d.ts +0 -47
  109. package/dist/dts/OtlpLogger.d.ts.map +0 -1
  110. package/dist/dts/OtlpMetrics.d.ts +0 -41
  111. package/dist/dts/OtlpMetrics.d.ts.map +0 -1
  112. package/dist/dts/OtlpResource.d.ts +0 -104
  113. package/dist/dts/OtlpResource.d.ts.map +0 -1
  114. package/dist/dts/OtlpSerialization.d.ts +0 -53
  115. package/dist/dts/OtlpSerialization.d.ts.map +0 -1
  116. package/dist/dts/OtlpTracer.d.ts +0 -50
  117. package/dist/dts/OtlpTracer.d.ts.map +0 -1
  118. package/dist/dts/Resource.d.ts.map +0 -1
  119. package/dist/dts/Tracer.d.ts +0 -143
  120. package/dist/dts/Tracer.d.ts.map +0 -1
  121. package/dist/dts/WebSdk.d.ts.map +0 -1
  122. package/dist/dts/index.d.ts +0 -54
  123. package/dist/dts/index.d.ts.map +0 -1
  124. package/dist/dts/internal/otlpExporter.d.ts +0 -2
  125. package/dist/dts/internal/otlpExporter.d.ts.map +0 -1
  126. package/dist/dts/internal/otlpProtobuf.d.ts +0 -501
  127. package/dist/dts/internal/otlpProtobuf.d.ts.map +0 -1
  128. package/dist/dts/internal/protobuf.d.ts +0 -100
  129. package/dist/dts/internal/protobuf.d.ts.map +0 -1
  130. package/dist/dts/internal/tracer.d.ts +0 -2
  131. package/dist/dts/internal/tracer.d.ts.map +0 -1
  132. package/dist/dts/internal/utils.d.ts +0 -2
  133. package/dist/dts/internal/utils.d.ts.map +0 -1
  134. package/dist/esm/Logger.js +0 -75
  135. package/dist/esm/Logger.js.map +0 -1
  136. package/dist/esm/Metrics.js +0 -17
  137. package/dist/esm/Metrics.js.map +0 -1
  138. package/dist/esm/NodeSdk.js.map +0 -1
  139. package/dist/esm/Otlp.js +0 -56
  140. package/dist/esm/Otlp.js.map +0 -1
  141. package/dist/esm/OtlpLogger.js +0 -155
  142. package/dist/esm/OtlpLogger.js.map +0 -1
  143. package/dist/esm/OtlpMetrics.js +0 -349
  144. package/dist/esm/OtlpMetrics.js.map +0 -1
  145. package/dist/esm/OtlpResource.js +0 -124
  146. package/dist/esm/OtlpResource.js.map +0 -1
  147. package/dist/esm/OtlpSerialization.js +0 -46
  148. package/dist/esm/OtlpSerialization.js.map +0 -1
  149. package/dist/esm/OtlpTracer.js +0 -223
  150. package/dist/esm/OtlpTracer.js.map +0 -1
  151. package/dist/esm/Resource.js.map +0 -1
  152. package/dist/esm/Tracer.js +0 -80
  153. package/dist/esm/Tracer.js.map +0 -1
  154. package/dist/esm/WebSdk.js +0 -33
  155. package/dist/esm/WebSdk.js.map +0 -1
  156. package/dist/esm/index.js +0 -54
  157. package/dist/esm/index.js.map +0 -1
  158. package/dist/esm/internal/metrics.js +0 -278
  159. package/dist/esm/internal/metrics.js.map +0 -1
  160. package/dist/esm/internal/otlpExporter.js +0 -76
  161. package/dist/esm/internal/otlpExporter.js.map +0 -1
  162. package/dist/esm/internal/otlpProtobuf.js +0 -396
  163. package/dist/esm/internal/otlpProtobuf.js.map +0 -1
  164. package/dist/esm/internal/protobuf.js +0 -155
  165. package/dist/esm/internal/protobuf.js.map +0 -1
  166. package/dist/esm/internal/tracer.js +0 -297
  167. package/dist/esm/internal/tracer.js.map +0 -1
  168. package/dist/esm/internal/utils.js +0 -23
  169. package/dist/esm/internal/utils.js.map +0 -1
  170. package/dist/esm/package.json +0 -4
  171. package/index/package.json +0 -6
  172. package/src/Otlp.ts +0 -118
  173. package/src/OtlpLogger.ts +0 -263
  174. package/src/OtlpMetrics.ts +0 -575
  175. package/src/OtlpResource.ts +0 -232
  176. package/src/OtlpSerialization.ts +0 -64
  177. package/src/OtlpTracer.ts +0 -352
  178. package/src/internal/otlpExporter.ts +0 -126
  179. package/src/internal/otlpProtobuf.ts +0 -729
  180. package/src/internal/protobuf.ts +0 -219
  181. package/src/internal/tracer.ts +0 -448
  182. package/src/internal/utils.ts +0 -31
  183. /package/dist/{dts/internal → internal}/metrics.d.ts +0 -0
@@ -1,219 +0,0 @@
1
- /**
2
- * Low-level protobuf wire format encoding utilities.
3
- *
4
- * Protobuf wire types:
5
- * - 0: Varint (int32, int64, uint32, uint64, sint32, sint64, bool, enum)
6
- * - 1: 64-bit (fixed64, sfixed64, double)
7
- * - 2: Length-delimited (string, bytes, embedded messages, packed repeated fields)
8
- * - 5: 32-bit (fixed32, sfixed32, float)
9
- *
10
- * @internal
11
- */
12
-
13
- const enum WireType {
14
- Varint = 0,
15
- Fixed64 = 1,
16
- LengthDelimited = 2,
17
- Fixed32 = 5
18
- }
19
-
20
- /**
21
- * Encodes a field tag (field number + wire type)
22
- */
23
- const encodeTag = (fieldNumber: number, wireType: WireType): number => (fieldNumber << 3) | wireType
24
-
25
- /**
26
- * Encodes a varint (variable-length integer)
27
- */
28
- export const encodeVarint = (value: number | bigint): Uint8Array => {
29
- const bytes: Array<number> = []
30
- let n = typeof value === "bigint" ? value : BigInt(value)
31
- while (n > 0x7fn) {
32
- bytes.push(Number(n & 0x7fn) | 0x80)
33
- n >>= 7n
34
- }
35
- bytes.push(Number(n))
36
- return new Uint8Array(bytes)
37
- }
38
-
39
- /**
40
- * Encodes a signed varint using ZigZag encoding
41
- */
42
- export const encodeSint = (value: number | bigint): Uint8Array => {
43
- const n = typeof value === "bigint" ? value : BigInt(value)
44
- const zigzag = (n << 1n) ^ (n >> 63n)
45
- return encodeVarint(zigzag)
46
- }
47
-
48
- /**
49
- * Encodes a 64-bit fixed value (little-endian)
50
- */
51
- export const encodeFixed64 = (value: bigint): Uint8Array => {
52
- const bytes = new Uint8Array(8)
53
- const view = new DataView(bytes.buffer)
54
- view.setBigUint64(0, value, true)
55
- return bytes
56
- }
57
-
58
- /**
59
- * Encodes a 32-bit fixed value (little-endian)
60
- */
61
- export const encodeFixed32 = (value: number): Uint8Array => {
62
- const bytes = new Uint8Array(4)
63
- const view = new DataView(bytes.buffer)
64
- view.setUint32(0, value, true)
65
- return bytes
66
- }
67
-
68
- /**
69
- * Encodes a double (64-bit float, little-endian)
70
- */
71
- export const encodeDouble = (value: number): Uint8Array => {
72
- const bytes = new Uint8Array(8)
73
- const view = new DataView(bytes.buffer)
74
- view.setFloat64(0, value, true)
75
- return bytes
76
- }
77
-
78
- /**
79
- * Encodes a string to UTF-8 bytes
80
- */
81
- export const encodeString = (value: string): Uint8Array => new TextEncoder().encode(value)
82
-
83
- /**
84
- * Encodes bytes as a hex string to Uint8Array
85
- */
86
- export const encodeHexBytes = (hex: string): Uint8Array => {
87
- const bytes = new Uint8Array(hex.length / 2)
88
- for (let i = 0; i < hex.length; i += 2) {
89
- bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16)
90
- }
91
- return bytes
92
- }
93
-
94
- /**
95
- * Concatenates multiple Uint8Arrays
96
- */
97
- export const concat = (...arrays: Array<Uint8Array>): Uint8Array => {
98
- const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0)
99
- const result = new Uint8Array(totalLength)
100
- let offset = 0
101
- for (const arr of arrays) {
102
- result.set(arr, offset)
103
- offset += arr.length
104
- }
105
- return result
106
- }
107
-
108
- // Field encoders
109
-
110
- /**
111
- * Encodes a varint field
112
- */
113
- export const varintField = (fieldNumber: number, value: number | bigint): Uint8Array =>
114
- concat(
115
- encodeVarint(encodeTag(fieldNumber, WireType.Varint)),
116
- encodeVarint(value)
117
- )
118
-
119
- /**
120
- * Encodes a sint field (ZigZag encoded)
121
- */
122
- export const sintField = (fieldNumber: number, value: number | bigint): Uint8Array =>
123
- concat(
124
- encodeVarint(encodeTag(fieldNumber, WireType.Varint)),
125
- encodeSint(value)
126
- )
127
-
128
- /**
129
- * Encodes a bool field
130
- */
131
- export const boolField = (fieldNumber: number, value: boolean): Uint8Array => varintField(fieldNumber, value ? 1 : 0)
132
-
133
- /**
134
- * Encodes a fixed64 field
135
- */
136
- export const fixed64Field = (fieldNumber: number, value: bigint): Uint8Array =>
137
- concat(
138
- encodeVarint(encodeTag(fieldNumber, WireType.Fixed64)),
139
- encodeFixed64(value)
140
- )
141
-
142
- /**
143
- * Encodes a fixed32 field
144
- */
145
- export const fixed32Field = (fieldNumber: number, value: number): Uint8Array =>
146
- concat(
147
- encodeVarint(encodeTag(fieldNumber, WireType.Fixed32)),
148
- encodeFixed32(value)
149
- )
150
-
151
- /**
152
- * Encodes a double field
153
- */
154
- export const doubleField = (fieldNumber: number, value: number): Uint8Array =>
155
- concat(
156
- encodeVarint(encodeTag(fieldNumber, WireType.Fixed64)),
157
- encodeDouble(value)
158
- )
159
-
160
- /**
161
- * Encodes a length-delimited field (bytes, string, embedded message)
162
- */
163
- export const lengthDelimitedField = (fieldNumber: number, value: Uint8Array): Uint8Array =>
164
- concat(
165
- encodeVarint(encodeTag(fieldNumber, WireType.LengthDelimited)),
166
- encodeVarint(value.length),
167
- value
168
- )
169
-
170
- /**
171
- * Encodes a string field
172
- */
173
- export const stringField = (fieldNumber: number, value: string): Uint8Array =>
174
- lengthDelimitedField(fieldNumber, encodeString(value))
175
-
176
- /**
177
- * Encodes a bytes field from hex string
178
- */
179
- export const bytesFieldFromHex = (fieldNumber: number, hex: string): Uint8Array =>
180
- lengthDelimitedField(fieldNumber, encodeHexBytes(hex))
181
-
182
- /**
183
- * Encodes an embedded message field
184
- */
185
- export const messageField = (fieldNumber: number, message: Uint8Array): Uint8Array =>
186
- lengthDelimitedField(fieldNumber, message)
187
-
188
- /**
189
- * Encodes repeated fields
190
- */
191
- export const repeatedField = <T>(
192
- fieldNumber: number,
193
- values: ReadonlyArray<T>,
194
- encode: (value: T) => Uint8Array
195
- ): Uint8Array => concat(...values.map((v) => messageField(fieldNumber, encode(v))))
196
-
197
- /**
198
- * Encodes repeated varint fields (not packed)
199
- */
200
- export const repeatedVarintField = (
201
- fieldNumber: number,
202
- values: ReadonlyArray<number | bigint>
203
- ): Uint8Array => concat(...values.map((v) => varintField(fieldNumber, v)))
204
-
205
- /**
206
- * Helper to conditionally encode an optional field
207
- */
208
- export const optionalField = <T>(
209
- value: T | undefined,
210
- encode: (v: T) => Uint8Array
211
- ): Uint8Array => value !== undefined ? encode(value) : new Uint8Array(0)
212
-
213
- /**
214
- * Helper to conditionally encode a string field if non-empty
215
- */
216
- export const optionalStringField = (
217
- fieldNumber: number,
218
- value: string | undefined
219
- ): Uint8Array => value !== undefined && value !== "" ? stringField(fieldNumber, value) : new Uint8Array(0)
@@ -1,448 +0,0 @@
1
- import * as OtelApi from "@opentelemetry/api"
2
- import * as OtelSemConv from "@opentelemetry/semantic-conventions"
3
- import * as Cause from "effect/Cause"
4
- import type * as Clock from "effect/Clock"
5
- import * as Context from "effect/Context"
6
- import * as Effect from "effect/Effect"
7
- import * as Exit from "effect/Exit"
8
- import { constTrue, dual } from "effect/Function"
9
- import * as Layer from "effect/Layer"
10
- import * as Option from "effect/Option"
11
- import * as EffectTracer from "effect/Tracer"
12
- import { Resource } from "../Resource.js"
13
- import type { OtelTraceFlags, OtelTracer, OtelTracerProvider, OtelTraceState } from "../Tracer.js"
14
- import { nanosToHrTime, recordToAttributes, unknownToAttributeValue } from "./utils.js"
15
-
16
- const OtelSpanTypeId = Symbol.for("@effect/opentelemetry/Tracer/OtelSpan")
17
-
18
- const kindMap = {
19
- "internal": OtelApi.SpanKind.INTERNAL,
20
- "client": OtelApi.SpanKind.CLIENT,
21
- "server": OtelApi.SpanKind.SERVER,
22
- "producer": OtelApi.SpanKind.PRODUCER,
23
- "consumer": OtelApi.SpanKind.CONSUMER
24
- }
25
-
26
- const getOtelParent = (tracer: OtelApi.TraceAPI, otelContext: OtelApi.Context, context: Context.Context<never>) => {
27
- const active = tracer.getSpan(otelContext)
28
- const otelParent = active ? active.spanContext() : undefined
29
- return otelParent
30
- ? Option.some(
31
- EffectTracer.externalSpan({
32
- spanId: otelParent.spanId,
33
- traceId: otelParent.traceId,
34
- sampled: (otelParent.traceFlags & 1) === 1,
35
- context
36
- })
37
- )
38
- : Option.none()
39
- }
40
-
41
- /** @internal */
42
- export class OtelSpan implements EffectTracer.Span {
43
- readonly [OtelSpanTypeId]: typeof OtelSpanTypeId
44
- readonly _tag = "Span"
45
-
46
- readonly span: OtelApi.Span
47
- readonly spanId: string
48
- readonly traceId: string
49
- readonly attributes = new Map<string, unknown>()
50
- readonly sampled: boolean
51
- readonly parent: Option.Option<EffectTracer.AnySpan>
52
- status: EffectTracer.SpanStatus
53
-
54
- constructor(
55
- contextApi: OtelApi.ContextAPI,
56
- traceApi: OtelApi.TraceAPI,
57
- tracer: OtelApi.Tracer,
58
- readonly name: string,
59
- effectParent: Option.Option<EffectTracer.AnySpan>,
60
- readonly context: Context.Context<never>,
61
- readonly links: Array<EffectTracer.SpanLink>,
62
- startTime: bigint,
63
- readonly kind: EffectTracer.SpanKind,
64
- options?: EffectTracer.SpanOptions
65
- ) {
66
- this[OtelSpanTypeId] = OtelSpanTypeId
67
- const active = contextApi.active()
68
- this.parent = effectParent._tag === "Some"
69
- ? effectParent
70
- : (options?.root !== true)
71
- ? getOtelParent(traceApi, active, context)
72
- : Option.none()
73
- this.span = tracer.startSpan(
74
- name,
75
- {
76
- startTime: nanosToHrTime(startTime),
77
- links: links.length > 0
78
- ? links.map((link) => ({
79
- context: makeSpanContext(link.span),
80
- attributes: recordToAttributes(link.attributes)
81
- }))
82
- : undefined as any,
83
- kind: kindMap[this.kind]
84
- },
85
- this.parent._tag === "Some"
86
- ? populateContext(active, this.parent.value, context)
87
- : OtelApi.trace.deleteSpan(active)
88
- )
89
- const spanContext = this.span.spanContext()
90
- this.spanId = spanContext.spanId
91
- this.traceId = spanContext.traceId
92
- this.status = {
93
- _tag: "Started",
94
- startTime
95
- }
96
- this.sampled = (spanContext.traceFlags & OtelApi.TraceFlags.SAMPLED) === OtelApi.TraceFlags.SAMPLED
97
- }
98
-
99
- attribute(key: string, value: unknown) {
100
- this.span.setAttribute(key, unknownToAttributeValue(value))
101
- this.attributes.set(key, value)
102
- }
103
-
104
- addLinks(links: ReadonlyArray<EffectTracer.SpanLink>): void {
105
- // eslint-disable-next-line no-restricted-syntax
106
- this.links.push(...links)
107
- this.span.addLinks(links.map((link) => ({
108
- context: makeSpanContext(link.span),
109
- attributes: recordToAttributes(link.attributes)
110
- })))
111
- }
112
-
113
- end(endTime: bigint, exit: Exit.Exit<unknown, unknown>) {
114
- const hrTime = nanosToHrTime(endTime)
115
- this.status = {
116
- _tag: "Ended",
117
- endTime,
118
- exit,
119
- startTime: this.status.startTime
120
- }
121
-
122
- if (exit._tag === "Success") {
123
- this.span.setStatus({ code: OtelApi.SpanStatusCode.OK })
124
- } else {
125
- if (Cause.isInterruptedOnly(exit.cause)) {
126
- this.span.setStatus({
127
- code: OtelApi.SpanStatusCode.OK,
128
- message: Cause.pretty(exit.cause)
129
- })
130
- this.span.setAttribute("span.label", "⚠︎ Interrupted")
131
- this.span.setAttribute("status.interrupted", true)
132
- } else {
133
- const firstError = Cause.prettyErrors(exit.cause)[0]
134
- if (firstError) {
135
- firstError.stack = Cause.pretty(exit.cause, { renderErrorCause: true })
136
- this.span.recordException(firstError, hrTime)
137
- this.span.setStatus({
138
- code: OtelApi.SpanStatusCode.ERROR,
139
- message: firstError.message
140
- })
141
- } else {
142
- // empty cause means no error
143
- this.span.setStatus({ code: OtelApi.SpanStatusCode.OK })
144
- }
145
- }
146
- }
147
- this.span.end(hrTime)
148
- }
149
-
150
- event(name: string, startTime: bigint, attributes?: Record<string, unknown>) {
151
- this.span.addEvent(
152
- name,
153
- attributes ? recordToAttributes(attributes) : undefined,
154
- nanosToHrTime(startTime)
155
- )
156
- }
157
- }
158
-
159
- /** @internal */
160
- export const TracerProvider = Context.GenericTag<OtelTracerProvider, OtelApi.TracerProvider>(
161
- "@effect/opentelemetry/Tracer/OtelTracerProvider"
162
- )
163
-
164
- /** @internal */
165
- export const Tracer = Context.GenericTag<OtelTracer, OtelApi.Tracer>("@effect/opentelemetry/Tracer/OtelTracer")
166
-
167
- /** @internal */
168
- export const make = Effect.map(Tracer, (tracer) =>
169
- EffectTracer.make({
170
- span(name, parent, context, links, startTime, kind, options) {
171
- return new OtelSpan(
172
- OtelApi.context,
173
- OtelApi.trace,
174
- tracer,
175
- name,
176
- parent,
177
- context,
178
- links.slice(),
179
- startTime,
180
- kind,
181
- options
182
- )
183
- },
184
- context(execution, fiber) {
185
- const currentSpan = fiber.currentSpan
186
-
187
- if (currentSpan === undefined) {
188
- return execution()
189
- }
190
-
191
- return OtelApi.context.with(
192
- populateContext(OtelApi.context.active(), currentSpan),
193
- execution
194
- )
195
- }
196
- }))
197
-
198
- /** @internal */
199
- export const traceFlagsTag = Context.GenericTag<OtelTraceFlags, OtelApi.TraceFlags>(
200
- "@effect/opentelemetry/Tracer/OtelTraceFlags"
201
- )
202
-
203
- /** @internal */
204
- export const traceStateTag = Context.GenericTag<OtelTraceState, OtelApi.TraceState>(
205
- "@effect/opentelemetry/Tracer/OtelTraceState"
206
- )
207
-
208
- /** @internal */
209
- export const makeExternalSpan = (options: {
210
- readonly traceId: string
211
- readonly spanId: string
212
- readonly traceFlags?: number | undefined
213
- readonly traceState?: string | OtelApi.TraceState | undefined
214
- }): EffectTracer.ExternalSpan => {
215
- let context = Context.empty()
216
-
217
- if (options.traceFlags !== undefined) {
218
- context = Context.add(context, traceFlagsTag, options.traceFlags)
219
- }
220
-
221
- if (typeof options.traceState === "string") {
222
- context = Option.match(createTraceState(options.traceState), {
223
- onNone: () => context,
224
- onSome: (traceState) => Context.add(context, traceStateTag, traceState)
225
- })
226
- } else if (options.traceState) {
227
- context = Context.add(context, traceStateTag, options.traceState)
228
- }
229
-
230
- return {
231
- _tag: "ExternalSpan",
232
- traceId: options.traceId,
233
- spanId: options.spanId,
234
- sampled: options.traceFlags !== undefined
235
- ? (options.traceFlags & OtelApi.TraceFlags.SAMPLED) === OtelApi.TraceFlags.SAMPLED
236
- : true,
237
- context
238
- }
239
- }
240
-
241
- const makeOtelSpan = (span: EffectTracer.Span, clock: Clock.Clock): OtelApi.Span => {
242
- const spanContext: OtelApi.SpanContext = {
243
- traceId: span.traceId,
244
- spanId: span.spanId,
245
- traceFlags: span.sampled ? OtelApi.TraceFlags.SAMPLED : OtelApi.TraceFlags.NONE,
246
- isRemote: false
247
- }
248
-
249
- let exit = Exit.void
250
-
251
- const self: OtelApi.Span = {
252
- spanContext: () => spanContext,
253
- setAttribute(key, value) {
254
- span.attribute(key, value)
255
- return self
256
- },
257
- setAttributes(attributes) {
258
- for (const [key, value] of Object.entries(attributes)) {
259
- span.attribute(key, value)
260
- }
261
- return self
262
- },
263
- addEvent(name) {
264
- let attributes: OtelApi.Attributes | undefined = undefined
265
- let startTime: OtelApi.TimeInput | undefined = undefined
266
- if (arguments.length === 3) {
267
- attributes = arguments[1]
268
- startTime = arguments[2]
269
- } else if (arguments.length === 2) {
270
- const arg1 = arguments[1]
271
- if (isTimeInput(arg1)) {
272
- startTime = arg1
273
- } else {
274
- attributes = arg1
275
- }
276
- }
277
- span.event(name, convertOtelTimeInput(startTime, clock), attributes)
278
- return self
279
- },
280
- addLink(link) {
281
- span.addLinks([{
282
- _tag: "SpanLink",
283
- span: makeExternalSpan(link.context),
284
- attributes: link.attributes ?? {}
285
- }])
286
- return self
287
- },
288
- addLinks(links) {
289
- span.addLinks(links.map((link) => ({
290
- _tag: "SpanLink",
291
- span: makeExternalSpan(link.context),
292
- attributes: link.attributes ?? {}
293
- })))
294
- return self
295
- },
296
- setStatus(status) {
297
- exit = OtelApi.SpanStatusCode.ERROR
298
- ? Exit.die(status.message ?? "Unknown error")
299
- : Exit.void
300
- return self
301
- },
302
- updateName: () => self,
303
- end(endTime) {
304
- const time = convertOtelTimeInput(endTime, clock)
305
- span.end(time, exit)
306
- return self
307
- },
308
- isRecording: constTrue,
309
- recordException(exception, timeInput) {
310
- const time = convertOtelTimeInput(timeInput, clock)
311
- const cause = Cause.fail(exception)
312
- const error = Cause.prettyErrors(cause)[0]
313
- span.event(error.message, time, {
314
- "exception.type": error.name,
315
- "exception.message": error.message,
316
- "exception.stacktrace": error.stack ?? ""
317
- })
318
- }
319
- }
320
- return self
321
- }
322
-
323
- const bigint1e6 = BigInt(1_000_000)
324
- const bigint1e9 = BigInt(1_000_000_000)
325
-
326
- /** Distinguishes TimeInput (number | Date | [number, number]) from Attributes (plain object) */
327
- const isTimeInput = (u: unknown): u is OtelApi.TimeInput =>
328
- typeof u === "number" ||
329
- u instanceof Date ||
330
- (Array.isArray(u) && u.length === 2 && typeof u[0] === "number" && typeof u[1] === "number")
331
-
332
- const convertOtelTimeInput = (input: OtelApi.TimeInput | undefined, clock: Clock.Clock): bigint => {
333
- if (input === undefined) {
334
- return clock.unsafeCurrentTimeNanos()
335
- } else if (typeof input === "number") {
336
- return BigInt(Math.round(input * 1_000_000))
337
- } else if (input instanceof Date) {
338
- return BigInt(input.getTime()) * bigint1e6
339
- }
340
- const [seconds, nanos] = input
341
- return BigInt(seconds) * bigint1e9 + BigInt(nanos)
342
- }
343
-
344
- /** @internal */
345
- export const currentOtelSpan: Effect.Effect<OtelApi.Span, Cause.NoSuchElementException> = Effect.clockWith((clock) =>
346
- Effect.map(Effect.currentSpan, (span): OtelApi.Span => {
347
- if (OtelSpanTypeId in span) {
348
- return (span as OtelSpan).span
349
- }
350
- return makeOtelSpan(span, clock)
351
- })
352
- )
353
-
354
- /** @internal */
355
- export const layerGlobalProvider = Layer.sync(
356
- TracerProvider,
357
- () => OtelApi.trace.getTracerProvider()
358
- )
359
-
360
- /** @internal */
361
- export const layerTracer = Layer.effect(
362
- Tracer,
363
- Effect.flatMap(
364
- Effect.zip(Resource, TracerProvider),
365
- ([resource, provider]) =>
366
- Effect.sync(() =>
367
- provider.getTracer(
368
- resource.attributes[OtelSemConv.ATTR_SERVICE_NAME] as string,
369
- resource.attributes[OtelSemConv.ATTR_SERVICE_VERSION] as string
370
- )
371
- )
372
- )
373
- )
374
-
375
- /** @internal */
376
- export const layerGlobalTracer = layerTracer.pipe(
377
- Layer.provide(layerGlobalProvider)
378
- )
379
-
380
- /** @internal */
381
- export const layerGlobal = Layer.unwrapEffect(Effect.map(make, Layer.setTracer)).pipe(
382
- Layer.provideMerge(layerGlobalTracer)
383
- )
384
-
385
- /** @internal */
386
- export const layerWithoutOtelTracer = Layer.unwrapEffect(Effect.map(make, Layer.setTracer))
387
-
388
- /** @internal */
389
- export const layer = layerWithoutOtelTracer.pipe(
390
- Layer.provideMerge(layerTracer)
391
- )
392
-
393
- // -------------------------------------------------------------------------------------
394
- // utils
395
- // -------------------------------------------------------------------------------------
396
-
397
- const createTraceState = Option.liftThrowable(OtelApi.createTraceState)
398
-
399
- const populateContext = (
400
- otelContext: OtelApi.Context,
401
- span: EffectTracer.AnySpan,
402
- context?: Context.Context<never>
403
- ): OtelApi.Context =>
404
- span instanceof OtelSpan ?
405
- OtelApi.trace.setSpan(otelContext, span.span) :
406
- OtelApi.trace.setSpanContext(otelContext, makeSpanContext(span, context))
407
-
408
- const makeSpanContext = (span: EffectTracer.AnySpan, context?: Context.Context<never>): OtelApi.SpanContext => ({
409
- spanId: span.spanId,
410
- traceId: span.traceId,
411
- isRemote: span._tag === "ExternalSpan",
412
- traceFlags: Option.getOrElse(
413
- context ?
414
- extractTraceTag(span, context, traceFlagsTag) :
415
- Context.getOption(span.context, traceFlagsTag),
416
- () => OtelApi.TraceFlags.SAMPLED
417
- ),
418
- traceState: Option.getOrUndefined(
419
- context ?
420
- extractTraceTag(span, context, traceStateTag) :
421
- Context.getOption(span.context, traceStateTag)
422
- ) as OtelApi.TraceState
423
- })
424
-
425
- const extractTraceTag = <I, S>(
426
- parent: EffectTracer.AnySpan,
427
- context: Context.Context<never>,
428
- tag: Context.Tag<I, S>
429
- ) =>
430
- Option.orElse(
431
- Context.getOption(context, tag),
432
- () => Context.getOption(parent.context, tag)
433
- )
434
-
435
- /** @internal */
436
- export const withSpanContext = dual<
437
- (
438
- spanContext: OtelApi.SpanContext
439
- ) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, EffectTracer.ParentSpan>>,
440
- <A, E, R>(
441
- effect: Effect.Effect<A, E, R>,
442
- spanContext: OtelApi.SpanContext
443
- ) => Effect.Effect<A, E, Exclude<R, EffectTracer.ParentSpan>>
444
- >(2, <A, E, R>(
445
- effect: Effect.Effect<A, E, R>,
446
- spanContext: OtelApi.SpanContext
447
- ): Effect.Effect<A, E, Exclude<R, EffectTracer.ParentSpan>> =>
448
- Effect.withParentSpan(effect, makeExternalSpan(spanContext)))
@@ -1,31 +0,0 @@
1
- import type * as OtelApi from "@opentelemetry/api"
2
- import type { NonEmptyReadonlyArray } from "effect/Array"
3
- import * as Inspectable from "effect/Inspectable"
4
-
5
- const bigint1e9 = 1_000_000_000n
6
-
7
- /** @internal */
8
- export const nanosToHrTime = (timestamp: bigint): OtelApi.HrTime => {
9
- return [Number(timestamp / bigint1e9), Number(timestamp % bigint1e9)]
10
- }
11
-
12
- /** @internal */
13
- export const recordToAttributes = (value: Record<string, unknown>): OtelApi.Attributes =>
14
- Object.entries(value).reduce((acc, [key, value]) => {
15
- acc[key] = unknownToAttributeValue(value)
16
- return acc
17
- }, {} as OtelApi.Attributes)
18
-
19
- /** @internal */
20
- export const unknownToAttributeValue = (value: unknown): OtelApi.AttributeValue => {
21
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
22
- return value
23
- } else if (typeof value === "bigint") {
24
- return value.toString()
25
- }
26
- return Inspectable.toStringUnknown(value)
27
- }
28
-
29
- /** @internal */
30
- export const isNonEmpty = <A>(a: A | ReadonlyArray<A> | undefined): a is A | NonEmptyReadonlyArray<A> =>
31
- a !== undefined && !(Array.isArray(a) && a.length === 0)
File without changes