@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.
Files changed (160) 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/{esm/index.js → index.js} +4 -20
  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 +70 -118
  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 -26
  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/OtlpTracer/package.json +0 -6
  60. package/Resource/package.json +0 -6
  61. package/Tracer/package.json +0 -6
  62. package/WebSdk/package.json +0 -6
  63. package/dist/cjs/Logger.js +0 -85
  64. package/dist/cjs/Logger.js.map +0 -1
  65. package/dist/cjs/Metrics.js +0 -24
  66. package/dist/cjs/Metrics.js.map +0 -1
  67. package/dist/cjs/NodeSdk.js +0 -53
  68. package/dist/cjs/NodeSdk.js.map +0 -1
  69. package/dist/cjs/Otlp.js +0 -46
  70. package/dist/cjs/Otlp.js.map +0 -1
  71. package/dist/cjs/OtlpLogger.js +0 -158
  72. package/dist/cjs/OtlpLogger.js.map +0 -1
  73. package/dist/cjs/OtlpMetrics.js +0 -354
  74. package/dist/cjs/OtlpMetrics.js.map +0 -1
  75. package/dist/cjs/OtlpResource.js +0 -136
  76. package/dist/cjs/OtlpResource.js.map +0 -1
  77. package/dist/cjs/OtlpTracer.js +0 -229
  78. package/dist/cjs/OtlpTracer.js.map +0 -1
  79. package/dist/cjs/Resource.js +0 -75
  80. package/dist/cjs/Resource.js.map +0 -1
  81. package/dist/cjs/Tracer.js +0 -87
  82. package/dist/cjs/Tracer.js.map +0 -1
  83. package/dist/cjs/WebSdk.js +0 -42
  84. package/dist/cjs/WebSdk.js.map +0 -1
  85. package/dist/cjs/index.js +0 -30
  86. package/dist/cjs/index.js.map +0 -1
  87. package/dist/cjs/internal/metrics.js +0 -288
  88. package/dist/cjs/internal/metrics.js.map +0 -1
  89. package/dist/cjs/internal/otlpExporter.js +0 -81
  90. package/dist/cjs/internal/otlpExporter.js.map +0 -1
  91. package/dist/cjs/internal/tracer.js +0 -299
  92. package/dist/cjs/internal/tracer.js.map +0 -1
  93. package/dist/cjs/internal/utils.js +0 -34
  94. package/dist/cjs/internal/utils.js.map +0 -1
  95. package/dist/dts/Logger.d.ts.map +0 -1
  96. package/dist/dts/Metrics.d.ts +0 -29
  97. package/dist/dts/Metrics.d.ts.map +0 -1
  98. package/dist/dts/NodeSdk.d.ts.map +0 -1
  99. package/dist/dts/Otlp.d.ts +0 -31
  100. package/dist/dts/Otlp.d.ts.map +0 -1
  101. package/dist/dts/OtlpLogger.d.ts +0 -46
  102. package/dist/dts/OtlpLogger.d.ts.map +0 -1
  103. package/dist/dts/OtlpMetrics.d.ts +0 -40
  104. package/dist/dts/OtlpMetrics.d.ts.map +0 -1
  105. package/dist/dts/OtlpResource.d.ts +0 -104
  106. package/dist/dts/OtlpResource.d.ts.map +0 -1
  107. package/dist/dts/OtlpTracer.d.ts +0 -49
  108. package/dist/dts/OtlpTracer.d.ts.map +0 -1
  109. package/dist/dts/Resource.d.ts.map +0 -1
  110. package/dist/dts/Tracer.d.ts +0 -143
  111. package/dist/dts/Tracer.d.ts.map +0 -1
  112. package/dist/dts/WebSdk.d.ts.map +0 -1
  113. package/dist/dts/index.d.ts +0 -45
  114. package/dist/dts/index.d.ts.map +0 -1
  115. package/dist/dts/internal/otlpExporter.d.ts +0 -2
  116. package/dist/dts/internal/otlpExporter.d.ts.map +0 -1
  117. package/dist/dts/internal/tracer.d.ts +0 -2
  118. package/dist/dts/internal/tracer.d.ts.map +0 -1
  119. package/dist/dts/internal/utils.d.ts +0 -2
  120. package/dist/dts/internal/utils.d.ts.map +0 -1
  121. package/dist/esm/Logger.js +0 -75
  122. package/dist/esm/Logger.js.map +0 -1
  123. package/dist/esm/Metrics.js +0 -17
  124. package/dist/esm/Metrics.js.map +0 -1
  125. package/dist/esm/NodeSdk.js.map +0 -1
  126. package/dist/esm/Otlp.js +0 -38
  127. package/dist/esm/Otlp.js.map +0 -1
  128. package/dist/esm/OtlpLogger.js +0 -150
  129. package/dist/esm/OtlpLogger.js.map +0 -1
  130. package/dist/esm/OtlpMetrics.js +0 -346
  131. package/dist/esm/OtlpMetrics.js.map +0 -1
  132. package/dist/esm/OtlpResource.js +0 -124
  133. package/dist/esm/OtlpResource.js.map +0 -1
  134. package/dist/esm/OtlpTracer.js +0 -221
  135. package/dist/esm/OtlpTracer.js.map +0 -1
  136. package/dist/esm/Resource.js.map +0 -1
  137. package/dist/esm/Tracer.js +0 -80
  138. package/dist/esm/Tracer.js.map +0 -1
  139. package/dist/esm/WebSdk.js +0 -33
  140. package/dist/esm/WebSdk.js.map +0 -1
  141. package/dist/esm/index.js.map +0 -1
  142. package/dist/esm/internal/metrics.js +0 -278
  143. package/dist/esm/internal/metrics.js.map +0 -1
  144. package/dist/esm/internal/otlpExporter.js +0 -74
  145. package/dist/esm/internal/otlpExporter.js.map +0 -1
  146. package/dist/esm/internal/tracer.js +0 -290
  147. package/dist/esm/internal/tracer.js.map +0 -1
  148. package/dist/esm/internal/utils.js +0 -23
  149. package/dist/esm/internal/utils.js.map +0 -1
  150. package/dist/esm/package.json +0 -4
  151. package/index/package.json +0 -6
  152. package/src/Otlp.ts +0 -66
  153. package/src/OtlpLogger.ts +0 -258
  154. package/src/OtlpMetrics.ts +0 -571
  155. package/src/OtlpResource.ts +0 -232
  156. package/src/OtlpTracer.ts +0 -349
  157. package/src/internal/otlpExporter.ts +0 -124
  158. package/src/internal/tracer.ts +0 -437
  159. package/src/internal/utils.ts +0 -31
  160. /package/dist/{dts/internal → internal}/metrics.d.ts +0 -0
@@ -1,232 +0,0 @@
1
- /**
2
- * @since 1.0.0
3
- */
4
- import * as Arr from "effect/Array"
5
- import * as Config from "effect/Config"
6
- import * as Effect from "effect/Effect"
7
- import * as Inspectable from "effect/Inspectable"
8
-
9
- const ATTR_SERVICE_NAME = "service.name"
10
- const ATTR_SERVICE_VERSION = "service.version"
11
-
12
- /**
13
- * @since 1.0.0
14
- * @category Models
15
- */
16
- export interface Resource {
17
- /** Resource attributes */
18
- attributes: Array<KeyValue>
19
- /** Resource droppedAttributesCount */
20
- droppedAttributesCount: number
21
- }
22
-
23
- /**
24
- * @since 1.0.0
25
- * @category Constructors
26
- */
27
- export const make = (options: {
28
- readonly serviceName: string
29
- readonly serviceVersion?: string | undefined
30
- readonly attributes?: Record<string, unknown> | undefined
31
- }): Resource => {
32
- const resourceAttributes = options.attributes
33
- ? entriesToAttributes(Object.entries(options.attributes))
34
- : []
35
- resourceAttributes.push({
36
- key: ATTR_SERVICE_NAME,
37
- value: {
38
- stringValue: options.serviceName
39
- }
40
- })
41
- if (options.serviceVersion) {
42
- resourceAttributes.push({
43
- key: ATTR_SERVICE_VERSION,
44
- value: {
45
- stringValue: options.serviceVersion
46
- }
47
- })
48
- }
49
-
50
- return {
51
- attributes: resourceAttributes,
52
- droppedAttributesCount: 0
53
- }
54
- }
55
-
56
- /**
57
- * @since 1.0.0
58
- * @category Constructors
59
- */
60
- export const fromConfig: (
61
- options?: {
62
- readonly serviceName?: string | undefined
63
- readonly serviceVersion?: string | undefined
64
- readonly attributes?: Record<string, unknown> | undefined
65
- } | undefined
66
- ) => Effect.Effect<Resource> = Effect.fnUntraced(function*(options?: {
67
- readonly serviceName?: string | undefined
68
- readonly serviceVersion?: string | undefined
69
- readonly attributes?: Record<string, unknown> | undefined
70
- }) {
71
- const attributes = yield* Config.string("OTEL_RESOURCE_ATTRIBUTES").pipe(
72
- Config.map((s) => {
73
- const attrs = s.split(",")
74
- return Arr.reduce(attrs, {} as Record<string, string>, (acc, attr) => {
75
- const parts = attr.split("=")
76
- if (parts.length !== 2) {
77
- return acc
78
- }
79
- acc[parts[0].trim()] = parts[1].trim()
80
- return acc
81
- })
82
- }),
83
- Config.withDefault({}),
84
- Effect.map((envAttrs) => ({
85
- ...envAttrs,
86
- ...options?.attributes
87
- }))
88
- )
89
- const serviceName = options?.serviceName ?? attributes[ATTR_SERVICE_NAME] as string ??
90
- (yield* Config.string("OTEL_SERVICE_NAME"))
91
- const serviceVersion = options?.serviceVersion ?? attributes[ATTR_SERVICE_VERSION] as string ??
92
- (yield* Config.string("OTEL_SERVICE_VERSION").pipe(Config.withDefault(undefined)))
93
- return make({
94
- serviceName,
95
- serviceVersion,
96
- attributes
97
- })
98
- }, Effect.orDie)
99
-
100
- /**
101
- * @since 1.0.0
102
- * @category Attributes
103
- */
104
- export const unsafeServiceName = (resource: Resource): string => {
105
- const serviceNameAttribute = resource.attributes.find(
106
- (attr) => attr.key === ATTR_SERVICE_NAME
107
- )
108
- if (!serviceNameAttribute || !serviceNameAttribute.value.stringValue) {
109
- throw new Error("Resource does not contain a service name")
110
- }
111
- return serviceNameAttribute.value.stringValue
112
- }
113
-
114
- /**
115
- * @since 1.0.0
116
- * @category Attributes
117
- */
118
- export const entriesToAttributes = (entries: Iterable<[string, unknown]>): Array<KeyValue> => {
119
- const attributes: Array<KeyValue> = []
120
- for (const [key, value] of entries) {
121
- attributes.push({
122
- key,
123
- value: unknownToAttributeValue(value)
124
- })
125
- }
126
- return attributes
127
- }
128
-
129
- /**
130
- * @since 1.0.0
131
- * @category Attributes
132
- */
133
- export const unknownToAttributeValue = (value: unknown): AnyValue => {
134
- if (Array.isArray(value)) {
135
- return {
136
- arrayValue: {
137
- values: value.map(unknownToAttributeValue)
138
- }
139
- }
140
- }
141
- switch (typeof value) {
142
- case "string":
143
- return {
144
- stringValue: value
145
- }
146
- case "bigint":
147
- return {
148
- intValue: Number(value)
149
- }
150
- case "number":
151
- return Number.isInteger(value)
152
- ? {
153
- intValue: value
154
- }
155
- : {
156
- doubleValue: value
157
- }
158
- case "boolean":
159
- return {
160
- boolValue: value
161
- }
162
- default:
163
- return {
164
- stringValue: Inspectable.toStringUnknown(value)
165
- }
166
- }
167
- }
168
-
169
- /**
170
- * @since 1.0.0
171
- * @category Models
172
- */
173
- export interface KeyValue {
174
- /** KeyValue key */
175
- key: string
176
- /** KeyValue value */
177
- value: AnyValue
178
- }
179
-
180
- /**
181
- * @since 1.0.0
182
- * @category Models
183
- */
184
- export interface AnyValue {
185
- /** AnyValue stringValue */
186
- stringValue?: string | null
187
- /** AnyValue boolValue */
188
- boolValue?: boolean | null
189
- /** AnyValue intValue */
190
- intValue?: number | null
191
- /** AnyValue doubleValue */
192
- doubleValue?: number | null
193
- /** AnyValue arrayValue */
194
- arrayValue?: ArrayValue
195
- /** AnyValue kvlistValue */
196
- kvlistValue?: KeyValueList
197
- /** AnyValue bytesValue */
198
- bytesValue?: Uint8Array
199
- }
200
-
201
- /**
202
- * @since 1.0.0
203
- * @category Models
204
- */
205
- export interface ArrayValue {
206
- /** ArrayValue values */
207
- values: Array<AnyValue>
208
- }
209
-
210
- /**
211
- * @since 1.0.0
212
- * @category Models
213
- */
214
- export interface KeyValueList {
215
- /** KeyValueList values */
216
- values: Array<KeyValue>
217
- }
218
-
219
- /**
220
- * @since 1.0.0
221
- * @category Models
222
- */
223
- export interface LongBits {
224
- low: number
225
- high: number
226
- }
227
-
228
- /**
229
- * @since 1.0.0
230
- * @category Models
231
- */
232
- export type Fixed64 = LongBits | string | number
package/src/OtlpTracer.ts DELETED
@@ -1,349 +0,0 @@
1
- /**
2
- * @since 1.0.0
3
- */
4
- import type * as Headers from "@effect/platform/Headers"
5
- import type * as HttpClient from "@effect/platform/HttpClient"
6
- import * as Cause from "effect/Cause"
7
- import type * as Context from "effect/Context"
8
- import * as Duration from "effect/Duration"
9
- import * as Effect from "effect/Effect"
10
- import type * as Exit from "effect/Exit"
11
- import * as Layer from "effect/Layer"
12
- import * as Option from "effect/Option"
13
- import type * as Scope from "effect/Scope"
14
- import * as Tracer from "effect/Tracer"
15
- import type { ExtractTag } from "effect/Types"
16
- import * as Exporter from "./internal/otlpExporter.js"
17
- import type { KeyValue, Resource } from "./OtlpResource.js"
18
- import { entriesToAttributes } from "./OtlpResource.js"
19
- import * as OtlpResource from "./OtlpResource.js"
20
-
21
- const ATTR_EXCEPTION_TYPE = "exception.type"
22
- const ATTR_EXCEPTION_MESSAGE = "exception.message"
23
- const ATTR_EXCEPTION_STACKTRACE = "exception.stacktrace"
24
-
25
- /**
26
- * @since 1.0.0
27
- * @category Constructors
28
- */
29
- export const make: (
30
- options: {
31
- readonly url: string
32
- readonly resource?: {
33
- readonly serviceName?: string | undefined
34
- readonly serviceVersion?: string | undefined
35
- readonly attributes?: Record<string, unknown>
36
- } | undefined
37
- readonly headers?: Headers.Input | undefined
38
- readonly exportInterval?: Duration.DurationInput | undefined
39
- readonly maxBatchSize?: number | undefined
40
- readonly context?: (<X>(f: () => X, span: Tracer.AnySpan) => X) | undefined
41
- readonly shutdownTimeout?: Duration.DurationInput | undefined
42
- }
43
- ) => Effect.Effect<
44
- Tracer.Tracer,
45
- never,
46
- HttpClient.HttpClient | Scope.Scope
47
- > = Effect.fnUntraced(function*(options) {
48
- const otelResource = yield* OtlpResource.fromConfig(options.resource)
49
- const scope: Scope = {
50
- name: OtlpResource.unsafeServiceName(otelResource)
51
- }
52
-
53
- const exporter = yield* Exporter.make({
54
- label: "OtlpTracer",
55
- url: options.url,
56
- headers: options.headers,
57
- exportInterval: options.exportInterval ?? Duration.seconds(5),
58
- maxBatchSize: options.maxBatchSize ?? 1000,
59
- body(spans) {
60
- const data: TraceData = {
61
- resourceSpans: [{
62
- resource: otelResource,
63
- scopeSpans: [{
64
- scope,
65
- spans
66
- }]
67
- }]
68
- }
69
- return data
70
- },
71
- shutdownTimeout: options.shutdownTimeout ?? Duration.seconds(3)
72
- })
73
-
74
- const exportFn = (span: SpanImpl) => {
75
- exporter.push(makeOtlpSpan(span))
76
- }
77
-
78
- return Tracer.make({
79
- span(name, parent, context, links, startTime, kind) {
80
- return makeSpan({
81
- name,
82
- parent,
83
- context,
84
- status: {
85
- _tag: "Started",
86
- startTime
87
- },
88
- attributes: new Map(),
89
- links,
90
- sampled: true,
91
- kind,
92
- export: exportFn
93
- })
94
- },
95
- context: options.context ?
96
- function(f, fiber) {
97
- if (fiber.currentSpan === undefined) {
98
- return f()
99
- }
100
- return options.context!(f, fiber.currentSpan)
101
- } :
102
- defaultContext
103
- })
104
- })
105
-
106
- /**
107
- * @since 1.0.0
108
- * @category Layers
109
- */
110
- export const layer = (options: {
111
- readonly url: string
112
- readonly resource?: {
113
- readonly serviceName?: string | undefined
114
- readonly serviceVersion?: string | undefined
115
- readonly attributes?: Record<string, unknown>
116
- } | undefined
117
- readonly headers?: Headers.Input | undefined
118
- readonly exportInterval?: Duration.DurationInput | undefined
119
- readonly maxBatchSize?: number | undefined
120
- readonly context?: (<X>(f: () => X, span: Tracer.AnySpan) => X) | undefined
121
- readonly shutdownTimeout?: Duration.DurationInput | undefined
122
- }): Layer.Layer<never, never, HttpClient.HttpClient> => Layer.unwrapScoped(Effect.map(make(options), Layer.setTracer))
123
-
124
- // internal
125
-
126
- function defaultContext<X>(f: () => X, _: any): X {
127
- return f()
128
- }
129
-
130
- interface SpanImpl extends Tracer.Span {
131
- readonly export: (span: SpanImpl) => void
132
- readonly attributes: Map<string, unknown>
133
- readonly links: Array<Tracer.SpanLink>
134
- readonly events: Array<[name: string, startTime: bigint, attributes: Record<string, unknown> | undefined]>
135
- status: Tracer.SpanStatus
136
- }
137
-
138
- const SpanProto = {
139
- _tag: "Span",
140
- end(this: SpanImpl, endTime: bigint, exit: Exit.Exit<unknown, unknown>) {
141
- this.status = {
142
- _tag: "Ended",
143
- startTime: this.status.startTime,
144
- endTime,
145
- exit
146
- }
147
- this.export(this)
148
- },
149
- attribute(this: SpanImpl, key: string, value: unknown) {
150
- this.attributes.set(key, value)
151
- },
152
- event(this: SpanImpl, name: string, startTime: bigint, attributes?: Record<string, unknown>) {
153
- this.events.push([name, startTime, attributes])
154
- },
155
- addLinks(this: SpanImpl, links: ReadonlyArray<Tracer.SpanLink>) {
156
- // eslint-disable-next-line no-restricted-syntax
157
- this.links.push(...links)
158
- }
159
- }
160
-
161
- const makeSpan = (options: {
162
- readonly name: string
163
- readonly parent: Option.Option<Tracer.AnySpan>
164
- readonly context: Context.Context<never>
165
- readonly status: Tracer.SpanStatus
166
- readonly attributes: ReadonlyMap<string, unknown>
167
- readonly links: ReadonlyArray<Tracer.SpanLink>
168
- readonly sampled: boolean
169
- readonly kind: Tracer.SpanKind
170
- readonly export: (span: SpanImpl) => void
171
- }): SpanImpl => {
172
- const self = Object.assign(Object.create(SpanProto), options)
173
- if (Option.isSome(self.parent)) {
174
- self.traceId = self.parent.value.traceId
175
- } else {
176
- self.traceId = generateId(32)
177
- }
178
- self.spanId = generateId(16)
179
- self.events = []
180
- return self
181
- }
182
-
183
- const generateId = (len: number): string => {
184
- const chars = "0123456789abcdef"
185
- let result = ""
186
- for (let i = 0; i < len; i++) {
187
- result += chars[Math.floor(Math.random() * chars.length)]
188
- }
189
- return result
190
- }
191
-
192
- const makeOtlpSpan = (self: SpanImpl): OtlpSpan => {
193
- const status = self.status as ExtractTag<Tracer.SpanStatus, "Ended">
194
- const attributes = entriesToAttributes(self.attributes.entries())
195
- const events = self.events.map(([name, startTime, attributes]) => ({
196
- name,
197
- timeUnixNano: String(startTime),
198
- attributes: attributes
199
- ? entriesToAttributes(Object.entries(attributes))
200
- : [],
201
- droppedAttributesCount: 0
202
- }))
203
- let otelStatus: Status
204
-
205
- if (status.exit._tag === "Success") {
206
- otelStatus = constOtelStatusSuccess
207
- } else if (Cause.isInterruptedOnly(status.exit.cause)) {
208
- otelStatus = {
209
- code: StatusCode.Ok,
210
- message: Cause.pretty(status.exit.cause)
211
- }
212
- } else {
213
- const errors = Cause.prettyErrors(status.exit.cause)
214
- const firstError = errors[0]
215
- otelStatus = {
216
- code: StatusCode.Error
217
- }
218
- attributes.push({
219
- key: "span.label",
220
- value: { stringValue: "⚠︎ Interrupted" }
221
- }, {
222
- key: "status.interrupted",
223
- value: { boolValue: true }
224
- })
225
- if (firstError) {
226
- otelStatus.message = firstError.message
227
- events.push({
228
- name: "exception",
229
- timeUnixNano: String(status.endTime),
230
- droppedAttributesCount: 0,
231
- attributes: [
232
- {
233
- "key": ATTR_EXCEPTION_TYPE,
234
- "value": {
235
- "stringValue": firstError.name
236
- }
237
- },
238
- {
239
- "key": ATTR_EXCEPTION_MESSAGE,
240
- "value": {
241
- "stringValue": firstError.message
242
- }
243
- },
244
- {
245
- "key": ATTR_EXCEPTION_STACKTRACE,
246
- "value": {
247
- "stringValue": Cause.pretty(status.exit.cause, { renderErrorCause: true })
248
- }
249
- }
250
- ]
251
- })
252
- }
253
- }
254
-
255
- return {
256
- traceId: self.traceId,
257
- spanId: self.spanId,
258
- parentSpanId: Option.isSome(self.parent) ? self.parent.value.spanId : undefined,
259
- name: self.name,
260
- kind: SpanKind[self.kind],
261
- startTimeUnixNano: String(status.startTime),
262
- endTimeUnixNano: String(status.endTime),
263
- attributes,
264
- droppedAttributesCount: 0,
265
- events,
266
- droppedEventsCount: 0,
267
- status: otelStatus,
268
- links: self.links.map((link) => ({
269
- traceId: link.span.traceId,
270
- spanId: link.span.spanId,
271
- attributes: entriesToAttributes(Object.entries(link.attributes)),
272
- droppedAttributesCount: 0
273
- })),
274
- droppedLinksCount: 0
275
- }
276
- }
277
-
278
- interface TraceData {
279
- readonly resourceSpans: Array<ResourceSpan>
280
- }
281
-
282
- interface ResourceSpan {
283
- readonly resource: Resource
284
- readonly scopeSpans: Array<ScopeSpan>
285
- }
286
-
287
- interface ScopeSpan {
288
- readonly scope: Scope
289
- readonly spans: Array<OtlpSpan>
290
- }
291
-
292
- interface Scope {
293
- readonly name: string
294
- }
295
-
296
- interface OtlpSpan {
297
- readonly traceId: string
298
- readonly spanId: string
299
- readonly parentSpanId: string | undefined
300
- readonly name: string
301
- readonly kind: number
302
- readonly startTimeUnixNano: string
303
- readonly endTimeUnixNano: string
304
- readonly attributes: Array<KeyValue>
305
- readonly droppedAttributesCount: number
306
- readonly events: Array<Event>
307
- readonly droppedEventsCount: number
308
- readonly status: Status
309
- readonly links: Array<Link>
310
- readonly droppedLinksCount: number
311
- }
312
-
313
- interface Event {
314
- readonly attributes: Array<KeyValue>
315
- readonly name: string
316
- readonly timeUnixNano: string
317
- readonly droppedAttributesCount: number
318
- }
319
-
320
- interface Link {
321
- readonly attributes: Array<KeyValue>
322
- readonly spanId: string
323
- readonly traceId: string
324
- readonly droppedAttributesCount: number
325
- }
326
-
327
- interface Status {
328
- readonly code: StatusCode
329
- message?: string
330
- }
331
-
332
- const enum StatusCode {
333
- Unset = 0,
334
- Ok = 1,
335
- Error = 2
336
- }
337
-
338
- enum SpanKind {
339
- unspecified = 0,
340
- internal = 1,
341
- server = 2,
342
- client = 3,
343
- producer = 4,
344
- consumer = 5
345
- }
346
-
347
- const constOtelStatusSuccess: Status = {
348
- code: StatusCode.Ok
349
- }
@@ -1,124 +0,0 @@
1
- import * as Headers from "@effect/platform/Headers"
2
- import * as HttpClient from "@effect/platform/HttpClient"
3
- import * as HttpClientError from "@effect/platform/HttpClientError"
4
- import * as HttpClientRequest from "@effect/platform/HttpClientRequest"
5
- import * as Duration from "effect/Duration"
6
- import * as Effect from "effect/Effect"
7
- import * as FiberSet from "effect/FiberSet"
8
- import * as Num from "effect/Number"
9
- import * as Option from "effect/Option"
10
- import * as Schedule from "effect/Schedule"
11
- import * as Scope from "effect/Scope"
12
-
13
- const policy = Schedule.forever.pipe(
14
- Schedule.passthrough,
15
- Schedule.addDelay((error) => {
16
- if (
17
- HttpClientError.isHttpClientError(error)
18
- && error._tag === "ResponseError"
19
- && error.response.status === 429
20
- ) {
21
- const retryAfter = Option.fromNullable(error.response.headers["retry-after"]).pipe(
22
- Option.flatMap(Num.parse),
23
- Option.getOrElse(() => 5)
24
- )
25
- return Duration.seconds(retryAfter)
26
- }
27
- return Duration.seconds(1)
28
- })
29
- )
30
-
31
- /** @internal */
32
- export const make: (
33
- options: {
34
- readonly url: string
35
- readonly headers: Headers.Input | undefined
36
- readonly label: string
37
- readonly exportInterval: Duration.DurationInput
38
- readonly maxBatchSize: number | "disabled"
39
- readonly body: (data: Array<any>) => unknown
40
- readonly shutdownTimeout: Duration.DurationInput
41
- }
42
- ) => Effect.Effect<
43
- { readonly push: (data: unknown) => void },
44
- never,
45
- HttpClient.HttpClient | Scope.Scope
46
- > = Effect.fnUntraced(function*(options) {
47
- const clock = yield* Effect.clock
48
- const scope = yield* Effect.scope
49
- const exportInterval = Duration.decode(options.exportInterval)
50
- let disabledUntil: number | undefined = undefined
51
-
52
- const client = HttpClient.filterStatusOk(yield* HttpClient.HttpClient).pipe(
53
- HttpClient.retryTransient({ schedule: policy, times: 3 })
54
- )
55
-
56
- let headers = Headers.unsafeFromRecord({
57
- "user-agent": `effect-opentelemetry-${options.label}/0.0.0`
58
- })
59
- if (options.headers) {
60
- headers = Headers.merge(Headers.fromInput(options.headers), headers)
61
- }
62
-
63
- const request = HttpClientRequest.post(options.url, { headers })
64
- let buffer: Array<any> = []
65
- const runExport = Effect.suspend(() => {
66
- if (disabledUntil !== undefined && clock.unsafeCurrentTimeMillis() < disabledUntil) {
67
- return Effect.void
68
- } else if (disabledUntil !== undefined) {
69
- disabledUntil = undefined
70
- }
71
- const items = buffer
72
- if (options.maxBatchSize !== "disabled") {
73
- if (buffer.length === 0) {
74
- return Effect.void
75
- }
76
- buffer = []
77
- }
78
- return client.execute(
79
- HttpClientRequest.bodyUnsafeJson(request, options.body(items))
80
- ).pipe(
81
- Effect.asVoid,
82
- Effect.withTracerEnabled(false)
83
- )
84
- }).pipe(
85
- Effect.catchAllCause((cause) => {
86
- if (disabledUntil !== undefined) return Effect.void
87
- disabledUntil = clock.unsafeCurrentTimeMillis() + Duration.toMillis("1 minute")
88
- return Effect.logDebug(`Disabling ${options.label} for 60 seconds`, cause)
89
- })
90
- )
91
-
92
- yield* Scope.addFinalizer(
93
- scope,
94
- runExport.pipe(
95
- Effect.ignore,
96
- Effect.interruptible,
97
- Effect.timeoutOption(options.shutdownTimeout)
98
- )
99
- )
100
-
101
- yield* Effect.sleep(exportInterval).pipe(
102
- Effect.zipRight(runExport),
103
- Effect.forever,
104
- Effect.annotateLogs({
105
- package: "@effect/opentelemetry",
106
- module: options.label
107
- }),
108
- Effect.forkIn(scope),
109
- Effect.interruptible
110
- )
111
-
112
- const runFork = yield* FiberSet.makeRuntime().pipe(
113
- Effect.interruptible
114
- )
115
- return {
116
- push(data) {
117
- if (disabledUntil !== undefined) return
118
- buffer.push(data)
119
- if (options.maxBatchSize !== "disabled" && buffer.length >= options.maxBatchSize) {
120
- runFork(runExport)
121
- }
122
- }
123
- }
124
- })