@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/OtlpResource.ts
DELETED
|
@@ -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
|
-
})
|