@smthrs/observability 1.0.0-rc.5 → 1.0.0-rc.7
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/README.md +9 -8
- package/dist/cjs/Otel.d.ts +3 -2
- package/dist/cjs/Otel.d.ts.map +1 -1
- package/dist/cjs/Otel.js.map +2 -2
- package/dist/cjs/Otlp.d.ts +9 -6
- package/dist/cjs/Otlp.d.ts.map +1 -1
- package/dist/cjs/Otlp.js +23 -7
- package/dist/cjs/Otlp.js.map +2 -2
- package/dist/cjs/Resource.d.ts +6 -5
- package/dist/cjs/Resource.d.ts.map +1 -1
- package/dist/cjs/Resource.js +1 -1
- package/dist/cjs/Resource.js.map +2 -2
- package/dist/esm/Otel.d.ts +3 -2
- package/dist/esm/Otel.d.ts.map +1 -1
- package/dist/esm/Otel.js +2 -1
- package/dist/esm/Otel.js.map +1 -1
- package/dist/esm/Otlp.d.ts +9 -6
- package/dist/esm/Otlp.d.ts.map +1 -1
- package/dist/esm/Otlp.js +47 -13
- package/dist/esm/Otlp.js.map +1 -1
- package/dist/esm/Resource.d.ts +6 -5
- package/dist/esm/Resource.d.ts.map +1 -1
- package/dist/esm/Resource.js +7 -6
- package/dist/esm/Resource.js.map +1 -1
- package/docs/api.md +24 -12
- package/docs/concepts/instrumentation-and-export.md +31 -1
- package/docs/concepts/layer-map.md +5 -4
- package/docs/concepts/validated-acquisition.md +4 -3
- package/docs/guides/export-to-a-collector.md +27 -4
- package/docs/guides/testing.md +1 -1
- package/docs/guides/wire-an-opentelemetry-sdk.md +10 -4
- package/docs/installation.md +8 -8
- package/docs/troubleshooting.md +2 -2
- package/package.json +7 -7
- package/src/Otel.ts +3 -2
- package/src/Otlp.ts +52 -15
- package/src/Resource.ts +7 -6
package/src/Otel.ts
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
* @since 0.1.0
|
|
27
27
|
*/
|
|
28
28
|
export interface Options {
|
|
29
|
-
/** Validated resource for metrics
|
|
29
|
+
/** Validated resource for metrics and provider factories. */
|
|
30
30
|
readonly resource: ResourceConfiguration
|
|
31
31
|
/**
|
|
32
32
|
* Borrowed: the layer bridges onto this provider and never flushes or shuts
|
|
@@ -60,7 +60,8 @@ export interface Options {
|
|
|
60
60
|
* layer acquires. Factories run during layer acquisition after resource
|
|
61
61
|
* validation, and the provider factories receive the metric resource.
|
|
62
62
|
* Already-created providers keep the resource captured at construction; the
|
|
63
|
-
* resource option only sets
|
|
63
|
+
* resource option only sets the metric resource for them. Spans and log
|
|
64
|
+
* records carry the instrumentation scope `effect` with the Effect version.
|
|
64
65
|
* Ownership splits: callers own provider flushing and shutdown, including
|
|
65
66
|
* factory-built providers, while the layer scope shuts down every reader the
|
|
66
67
|
* `metricReader` factory returned. No exporter is allocated by this module.
|
package/src/Otlp.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `Metric` counters on their hot paths; what they deliberately do not do is
|
|
6
6
|
* ship an exporter. This module is that exporter: one layer that installs
|
|
7
7
|
* Effect's own OTLP logger, metrics exporter, and tracer
|
|
8
|
-
* (`effect/
|
|
8
|
+
* (`effect/observability/Otlp`) against a collector endpoint, with
|
|
9
9
|
* the flows service identity filled in. Nothing beyond `effect` is involved;
|
|
10
10
|
* no OpenTelemetry SDK dependency.
|
|
11
11
|
*
|
|
@@ -25,21 +25,21 @@ import * as ConfigProvider from "effect/ConfigProvider"
|
|
|
25
25
|
import type * as Duration from "effect/Duration"
|
|
26
26
|
import * as Effect from "effect/Effect"
|
|
27
27
|
import * as Exit from "effect/Exit"
|
|
28
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient"
|
|
29
|
+
import * as Headers from "effect/http/Headers"
|
|
30
|
+
import type * as HttpBody from "effect/http/HttpBody"
|
|
31
|
+
import * as HttpClient from "effect/http/HttpClient"
|
|
32
|
+
import type * as HttpClientRequest from "effect/http/HttpClientRequest"
|
|
33
|
+
import * as HttpClientResponse from "effect/http/HttpClientResponse"
|
|
28
34
|
import * as Layer from "effect/Layer"
|
|
29
35
|
import * as Logger from "effect/Logger"
|
|
30
36
|
import * as Metric from "effect/Metric"
|
|
37
|
+
import * as Otlp from "effect/observability/Otlp"
|
|
31
38
|
import * as Option from "effect/Option"
|
|
32
39
|
import * as References from "effect/References"
|
|
33
40
|
import * as Schema from "effect/Schema"
|
|
34
41
|
import * as Semaphore from "effect/Semaphore"
|
|
35
42
|
import * as Tracer from "effect/Tracer"
|
|
36
|
-
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient"
|
|
37
|
-
import * as Headers from "effect/unstable/http/Headers"
|
|
38
|
-
import type * as HttpBody from "effect/unstable/http/HttpBody"
|
|
39
|
-
import * as HttpClient from "effect/unstable/http/HttpClient"
|
|
40
|
-
import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
|
|
41
|
-
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
|
|
42
|
-
import * as Otlp from "effect/unstable/observability/Otlp"
|
|
43
43
|
import * as Endpoint from "./Endpoint.ts"
|
|
44
44
|
import * as Resource from "./Resource.ts"
|
|
45
45
|
|
|
@@ -66,7 +66,7 @@ export const defaultServiceName = "flows"
|
|
|
66
66
|
* @category resource
|
|
67
67
|
* @since 0.1.0
|
|
68
68
|
*/
|
|
69
|
-
export const defaultServiceVersion = "1.0.0-rc.
|
|
69
|
+
export const defaultServiceVersion = "1.0.0-rc.7"
|
|
70
70
|
|
|
71
71
|
// The upstream logger and tracer each buffer at most one 1,000-record batch.
|
|
72
72
|
// Do not queue at the transport: upstream forks every full batch independently.
|
|
@@ -80,9 +80,11 @@ const maxInFlight = 4
|
|
|
80
80
|
export const maxRequestBytes = 1024 * 1024
|
|
81
81
|
const requestTimeout = "10 seconds"
|
|
82
82
|
const diagnosticIntervalMillis = 60_000
|
|
83
|
-
// The envelope around the resource in
|
|
84
|
-
// the instrumentation scope name: at most 1,024 code units, at most six
|
|
85
|
-
// each once JSON-escaped, so under 8 KiB together with the envelope's own
|
|
83
|
+
// The envelope around the resource in a metrics request repeats `service.name`
|
|
84
|
+
// as the instrumentation scope name: at most 1,024 code units, at most six
|
|
85
|
+
// bytes each once JSON-escaped, so under 8 KiB together with the envelope's own
|
|
86
|
+
// keys. Log and trace requests name their scope `effect` with the Effect
|
|
87
|
+
// version instead, which is smaller.
|
|
86
88
|
/**
|
|
87
89
|
* Reserved bytes for request wrappers and the escaped instrumentation scope.
|
|
88
90
|
* @category transport
|
|
@@ -95,8 +97,9 @@ export const maximumEnvelopeBytes = 8 * 1024
|
|
|
95
97
|
*
|
|
96
98
|
* The transport discards a request over 1 MiB, and the resource rides in every
|
|
97
99
|
* request, so the largest resource `Resource.decode` admits
|
|
98
|
-
* (`Resource.maximumResourceBytes`, 128 KiB) and the envelope
|
|
99
|
-
* service name (under 8 KiB) come off the top.
|
|
100
|
+
* (`Resource.maximumResourceBytes`, 128 KiB) and the envelope, which in a
|
|
101
|
+
* metrics request repeats the service name (under 8 KiB), come off the top.
|
|
102
|
+
* The 888 KiB left gives each
|
|
100
103
|
* record of a full 1,000-record logger or tracer batch about 900 bytes, three
|
|
101
104
|
* to four times a bare log record or span, and gives the metrics snapshot room
|
|
102
105
|
* for several thousand series. Every resource the decoder accepts therefore
|
|
@@ -111,10 +114,42 @@ export const reservedBatchBytes = maxRequestBytes - Resource.maximumResourceByte
|
|
|
111
114
|
|
|
112
115
|
type DiscardReason = "oversized" | "stalled" | "saturated"
|
|
113
116
|
|
|
117
|
+
/**
|
|
118
|
+
* A collector's answer reduced to what the exporter acts on: its status and
|
|
119
|
+
* its `retry-after` delay, with no body.
|
|
120
|
+
*
|
|
121
|
+
* `Response` refuses a status outside 200 to 599 and a malformed header value.
|
|
122
|
+
* Neither is a collector's answer, so `Response.error()` reports it as status
|
|
123
|
+
* 0, which the exporter fails without retrying. A browser already reports a
|
|
124
|
+
* refused redirect that way.
|
|
125
|
+
*/
|
|
126
|
+
const withoutBody = (response: HttpClientResponse.HttpClientResponse): HttpClientResponse.HttpClientResponse => {
|
|
127
|
+
const retryAfter = response.headers["retry-after"]
|
|
128
|
+
let answer: Response
|
|
129
|
+
try {
|
|
130
|
+
answer = new Response(null, {
|
|
131
|
+
status: response.status,
|
|
132
|
+
headers: retryAfter === undefined ? {} : { "retry-after": retryAfter }
|
|
133
|
+
})
|
|
134
|
+
} catch {
|
|
135
|
+
answer = Response.error()
|
|
136
|
+
}
|
|
137
|
+
return HttpClientResponse.fromWeb(response.request, answer)
|
|
138
|
+
}
|
|
139
|
+
|
|
114
140
|
const boundedClient = Layer.effect(
|
|
115
141
|
HttpClient.HttpClient,
|
|
116
142
|
Effect.gen(function*() {
|
|
117
|
-
|
|
143
|
+
// The exporter reads every response body to its end, after the deadline
|
|
144
|
+
// and the in-flight limit below have let the request go. A collector that
|
|
145
|
+
// never ended its answer would stall that signal and grow the buffered
|
|
146
|
+
// body without bound. So the exchange ends here: the exporter receives the
|
|
147
|
+
// status and retry delay, and closing the scope aborts the transport with
|
|
148
|
+
// the body unread.
|
|
149
|
+
const client = HttpClient.transformResponse(
|
|
150
|
+
HttpClient.withScope(yield* HttpClient.HttpClient),
|
|
151
|
+
(exchange) => exchange.pipe(Effect.map(withoutBody), Effect.scoped)
|
|
152
|
+
)
|
|
118
153
|
const clock = yield* Clock
|
|
119
154
|
// The loggers installed when this transport is acquired. The OTLP logger
|
|
120
155
|
// the same acquisition installs does not exist yet, so a diagnostic pinned
|
|
@@ -329,6 +364,8 @@ export interface Options {
|
|
|
329
364
|
* once a minute a `Warn` record with code `otlp_export_discarded` names the
|
|
330
365
|
* reason, the request size, and the running total through the loggers that
|
|
331
366
|
* were installed when the layer was acquired, never through this exporter.
|
|
367
|
+
* A collector's response body is never read: the exporter acts on the status
|
|
368
|
+
* and the `retry-after` header, and the exchange ends when they arrive.
|
|
332
369
|
*
|
|
333
370
|
* **Details**
|
|
334
371
|
*
|
package/src/Resource.ts
CHANGED
|
@@ -8,8 +8,8 @@ import * as OtelResource from "@effect/opentelemetry/Resource"
|
|
|
8
8
|
import type { Attributes as OtelAttributes } from "@opentelemetry/api"
|
|
9
9
|
import * as Effect from "effect/Effect"
|
|
10
10
|
import * as Layer from "effect/Layer"
|
|
11
|
+
import * as OtlpResource from "effect/observability/OtlpResource"
|
|
11
12
|
import * as Schema from "effect/Schema"
|
|
12
|
-
import * as OtlpResource from "effect/unstable/observability/OtlpResource"
|
|
13
13
|
import { schemaIssueMessage, schemaIssuePath } from "./internal/schemaIssuePath.ts"
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -57,11 +57,12 @@ export const maximumAttributes = 256
|
|
|
57
57
|
*
|
|
58
58
|
* The measure is the OTLP `Resource` message the exporters place in every
|
|
59
59
|
* request, `{"attributes":[{"key":…,"value":{…}}],"droppedAttributesCount":0}`
|
|
60
|
-
* with service identity and SDK-added `telemetry.sdk.name
|
|
61
|
-
* `telemetry.sdk.language` included, after JSON
|
|
62
|
-
*
|
|
63
|
-
* exporter
|
|
64
|
-
*
|
|
60
|
+
* with service identity and SDK-added `telemetry.sdk.name`,
|
|
61
|
+
* `telemetry.sdk.language` and `telemetry.sdk.version` included, after JSON
|
|
62
|
+
* escaping and UTF-8 encoding. The SDK's own values are reserved even for the
|
|
63
|
+
* default Effect exporter, whose values are shorter. A caller attribute that
|
|
64
|
+
* replaces an SDK field, or that service identity overwrites, is counted
|
|
65
|
+
* beside it. It is the bound the per-field limits cannot express: 256
|
|
65
66
|
* attributes at the string ceiling would encode to 16 MiB, and the resource
|
|
66
67
|
* rides in every export request, so the default transport, which discards a
|
|
67
68
|
* request over 1 MiB, would deliver nothing at all. 128 KiB admits one
|