@inference-net/otel-cf-workers 2.1.1 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -177,6 +177,36 @@ export declare interface InstrumentationOptions {
177
177
 
178
178
  export declare function instrumentDO(doClass: DOClass, config: ConfigurationOption): DOClass;
179
179
 
180
+ /**
181
+ * Base class for instrumented Durable Objects with TypeScript support for injected methods.
182
+ * Extend this instead of DurableObject to automatically get typing for `this.instrument()`.
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * import { InstrumentedDurableObject } from '@inference-net/otel-cf-workers'
187
+ *
188
+ * export class MyDO extends InstrumentedDurableObject<Env> {
189
+ * async someMethod() {
190
+ * // this.instrument is available with full TypeScript support!
191
+ * await this.instrument('custom-operation', async () => {
192
+ * // Your code here
193
+ * })
194
+ * }
195
+ * }
196
+ * ```
197
+ */
198
+ export declare class InstrumentedDurableObject<Env = unknown> extends DurableObject_2<Env> {
199
+ instrument: InstrumentMethod;
200
+ }
201
+
202
+ export declare type InstrumentMethod = {
203
+ <T>(name: string, fn: () => Promise<T>): Promise<T>;
204
+ <T>(name: string, options: InstrumentOptions, fn: () => Promise<T>): Promise<T>;
205
+ };
206
+
207
+ export declare interface InstrumentOptions extends Omit<SpanOptions, 'kind'> {
208
+ }
209
+
180
210
  export declare function isAlarm(trigger: Trigger): trigger is 'do-alarm';
181
211
 
182
212
  export declare const isHeadSampled: TailSampleFn;
@@ -195,9 +225,16 @@ export declare interface LocalTrace {
195
225
  readonly spans: ReadableSpan[];
196
226
  }
197
227
 
198
- export declare interface LogAttributes {
199
- [key: string]: any;
200
- }
228
+ /**
229
+ * Log attributes must be a plain object with string keys.
230
+ * This type explicitly excludes strings to prevent accidental argument swapping.
231
+ */
232
+ export declare type LogAttributes = {
233
+ [key: string]: unknown;
234
+ } & {
235
+ length?: never;
236
+ substring?: never;
237
+ };
201
238
 
202
239
  export declare type LogBody = string | Record<string, any>;
203
240
 
@@ -207,7 +244,7 @@ export declare interface Logger {
207
244
  debug(message: string, attributes?: LogAttributes): void;
208
245
  info(message: string, attributes?: LogAttributes): void;
209
246
  warn(message: string, attributes?: LogAttributes): void;
210
- error(message: string | Error, attributes?: LogAttributes): void;
247
+ error(message: string, attributes?: LogAttributes): void;
211
248
  fatal(message: string, attributes?: LogAttributes): void;
212
249
  forceFlush(): Promise<void>;
213
250
  child(attributes: LogAttributes): Logger;
@@ -531,7 +568,7 @@ export declare class WorkerLogger implements Logger {
531
568
  debug(message: string, attributes?: LogAttributes): void;
532
569
  info(message: string, attributes?: LogAttributes): void;
533
570
  warn(message: string, attributes?: LogAttributes): void;
534
- error(message: string | Error, attributes?: LogAttributes): void;
571
+ error(message: string, attributes?: LogAttributes): void;
535
572
  fatal(message: string, attributes?: LogAttributes): void;
536
573
  forceFlush(): Promise<void>;
537
574
  /**
package/dist/index.js CHANGED
@@ -96,7 +96,7 @@ function passthroughGet(target, prop, thisArg) {
96
96
  }
97
97
  }
98
98
 
99
- const PACKAGE_VERSION = "2.1.1";
99
+ const PACKAGE_VERSION = "2.2.1";
100
100
  const PACKAGE_NAME = "@inference-net/otel-cf-workers";
101
101
  const ATTR_CLOUDFLARE_COLO = "cloudflare.colo";
102
102
  const ATTR_CLOUDFLARE_RAY_ID = "cloudflare.ray_id";
@@ -1022,6 +1022,53 @@ class WorkerTracerProvider {
1022
1022
  function millisToHr(millis) {
1023
1023
  return [Math.trunc(millis / 1e3), millis % 1e3 * 1e6];
1024
1024
  }
1025
+ function flattenError(error, prefix) {
1026
+ const result = {};
1027
+ result[`${prefix}.type`] = error.name;
1028
+ result[`${prefix}.message`] = error.message;
1029
+ if (error.stack) {
1030
+ result[`${prefix}.stacktrace`] = error.stack;
1031
+ }
1032
+ if ("cause" in error && error.cause) {
1033
+ if (error.cause instanceof Error) {
1034
+ Object.assign(result, flattenError(error.cause, `${prefix}.cause`));
1035
+ } else {
1036
+ result[`${prefix}.cause`] = String(error.cause);
1037
+ }
1038
+ }
1039
+ return result;
1040
+ }
1041
+ function flattenAttributes(attrs, prefix = "") {
1042
+ const result = {};
1043
+ for (const [key, value] of Object.entries(attrs)) {
1044
+ const fullKey = prefix ? `${prefix}.${key}` : key;
1045
+ if (value === null || value === void 0) {
1046
+ continue;
1047
+ } else if (value instanceof Error) {
1048
+ Object.assign(result, flattenError(value, fullKey));
1049
+ } else if (Array.isArray(value)) {
1050
+ const hasObjects = value.some((item) => item !== null && typeof item === "object" && !Array.isArray(item));
1051
+ if (hasObjects) {
1052
+ value.forEach((item, index) => {
1053
+ if (item instanceof Error) {
1054
+ Object.assign(result, flattenError(item, `${fullKey}.${index}`));
1055
+ } else if (item !== null && typeof item === "object" && !Array.isArray(item)) {
1056
+ Object.assign(result, flattenAttributes(item, `${fullKey}.${index}`));
1057
+ } else if (item !== null && item !== void 0) {
1058
+ result[`${fullKey}.${index}`] = item;
1059
+ }
1060
+ });
1061
+ } else {
1062
+ result[fullKey] = value;
1063
+ }
1064
+ } else if (typeof value === "object") {
1065
+ Object.assign(result, flattenAttributes(value, fullKey));
1066
+ } else {
1067
+ result[fullKey] = value;
1068
+ }
1069
+ }
1070
+ return result;
1071
+ }
1025
1072
  function getHrTime(input) {
1026
1073
  const now = Date.now();
1027
1074
  if (!input) {
@@ -1055,7 +1102,8 @@ class LogRecordImpl {
1055
1102
  this.severityNumber = init.severityNumber;
1056
1103
  this.severityText = init.severityText;
1057
1104
  this.body = init.body;
1058
- this.attributes = sanitizeAttributes(init.attributes || {});
1105
+ const flattenedAttributes = flattenAttributes(init.attributes || {});
1106
+ this.attributes = sanitizeAttributes(flattenedAttributes);
1059
1107
  this.resource = init.resource;
1060
1108
  this.instrumentationScope = init.instrumentationScope || {
1061
1109
  name: "@inference-net/otel-cf-workers"
@@ -1165,28 +1213,31 @@ class WorkerLogger {
1165
1213
  }
1166
1214
  error(message, attributes) {
1167
1215
  if (!this.shouldEmit(SEVERITY_NUMBERS.ERROR)) return;
1168
- let body;
1169
- let attrs = { ...attributes };
1170
- if (message instanceof Error) {
1171
- body = message.message;
1172
- attrs = {
1173
- ...attrs,
1174
- "exception.type": message.name,
1175
- "exception.message": message.message,
1176
- "exception.stacktrace": message.stack
1177
- };
1178
- } else {
1179
- body = message;
1216
+ const exception = attributes?.["error"] instanceof Error ? attributes["error"] : void 0;
1217
+ const activeSpan = trace.getActiveSpan();
1218
+ if (activeSpan) {
1219
+ activeSpan.setStatus({ code: SpanStatusCode.ERROR, message });
1220
+ if (exception) {
1221
+ activeSpan.recordException(exception);
1222
+ }
1180
1223
  }
1181
1224
  this.emit({
1182
1225
  severityNumber: SEVERITY_NUMBERS.ERROR,
1183
1226
  severityText: "ERROR",
1184
- body,
1185
- attributes: attrs
1227
+ body: message,
1228
+ attributes
1186
1229
  });
1187
1230
  }
1188
1231
  fatal(message, attributes) {
1189
1232
  if (!this.shouldEmit(SEVERITY_NUMBERS.FATAL)) return;
1233
+ const exception = attributes?.["error"] instanceof Error ? attributes["error"] : void 0;
1234
+ const activeSpan = trace.getActiveSpan();
1235
+ if (activeSpan) {
1236
+ activeSpan.setStatus({ code: SpanStatusCode.ERROR, message });
1237
+ if (exception) {
1238
+ activeSpan.recordException(exception);
1239
+ }
1240
+ }
1190
1241
  this.emit({
1191
1242
  severityNumber: SEVERITY_NUMBERS.FATAL,
1192
1243
  severityText: "FATAL",
@@ -3033,6 +3084,49 @@ function extractAndRemoveRpcContext(args) {
3033
3084
  return [void 0, args];
3034
3085
  }
3035
3086
 
3087
+ class InstrumentedDurableObject extends DurableObject {
3088
+ }
3089
+ function createInstrumentMethod(state, initialiser, env) {
3090
+ return async function instrument(name, optionsOrFn, maybeFn) {
3091
+ const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
3092
+ const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
3093
+ const tracer = trace.getTracer("DO custom span");
3094
+ const spanOptions = {
3095
+ ...options,
3096
+ attributes: {
3097
+ "do.id": state.id.toString(),
3098
+ ...state.id.name && { "do.name": state.id.name },
3099
+ ...options.attributes
3100
+ }
3101
+ };
3102
+ const executeWithConfig = async () => {
3103
+ const currentContext = context.active();
3104
+ const existingConfig = getActiveConfig();
3105
+ let context$1 = currentContext;
3106
+ if (!existingConfig) {
3107
+ const config = initialiser(env, { id: state.id.toString(), name: state.id.name });
3108
+ context$1 = setConfig(config, currentContext);
3109
+ }
3110
+ return context.with(
3111
+ context$1,
3112
+ () => tracer.startActiveSpan(name, spanOptions, async (span) => {
3113
+ try {
3114
+ const result = await fn();
3115
+ span.setStatus({ code: SpanStatusCode.OK });
3116
+ return result;
3117
+ } catch (error) {
3118
+ span.recordException(error);
3119
+ span.setStatus({ code: SpanStatusCode.ERROR });
3120
+ throw error;
3121
+ } finally {
3122
+ span.end();
3123
+ }
3124
+ })
3125
+ );
3126
+ };
3127
+ return executeWithConfig();
3128
+ };
3129
+ }
3036
3130
  function instrumentRpcMethod(method, methodName, nsName, stubId) {
3037
3131
  const tracer = trace.getTracer("do_rpc_client");
3038
3132
  const rpcHandler = {
@@ -3277,14 +3371,10 @@ function instrumentRpcHandlerMethod(fn, methodName, initialiser, env, id) {
3277
3371
  };
3278
3372
  return wrap(fn, fnHandler);
3279
3373
  }
3280
- function instrumentDurableObject(doObj, initialiser, env, state, classStyle) {
3374
+ function instrumentDurableObject(doObj, initialiser, env, state) {
3281
3375
  const objHandler = {
3282
3376
  get(target, prop) {
3283
- if (classStyle && prop === "ctx") {
3284
- return state;
3285
- } else if (classStyle && prop === "env") {
3286
- return env;
3287
- } else if (prop === "fetch") {
3377
+ if (prop === "fetch") {
3288
3378
  const fetchFn = Reflect.get(target, prop);
3289
3379
  return instrumentFetchFn(fetchFn, initialiser, env, state.id);
3290
3380
  } else if (prop === "alarm") {
@@ -3313,16 +3403,28 @@ function instrumentDOClass(doClass, initialiser) {
3313
3403
  const context$1 = setConfig(constructorConfig);
3314
3404
  const state = instrumentState(orig_state);
3315
3405
  const env = instrumentEnv(orig_env);
3316
- const classStyle = doClass.prototype instanceof DurableObject;
3317
- const createDO = () => {
3318
- if (classStyle) {
3319
- return new target(orig_state, orig_env);
3320
- } else {
3321
- return new target(state, env);
3322
- }
3323
- };
3406
+ const createDO = () => new target(orig_state, orig_env);
3324
3407
  const doObj = context.with(context$1, createDO);
3325
- return instrumentDurableObject(doObj, initialiser, env, state, classStyle);
3408
+ const instrumentMethod = createInstrumentMethod(state, initialiser, env);
3409
+ Object.defineProperty(doObj, "instrument", {
3410
+ value: instrumentMethod,
3411
+ writable: false,
3412
+ enumerable: false,
3413
+ configurable: true
3414
+ });
3415
+ Object.defineProperty(doObj, "env", {
3416
+ value: env,
3417
+ writable: false,
3418
+ enumerable: true,
3419
+ configurable: true
3420
+ });
3421
+ Object.defineProperty(doObj, "ctx", {
3422
+ value: state,
3423
+ writable: false,
3424
+ enumerable: true,
3425
+ configurable: true
3426
+ });
3427
+ return instrumentDurableObject(doObj, initialiser, env, state);
3326
3428
  }
3327
3429
  };
3328
3430
  return wrap(doClass, classHandler);
@@ -3866,5 +3968,5 @@ class ConsoleTransport {
3866
3968
  }
3867
3969
  }
3868
3970
 
3869
- export { BatchSizeLogRecordProcessor, BatchTraceSpanProcessor, ConsoleTransport, ImmediateLogRecordProcessor, MultiSpanExporter, MultiSpanExporterAsync, MultiTransportLogRecordProcessor, OTLPExporter, OTLPTransport, SEVERITY_NUMBERS, SpanImpl, WorkerLogger, WorkerLoggerProvider, __unwrappedFetch, createLogProcessor, createSampler, exportSpans, exportTelemetry, getGlobalLoggerProvider, getLogger, instrument, instrumentDO, isAlarm, isHeadSampled, isMessageBatch, isRequest, isRootErrorSpan, multiTailSampler, setGlobalLoggerProvider, withNextSpan };
3971
+ export { BatchSizeLogRecordProcessor, BatchTraceSpanProcessor, ConsoleTransport, ImmediateLogRecordProcessor, InstrumentedDurableObject, MultiSpanExporter, MultiSpanExporterAsync, MultiTransportLogRecordProcessor, OTLPExporter, OTLPTransport, SEVERITY_NUMBERS, SpanImpl, WorkerLogger, WorkerLoggerProvider, __unwrappedFetch, createLogProcessor, createSampler, exportSpans, exportTelemetry, getGlobalLoggerProvider, getLogger, instrument, instrumentDO, isAlarm, isHeadSampled, isMessageBatch, isRequest, isRootErrorSpan, multiTailSampler, setGlobalLoggerProvider, withNextSpan };
3870
3972
  //# sourceMappingURL=index.js.map