effect-cf 0.13.0 → 0.14.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/CHANGELOG.md +19 -0
- package/dist/{Hyperdrive-L6ldMlRF.d.mts → Hyperdrive-C0vfrPeG.d.mts} +1 -1
- package/dist/HyperdrivePg.d.mts +1 -1
- package/dist/index.d.mts +500 -311
- package/dist/index.mjs +434 -315
- package/package.json +8 -8
- package/src/AiGateway.ts +284 -0
- package/src/BrowserRendering.ts +286 -0
- package/src/CloudflareOtlp.ts +90 -386
- package/src/DurableObject.ts +5 -2
- package/src/Environment.ts +1 -1
- package/src/Images.ts +42 -24
- package/src/Kv.ts +6 -2
- package/src/QueueBinding.ts +19 -10
- package/src/R2.ts +47 -33
- package/src/Vectorize.ts +213 -0
- package/src/Worker.ts +2 -0
- package/src/WorkersAi.ts +239 -0
- package/src/Workflow.ts +2 -0
- package/src/WorkflowBinding.ts +15 -9
- package/src/index.ts +4 -0
- package/src/internal/Clock.ts +28 -0
package/src/CloudflareOtlp.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type * as Duration from "effect/Duration";
|
|
1
|
+
import { ConfigProvider, Effect, Layer, Option } from "effect";
|
|
3
2
|
import type * as Tracer from "effect/Tracer";
|
|
4
3
|
import { FetchHttpClient, type Headers } from "effect/unstable/http";
|
|
5
4
|
import {
|
|
6
|
-
Otlp,
|
|
7
5
|
OtlpLogger,
|
|
8
6
|
OtlpMetrics,
|
|
9
7
|
OtlpSerialization,
|
|
@@ -19,122 +17,52 @@ export type Signal = "logs" | "traces" | "metrics";
|
|
|
19
17
|
/** OTLP payload serialization used by the Effect OTLP exporters. */
|
|
20
18
|
export type Serialization = "json" | "protobuf";
|
|
21
19
|
|
|
22
|
-
/**
|
|
23
|
-
* Resolved OpenTelemetry settings used by the Cloudflare OTLP layers.
|
|
24
|
-
*
|
|
25
|
-
* The default {@link settingsLayer} reads the standard OpenTelemetry
|
|
26
|
-
* environment variable names from `WorkerEnvironment`. Applications that use
|
|
27
|
-
* different binding names can provide {@link CloudflareOtlpSettings} directly
|
|
28
|
-
* with a custom layer.
|
|
29
|
-
*/
|
|
30
|
-
export interface Settings {
|
|
31
|
-
/** Generic OTLP base endpoint, read from `OTEL_EXPORTER_OTLP_ENDPOINT`. */
|
|
32
|
-
readonly endpoint: Option.Option<string>;
|
|
33
|
-
/** Logs-specific OTLP endpoint, read from `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT`. */
|
|
34
|
-
readonly logsEndpoint: Option.Option<string>;
|
|
35
|
-
/** Traces-specific OTLP endpoint, read from `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`. */
|
|
36
|
-
readonly tracesEndpoint: Option.Option<string>;
|
|
37
|
-
/** Metrics-specific OTLP endpoint, read from `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`. */
|
|
38
|
-
readonly metricsEndpoint: Option.Option<string>;
|
|
39
|
-
/** Comma-separated OTLP headers, read from `OTEL_EXPORTER_OTLP_HEADERS`. */
|
|
40
|
-
readonly headers: Option.Option<string>;
|
|
41
|
-
/** Logs-specific OTLP headers, read from `OTEL_EXPORTER_OTLP_LOGS_HEADERS`. */
|
|
42
|
-
readonly logsHeaders: Option.Option<string>;
|
|
43
|
-
/** Traces-specific OTLP headers, read from `OTEL_EXPORTER_OTLP_TRACES_HEADERS`. */
|
|
44
|
-
readonly tracesHeaders: Option.Option<string>;
|
|
45
|
-
/** Metrics-specific OTLP headers, read from `OTEL_EXPORTER_OTLP_METRICS_HEADERS`. */
|
|
46
|
-
readonly metricsHeaders: Option.Option<string>;
|
|
47
|
-
/** Service name, read from `OTEL_SERVICE_NAME`. */
|
|
48
|
-
readonly serviceName: Option.Option<string>;
|
|
49
|
-
/** Service version, read from the effect-cf convenience alias `OTEL_SERVICE_VERSION`. */
|
|
50
|
-
readonly serviceVersion: Option.Option<string>;
|
|
51
|
-
/** Resource attributes, read from `OTEL_RESOURCE_ATTRIBUTES`. */
|
|
52
|
-
readonly resourceAttributes: Option.Option<Record<string, string>>;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Supported OpenTelemetry environment configuration for Cloudflare OTLP export.
|
|
57
|
-
*
|
|
58
|
-
* This config intentionally uses the conventional `OTEL_*` variable names so
|
|
59
|
-
* deployments can share the same settings used by other OpenTelemetry SDKs.
|
|
60
|
-
* It covers the OTLP HTTP settings supported by the Effect OTLP layers:
|
|
61
|
-
*
|
|
62
|
-
* - `OTEL_EXPORTER_OTLP_ENDPOINT`
|
|
63
|
-
* - `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT`
|
|
64
|
-
* - `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`
|
|
65
|
-
* - `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`
|
|
66
|
-
* - `OTEL_EXPORTER_OTLP_HEADERS`
|
|
67
|
-
* - `OTEL_EXPORTER_OTLP_LOGS_HEADERS`
|
|
68
|
-
* - `OTEL_EXPORTER_OTLP_TRACES_HEADERS`
|
|
69
|
-
* - `OTEL_EXPORTER_OTLP_METRICS_HEADERS`
|
|
70
|
-
* - `OTEL_SERVICE_NAME`
|
|
71
|
-
* - `OTEL_RESOURCE_ATTRIBUTES`
|
|
72
|
-
*
|
|
73
|
-
* `OTEL_SERVICE_VERSION` is also accepted as an effect-cf convenience alias.
|
|
74
|
-
* The standard OpenTelemetry way to configure service version is
|
|
75
|
-
* `OTEL_RESOURCE_ATTRIBUTES=service.version=...`.
|
|
76
|
-
*
|
|
77
|
-
* Consumers with non-standard binding names should map those names into
|
|
78
|
-
* {@link CloudflareOtlpSettings} rather than changing this config globally.
|
|
79
|
-
*/
|
|
80
|
-
export const settingsConfig = Config.all({
|
|
81
|
-
endpoint: Config.string("OTEL_EXPORTER_OTLP_ENDPOINT").pipe(Config.option),
|
|
82
|
-
logsEndpoint: Config.string("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT").pipe(Config.option),
|
|
83
|
-
tracesEndpoint: Config.string("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT").pipe(Config.option),
|
|
84
|
-
metricsEndpoint: Config.string("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT").pipe(Config.option),
|
|
85
|
-
headers: Config.string("OTEL_EXPORTER_OTLP_HEADERS").pipe(Config.option),
|
|
86
|
-
logsHeaders: Config.string("OTEL_EXPORTER_OTLP_LOGS_HEADERS").pipe(Config.option),
|
|
87
|
-
tracesHeaders: Config.string("OTEL_EXPORTER_OTLP_TRACES_HEADERS").pipe(Config.option),
|
|
88
|
-
metricsHeaders: Config.string("OTEL_EXPORTER_OTLP_METRICS_HEADERS").pipe(Config.option),
|
|
89
|
-
serviceName: Config.string("OTEL_SERVICE_NAME").pipe(Config.option),
|
|
90
|
-
serviceVersion: Config.string("OTEL_SERVICE_VERSION").pipe(Config.option),
|
|
91
|
-
resourceAttributes: Config.schema(
|
|
92
|
-
Config.Record(Schema.String, Schema.String),
|
|
93
|
-
"OTEL_RESOURCE_ATTRIBUTES",
|
|
94
|
-
).pipe(Config.option),
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Service carrying already-resolved Cloudflare OTLP settings.
|
|
99
|
-
*
|
|
100
|
-
* Provide this service directly when an application needs to map custom
|
|
101
|
-
* Cloudflare binding names, compute settings dynamically, or override settings
|
|
102
|
-
* in tests. When this service is present, the OTLP layers use it instead of
|
|
103
|
-
* reading {@link settingsConfig}.
|
|
104
|
-
*
|
|
105
|
-
* @example
|
|
106
|
-
* ```ts
|
|
107
|
-
* const CustomOtlpSettings = Layer.effect(
|
|
108
|
-
* CloudflareOtlp.CloudflareOtlpSettings,
|
|
109
|
-
* Config.all({
|
|
110
|
-
* ...CloudflareOtlp.settingsConfig,
|
|
111
|
-
* endpoint: Config.string("MY_OTLP_ENDPOINT").pipe(Config.option),
|
|
112
|
-
* }),
|
|
113
|
-
* ).pipe(Layer.provide(WorkerConfig.layer));
|
|
114
|
-
* ```
|
|
115
|
-
*/
|
|
116
|
-
export class CloudflareOtlpSettings extends Context.Service<CloudflareOtlpSettings, Settings>()(
|
|
117
|
-
"effect-cf/CloudflareOtlpSettings",
|
|
118
|
-
) {}
|
|
119
|
-
|
|
120
20
|
/** Resource metadata shared by Worker and Durable Object OTLP layers. */
|
|
121
21
|
export interface ResourceOptions {
|
|
122
|
-
/** Explicit service name.
|
|
22
|
+
/** Explicit service name. OTEL resource environment variables take precedence. */
|
|
123
23
|
readonly serviceName?: string;
|
|
124
|
-
/** Explicit service version.
|
|
24
|
+
/** Explicit service version. OTEL resource environment variables take precedence. */
|
|
125
25
|
readonly serviceVersion?: string;
|
|
126
|
-
/** Additional resource attributes attached to
|
|
26
|
+
/** Additional resource attributes attached to exported telemetry. */
|
|
127
27
|
readonly attributes?: Record<string, unknown>;
|
|
128
28
|
}
|
|
129
29
|
|
|
30
|
+
/** Options shared by all Cloudflare OTLP telemetry layers. */
|
|
31
|
+
export interface LayerOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Telemetry signals to export. Defaults to logs, traces, and metrics.
|
|
34
|
+
*
|
|
35
|
+
* Each selected signal is still controlled by standard OTEL exporter config,
|
|
36
|
+
* such as `OTEL_TRACES_EXPORTER=otlp`.
|
|
37
|
+
*/
|
|
38
|
+
readonly signals?: ReadonlyArray<Signal>;
|
|
39
|
+
/** OTLP payload serialization. Defaults to protobuf. */
|
|
40
|
+
readonly serialization?: Serialization;
|
|
41
|
+
/** Resource metadata forwarded to Effect's OTLP resource resolver. */
|
|
42
|
+
readonly resource?: ResourceOptions;
|
|
43
|
+
/**
|
|
44
|
+
* Explicit headers for every selected signal.
|
|
45
|
+
*
|
|
46
|
+
* When omitted, Effect reads `OTEL_EXPORTER_OTLP_HEADERS` and the
|
|
47
|
+
* signal-specific `OTEL_EXPORTER_OTLP_*_HEADERS` variables.
|
|
48
|
+
*/
|
|
49
|
+
readonly headers?: Headers.Input;
|
|
50
|
+
/** Exclude log records emitted for spans when exporting logs. */
|
|
51
|
+
readonly loggerExcludeLogSpans?: boolean;
|
|
52
|
+
/** Merge the OTLP logger with existing loggers instead of replacing them. */
|
|
53
|
+
readonly loggerMergeWithExisting?: boolean;
|
|
54
|
+
/** Custom trace context lookup used by the Effect OTLP tracer. */
|
|
55
|
+
readonly tracerContext?: <X>(primitive: Tracer.EffectPrimitive<X>, span: Tracer.AnySpan) => X;
|
|
56
|
+
}
|
|
57
|
+
|
|
130
58
|
/** Resource metadata specific to Cloudflare Workers. */
|
|
131
|
-
export interface
|
|
59
|
+
export interface WorkerLayerOptions extends LayerOptions {
|
|
132
60
|
/** Cloudflare Worker name to attach as `cloudflare.worker.name`. */
|
|
133
61
|
readonly workerName?: string;
|
|
134
62
|
}
|
|
135
63
|
|
|
136
64
|
/** Resource metadata specific to Cloudflare Durable Objects. */
|
|
137
|
-
export interface
|
|
65
|
+
export interface DurableObjectLayerOptions extends LayerOptions {
|
|
138
66
|
/** Durable Object class name to attach as `cloudflare.durable_object.class`. */
|
|
139
67
|
readonly className?: string;
|
|
140
68
|
/**
|
|
@@ -145,76 +73,15 @@ export interface DurableObjectResourceOptions extends ResourceOptions {
|
|
|
145
73
|
readonly includeObjectId?: boolean;
|
|
146
74
|
}
|
|
147
75
|
|
|
148
|
-
/** Options shared by all Cloudflare OTLP telemetry layers. */
|
|
149
|
-
export interface LayerOptions extends ResourceOptions {
|
|
150
|
-
/** Telemetry signals to export. Defaults to logs, traces, and metrics. */
|
|
151
|
-
readonly signals?: ReadonlyArray<Signal>;
|
|
152
|
-
/** OTLP payload serialization. Defaults to protobuf. */
|
|
153
|
-
readonly serialization?: Serialization;
|
|
154
|
-
/** Maximum batch size for log and trace exporters. */
|
|
155
|
-
readonly maxBatchSize?: number;
|
|
156
|
-
/** Maximum time to wait for exporter shutdown/flush when a scope closes. */
|
|
157
|
-
readonly shutdownTimeout?: Duration.Input;
|
|
158
|
-
/** Export interval for the logs exporter. */
|
|
159
|
-
readonly loggerExportInterval?: Duration.Input;
|
|
160
|
-
/** Exclude log records emitted for spans when exporting logs. */
|
|
161
|
-
readonly loggerExcludeLogSpans?: boolean;
|
|
162
|
-
/** Merge the OTLP logger with existing loggers instead of replacing them. */
|
|
163
|
-
readonly loggerMergeWithExisting?: boolean;
|
|
164
|
-
/** Export interval for the traces exporter. */
|
|
165
|
-
readonly tracerExportInterval?: Duration.Input;
|
|
166
|
-
/** Custom trace context lookup used by the Effect OTLP tracer. */
|
|
167
|
-
readonly tracerContext?: <X>(primitive: Tracer.EffectPrimitive<X>, span: Tracer.AnySpan) => X;
|
|
168
|
-
/** Export interval for the metrics exporter. */
|
|
169
|
-
readonly metricsExportInterval?: Duration.Input;
|
|
170
|
-
/** Aggregation temporality for exported metrics. */
|
|
171
|
-
readonly metricsTemporality?: OtlpMetrics.AggregationTemporality;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
76
|
const allSignals: ReadonlyArray<Signal> = ["logs", "traces", "metrics"];
|
|
175
77
|
|
|
176
|
-
const
|
|
177
|
-
Option.isSome(option) ? option.value : undefined;
|
|
178
|
-
|
|
179
|
-
const nonEmptyStringOption = (option: Option.Option<string>): Option.Option<string> =>
|
|
180
|
-
Option.flatMap(option, (value) => {
|
|
181
|
-
const trimmed = value.trim();
|
|
182
|
-
return trimmed === "" ? Option.none() : Option.some(trimmed);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
const nonEmptyRecordOption = (
|
|
186
|
-
option: Option.Option<Record<string, string>>,
|
|
187
|
-
): Option.Option<Record<string, string>> =>
|
|
188
|
-
Option.filter(option, (value) => Object.keys(value).length > 0);
|
|
189
|
-
|
|
190
|
-
const normalizeSettings = (settings: Settings): Settings => ({
|
|
191
|
-
endpoint: nonEmptyStringOption(settings.endpoint),
|
|
192
|
-
logsEndpoint: nonEmptyStringOption(settings.logsEndpoint),
|
|
193
|
-
tracesEndpoint: nonEmptyStringOption(settings.tracesEndpoint),
|
|
194
|
-
metricsEndpoint: nonEmptyStringOption(settings.metricsEndpoint),
|
|
195
|
-
headers: nonEmptyStringOption(settings.headers),
|
|
196
|
-
logsHeaders: nonEmptyStringOption(settings.logsHeaders),
|
|
197
|
-
tracesHeaders: nonEmptyStringOption(settings.tracesHeaders),
|
|
198
|
-
metricsHeaders: nonEmptyStringOption(settings.metricsHeaders),
|
|
199
|
-
serviceName: nonEmptyStringOption(settings.serviceName),
|
|
200
|
-
serviceVersion: nonEmptyStringOption(settings.serviceVersion),
|
|
201
|
-
resourceAttributes: nonEmptyRecordOption(settings.resourceAttributes),
|
|
202
|
-
});
|
|
78
|
+
const emptyConfigProvider = ConfigProvider.fromUnknown({});
|
|
203
79
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
* Cloudflare-backed Effect `ConfigProvider`, so `settingsConfig` reads from
|
|
210
|
-
* Worker vars/secrets instead of Node process environment variables.
|
|
211
|
-
*/
|
|
212
|
-
export const settingsLayer: Layer.Layer<
|
|
213
|
-
CloudflareOtlpSettings,
|
|
214
|
-
Config.ConfigError,
|
|
215
|
-
WorkerEnvironment
|
|
216
|
-
> = Layer.effect(CloudflareOtlpSettings, settingsConfig.pipe(Config.map(normalizeSettings))).pipe(
|
|
217
|
-
Layer.provide(WorkerConfig.layer),
|
|
80
|
+
const cloudflareConfigProviderLayer = ConfigProvider.layerAdd(
|
|
81
|
+
Effect.map(Effect.serviceOption(WorkerEnvironment), (env) =>
|
|
82
|
+
Option.isSome(env) ? WorkerConfig.providerFromEnv(env.value) : emptyConfigProvider,
|
|
83
|
+
),
|
|
84
|
+
{ asPrimary: true },
|
|
218
85
|
);
|
|
219
86
|
|
|
220
87
|
const selectedSignals = (signals: ReadonlyArray<Signal> | undefined): ReadonlySet<Signal> =>
|
|
@@ -230,109 +97,24 @@ const withDefinedAttributes = (attributes: Record<string, unknown>): Record<stri
|
|
|
230
97
|
return out;
|
|
231
98
|
};
|
|
232
99
|
|
|
233
|
-
const parseHeaders = (input: Option.Option<string>): Record<string, string> | undefined => {
|
|
234
|
-
if (Option.isNone(input) || input.value.trim() === "") {
|
|
235
|
-
return undefined;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const headers: Record<string, string> = {};
|
|
239
|
-
for (const part of input.value.split(",")) {
|
|
240
|
-
const index = part.indexOf("=");
|
|
241
|
-
if (index <= 0) {
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const key = part.slice(0, index).trim();
|
|
246
|
-
const value = part.slice(index + 1).trim();
|
|
247
|
-
if (key !== "") {
|
|
248
|
-
headers[decodeHeaderComponent(key)] = decodeHeaderComponent(value);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return headers;
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
const mergeHeaders = (
|
|
256
|
-
common: Option.Option<string>,
|
|
257
|
-
signalSpecific: Option.Option<string>,
|
|
258
|
-
): Headers.Input | undefined => {
|
|
259
|
-
const commonHeaders = parseHeaders(common);
|
|
260
|
-
const signalHeaders = parseHeaders(signalSpecific);
|
|
261
|
-
if (commonHeaders === undefined && signalHeaders === undefined) {
|
|
262
|
-
return undefined;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return {
|
|
266
|
-
...commonHeaders,
|
|
267
|
-
...signalHeaders,
|
|
268
|
-
};
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
const decodeHeaderComponent = (value: string): string => {
|
|
272
|
-
try {
|
|
273
|
-
return decodeURIComponent(value);
|
|
274
|
-
} catch {
|
|
275
|
-
return value;
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
const appendOtlpPath = (baseUrl: string, path: "/v1/logs" | "/v1/traces" | "/v1/metrics") =>
|
|
280
|
-
`${baseUrl.replace(/\/+$/, "")}${path}`;
|
|
281
|
-
|
|
282
100
|
const makeResource = (
|
|
283
|
-
settings: Settings,
|
|
284
101
|
options: LayerOptions,
|
|
285
102
|
runtimeAttributes: Record<string, unknown>,
|
|
286
|
-
) => ({
|
|
287
|
-
serviceName: options.serviceName
|
|
288
|
-
serviceVersion: options.serviceVersion
|
|
103
|
+
): ResourceOptions => ({
|
|
104
|
+
serviceName: options.resource?.serviceName,
|
|
105
|
+
serviceVersion: options.resource?.serviceVersion,
|
|
289
106
|
attributes: {
|
|
290
|
-
...optionToUndefined(settings.resourceAttributes),
|
|
291
107
|
...withDefinedAttributes(runtimeAttributes),
|
|
292
|
-
...options.attributes,
|
|
108
|
+
...options.resource?.attributes,
|
|
293
109
|
},
|
|
294
110
|
});
|
|
295
111
|
|
|
296
|
-
const commonOtlpOptions = (
|
|
297
|
-
settings: Settings,
|
|
298
|
-
options: LayerOptions,
|
|
299
|
-
runtimeAttributes: Record<string, unknown>,
|
|
300
|
-
) => ({
|
|
301
|
-
resource: makeResource(settings, options, runtimeAttributes),
|
|
302
|
-
maxBatchSize: options.maxBatchSize,
|
|
303
|
-
loggerExportInterval: options.loggerExportInterval,
|
|
304
|
-
loggerExcludeLogSpans: options.loggerExcludeLogSpans,
|
|
305
|
-
loggerMergeWithExisting: options.loggerMergeWithExisting,
|
|
306
|
-
tracerExportInterval: options.tracerExportInterval,
|
|
307
|
-
tracerContext: options.tracerContext,
|
|
308
|
-
metricsExportInterval: options.metricsExportInterval,
|
|
309
|
-
metricsTemporality: options.metricsTemporality,
|
|
310
|
-
shutdownTimeout: options.shutdownTimeout,
|
|
311
|
-
});
|
|
312
|
-
|
|
313
112
|
const serializationLayer = (serialization: Serialization | undefined) =>
|
|
314
113
|
serialization === "json" ? OtlpSerialization.layerJson : OtlpSerialization.layerProtobuf;
|
|
315
114
|
|
|
316
|
-
|
|
317
|
-
const settings = yield* Effect.serviceOption(CloudflareOtlpSettings);
|
|
318
|
-
if (Option.isSome(settings)) {
|
|
319
|
-
return normalizeSettings(settings.value);
|
|
320
|
-
}
|
|
115
|
+
type SignalLayer = ReturnType<typeof OtlpLogger.layerFromConfig>;
|
|
321
116
|
|
|
322
|
-
|
|
323
|
-
if (Option.isSome(env)) {
|
|
324
|
-
const settings = yield* settingsConfig.pipe(
|
|
325
|
-
Effect.provide(ConfigProvider.layer(Effect.succeed(WorkerConfig.providerFromEnv(env.value)))),
|
|
326
|
-
);
|
|
327
|
-
return normalizeSettings(settings);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return normalizeSettings(yield* settingsConfig);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
const mergeSignalLayers = (
|
|
334
|
-
layers: ReadonlyArray<Layer.Layer<never, never, never>>,
|
|
335
|
-
): Layer.Layer<never, never, never> => {
|
|
117
|
+
const mergeSignalLayers = (layers: ReadonlyArray<SignalLayer>): SignalLayer => {
|
|
336
118
|
if (layers.length === 0) {
|
|
337
119
|
return Layer.empty;
|
|
338
120
|
}
|
|
@@ -344,136 +126,58 @@ const mergeSignalLayers = (
|
|
|
344
126
|
return merged;
|
|
345
127
|
};
|
|
346
128
|
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
const makeLayerFromSettings = (
|
|
129
|
+
const makeLayer = (
|
|
350
130
|
options: LayerOptions = {},
|
|
351
131
|
runtimeAttributes: Record<string, unknown> = {},
|
|
352
|
-
): Layer.Layer<never, never, never> =>
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
if (
|
|
369
|
-
endpoint !== undefined &&
|
|
370
|
-
!hasSpecificEndpoint &&
|
|
371
|
-
!hasSpecificHeaders &&
|
|
372
|
-
signals.size === 3
|
|
373
|
-
) {
|
|
374
|
-
return Otlp.layer({
|
|
375
|
-
baseUrl: endpoint,
|
|
376
|
-
headers: parseHeaders(settings.headers),
|
|
377
|
-
...common,
|
|
378
|
-
}).pipe(
|
|
379
|
-
Layer.provide(serializationLayer(options.serialization)),
|
|
380
|
-
Layer.provide(FetchHttpClient.layer),
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const layers: Array<Layer.Layer<never, never, never>> = [];
|
|
385
|
-
if (signals.has("logs")) {
|
|
386
|
-
const url =
|
|
387
|
-
logsEndpoint ??
|
|
388
|
-
(endpoint === undefined ? undefined : appendOtlpPath(endpoint, "/v1/logs"));
|
|
389
|
-
if (url !== undefined) {
|
|
390
|
-
layers.push(
|
|
391
|
-
OtlpLogger.layer({
|
|
392
|
-
url,
|
|
393
|
-
resource: common.resource,
|
|
394
|
-
headers: mergeHeaders(settings.headers, settings.logsHeaders),
|
|
395
|
-
exportInterval: common.loggerExportInterval,
|
|
396
|
-
maxBatchSize: common.maxBatchSize,
|
|
397
|
-
shutdownTimeout: common.shutdownTimeout,
|
|
398
|
-
excludeLogSpans: common.loggerExcludeLogSpans,
|
|
399
|
-
mergeWithExisting: common.loggerMergeWithExisting,
|
|
400
|
-
}).pipe(
|
|
401
|
-
Layer.provide(serializationLayer(options.serialization)),
|
|
402
|
-
Layer.provide(FetchHttpClient.layer),
|
|
403
|
-
),
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
132
|
+
): Layer.Layer<never, never, never> => {
|
|
133
|
+
const signals = selectedSignals(options.signals);
|
|
134
|
+
const resource = makeResource(options, runtimeAttributes);
|
|
135
|
+
const layers: Array<SignalLayer> = [];
|
|
136
|
+
|
|
137
|
+
if (signals.has("logs")) {
|
|
138
|
+
layers.push(
|
|
139
|
+
OtlpLogger.layerFromConfig({
|
|
140
|
+
resource,
|
|
141
|
+
headers: options.headers,
|
|
142
|
+
excludeLogSpans: options.loggerExcludeLogSpans,
|
|
143
|
+
mergeWithExisting: options.loggerMergeWithExisting,
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
146
|
+
}
|
|
407
147
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
headers: mergeHeaders(settings.headers, settings.tracesHeaders),
|
|
418
|
-
exportInterval: common.tracerExportInterval,
|
|
419
|
-
maxBatchSize: common.maxBatchSize,
|
|
420
|
-
context: common.tracerContext,
|
|
421
|
-
shutdownTimeout: common.shutdownTimeout,
|
|
422
|
-
}).pipe(
|
|
423
|
-
Layer.provide(serializationLayer(options.serialization)),
|
|
424
|
-
Layer.provide(FetchHttpClient.layer),
|
|
425
|
-
),
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
148
|
+
if (signals.has("traces")) {
|
|
149
|
+
layers.push(
|
|
150
|
+
OtlpTracer.layerFromConfig({
|
|
151
|
+
resource,
|
|
152
|
+
headers: options.headers,
|
|
153
|
+
context: options.tracerContext,
|
|
154
|
+
}),
|
|
155
|
+
);
|
|
156
|
+
}
|
|
429
157
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
resource: common.resource,
|
|
439
|
-
headers: mergeHeaders(settings.headers, settings.metricsHeaders),
|
|
440
|
-
exportInterval: common.metricsExportInterval,
|
|
441
|
-
shutdownTimeout: common.shutdownTimeout,
|
|
442
|
-
temporality: common.metricsTemporality,
|
|
443
|
-
}).pipe(
|
|
444
|
-
Layer.provide(serializationLayer(options.serialization)),
|
|
445
|
-
Layer.provide(FetchHttpClient.layer),
|
|
446
|
-
),
|
|
447
|
-
);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
158
|
+
if (signals.has("metrics")) {
|
|
159
|
+
layers.push(
|
|
160
|
+
OtlpMetrics.layerFromConfig({
|
|
161
|
+
resource,
|
|
162
|
+
headers: options.headers,
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
450
166
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
error: String(error),
|
|
456
|
-
}).pipe(Effect.as(emptyLayer)),
|
|
457
|
-
),
|
|
458
|
-
),
|
|
167
|
+
return mergeSignalLayers(layers).pipe(
|
|
168
|
+
Layer.provide(cloudflareConfigProviderLayer),
|
|
169
|
+
Layer.provide(serializationLayer(options.serialization)),
|
|
170
|
+
Layer.provide(FetchHttpClient.layer),
|
|
459
171
|
);
|
|
460
|
-
|
|
461
|
-
const makeLayer = (
|
|
462
|
-
options: LayerOptions = {},
|
|
463
|
-
runtimeAttributes: Record<string, unknown> = {},
|
|
464
|
-
): Layer.Layer<never, never, never> => makeLayerFromSettings(options, runtimeAttributes);
|
|
172
|
+
};
|
|
465
173
|
|
|
466
174
|
/**
|
|
467
175
|
* Base OTLP telemetry layer for Cloudflare-compatible runtimes.
|
|
468
176
|
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
* When only `OTEL_EXPORTER_OTLP_ENDPOINT` is configured, per-signal paths are
|
|
474
|
-
* derived by appending `/v1/logs`, `/v1/traces`, and `/v1/metrics`. Signal
|
|
475
|
-
* specific endpoints are used as-is, matching the OpenTelemetry OTLP exporter
|
|
476
|
-
* convention.
|
|
177
|
+
* Standard OpenTelemetry environment variables are resolved by Effect's OTLP
|
|
178
|
+
* layers. In Cloudflare Workers and Durable Objects, the current `env` object is
|
|
179
|
+
* installed as the primary `ConfigProvider`; outside Cloudflare, the ambient
|
|
180
|
+
* Effect `ConfigProvider` remains the fallback.
|
|
477
181
|
*/
|
|
478
182
|
export const layer = (options: LayerOptions = {}) => makeLayer(options);
|
|
479
183
|
|
|
@@ -491,7 +195,7 @@ export const layerProtobuf = (options: Omit<LayerOptions, "serialization"> = {})
|
|
|
491
195
|
* Provide this layer at runtime scope for long-lived metrics aggregation, or at
|
|
492
196
|
* handler scope when traces/logs should flush as the Cloudflare event finishes.
|
|
493
197
|
*/
|
|
494
|
-
export const workerLayer = (options:
|
|
198
|
+
export const workerLayer = (options: WorkerLayerOptions = {}) =>
|
|
495
199
|
makeLayer(options, {
|
|
496
200
|
"cloudflare.resource_type": "worker",
|
|
497
201
|
"cloudflare.worker.name": options.workerName,
|
|
@@ -504,7 +208,7 @@ export const workerLayer = (options: LayerOptions & WorkerResourceOptions = {})
|
|
|
504
208
|
* Durable Object id. Prefer leaving `includeObjectId` disabled unless the
|
|
505
209
|
* backend can tolerate high-cardinality resource attributes.
|
|
506
210
|
*/
|
|
507
|
-
export const durableObjectLayer = (options:
|
|
211
|
+
export const durableObjectLayer = (options: DurableObjectLayerOptions = {}) =>
|
|
508
212
|
Layer.unwrap(
|
|
509
213
|
Effect.map(DurableObjectState, (state) =>
|
|
510
214
|
makeLayer(options, {
|
package/src/DurableObject.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { DurableObject as CloudflareDurableObject } from "cloudflare:workers";
|
|
2
|
-
import { Effect, Layer, ManagedRuntime, type Context, type Scope } from "effect";
|
|
2
|
+
import { ConfigProvider, Effect, Layer, ManagedRuntime, type Context, type Scope } from "effect";
|
|
3
3
|
import type { Schema as S } from "effect";
|
|
4
4
|
|
|
5
5
|
import { NativeRequest } from "./Worker";
|
|
6
|
-
import { WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
6
|
+
import { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
7
7
|
import { DurableObjectState, fromDurableObjectState } from "./DurableObjectState";
|
|
8
8
|
import { fromWebSocket, type DurableWebSocket } from "./DurableObjectWebSocket";
|
|
9
9
|
import type * as Binding from "./Binding";
|
|
10
10
|
import * as DurableObjectDefinition from "./DurableObjectDefinition";
|
|
11
11
|
import type * as DurableObjectNamespace from "./DurableObjectNamespace";
|
|
12
12
|
import type * as Rpc from "./Rpc";
|
|
13
|
+
import * as CloudflareClock from "./internal/Clock";
|
|
13
14
|
import * as Entrypoint from "./internal/Entrypoint";
|
|
14
15
|
|
|
15
16
|
const reservedMethodNames = new Set<string>([
|
|
@@ -161,6 +162,8 @@ export const make = <
|
|
|
161
162
|
super(state, env);
|
|
162
163
|
|
|
163
164
|
const services = Layer.mergeAll(
|
|
165
|
+
CloudflareClock.layer,
|
|
166
|
+
ConfigProvider.layer(Effect.succeed(WorkerConfig.providerFromEnv(env))),
|
|
164
167
|
Layer.succeed(DurableObjectState, fromDurableObjectState(state)),
|
|
165
168
|
Layer.succeed(WorkerEnvironment, env),
|
|
166
169
|
);
|
package/src/Environment.ts
CHANGED
|
@@ -25,7 +25,7 @@ type ScalarConfigKey = Extract<
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Effect `Config` helpers for scalar Cloudflare vars and secrets declared on
|
|
28
|
-
* `Cloudflare.Env
|
|
28
|
+
* `Cloudflare.Env`.
|
|
29
29
|
*
|
|
30
30
|
* Users still author their app config explicitly with Effect `Config`:
|
|
31
31
|
*
|