Skip to content

Commit 988bd77

Browse files
authored
Merge pull request #66 from DaniFoldi/scheduled-handler
Implement scheduled handler instrumentation
2 parents b03140e + 8f83b55 commit 988bd77

File tree

5 files changed

+85
-2
lines changed

5 files changed

+85
-2
lines changed

.changeset/fluffy-carrots-talk.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@microlabs/otel-cf-workers': minor
3+
---
4+
5+
Added instrumentation for scheduled handler

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ Triggers:
263263
- [ ] Email (`handler.email`)
264264
- [x] HTTP (`handler.fetch`)
265265
- [x] Queue (`handler.queue`)
266-
- [ ] Cron (`handler.scheduled`)
266+
- [x] Cron (`handler.scheduled`)
267267
- [ ] Tail (`handler.tail`)
268268
- [x] Durable Objects fetch
269269
- [x] Durable Objects alarm

src/instrumentation/scheduled.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import {
2+
trace,
3+
SpanOptions,
4+
SpanKind,
5+
Exception,
6+
context as api_context,
7+
SpanStatusCode
8+
} from '@opentelemetry/api'
9+
import { SemanticAttributes } from '@opentelemetry/semantic-conventions'
10+
import { Initialiser, setConfig } from '../config.js'
11+
import { exportSpans, proxyExecutionContext } from './common.js'
12+
import { instrumentEnv } from './env.js'
13+
import { wrap } from '../wrap.js'
14+
15+
type ScheduledHandler = ExportedHandlerScheduledHandler<unknown>
16+
export type ScheduledHandlerArgs = Parameters<ScheduledHandler>
17+
18+
const traceIdSymbol = Symbol('traceId')
19+
20+
let cold_start = true
21+
export function executeScheduledHandler(scheduledFn: ScheduledHandler, [controller, env, ctx]: ScheduledHandlerArgs): Promise<void> {
22+
const tracer = trace.getTracer('scheduledHandler')
23+
const attributes = {
24+
[SemanticAttributes.FAAS_TRIGGER]: 'timer',
25+
[SemanticAttributes.FAAS_COLDSTART]: cold_start,
26+
[SemanticAttributes.FAAS_CRON]: controller.cron,
27+
[SemanticAttributes.FAAS_TIME]: new Date(controller.scheduledTime).toISOString()
28+
}
29+
cold_start = false
30+
const options: SpanOptions = {
31+
attributes,
32+
kind: SpanKind.SERVER,
33+
}
34+
35+
const promise = tracer.startActiveSpan('scheduledHandler', options, async (span) => {
36+
const traceId = span.spanContext().traceId
37+
api_context.active().setValue(traceIdSymbol, traceId)
38+
try {
39+
await scheduledFn(controller, env, ctx)
40+
} catch (error) {
41+
span.recordException(error as Exception)
42+
span.setStatus({ code: SpanStatusCode.ERROR })
43+
span.end()
44+
throw error
45+
}
46+
})
47+
return promise
48+
}
49+
50+
export function createScheduledHandler(scheduledFn: ScheduledHandler, initialiser: Initialiser) {
51+
const scheduledHandler: ProxyHandler<ScheduledHandler> = {
52+
async apply(target, _thisArg, argArray: Parameters<ScheduledHandler>): Promise<void> {
53+
const [controller, orig_env, orig_ctx] = argArray
54+
const config = initialiser(orig_env as Record<string, unknown>, controller)
55+
const env = instrumentEnv(orig_env as Record<string, unknown>)
56+
const { ctx, tracker } = proxyExecutionContext(orig_ctx)
57+
const context = setConfig(config)
58+
59+
try {
60+
const args: ScheduledHandlerArgs = [controller, env, ctx]
61+
62+
return await api_context.with(context, executeScheduledHandler, undefined, target, args)
63+
} catch (error) {
64+
throw error
65+
} finally {
66+
orig_ctx.waitUntil(exportSpans(tracker))
67+
}
68+
},
69+
}
70+
return wrap(scheduledFn, scheduledHandler)
71+
}

src/sdk.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ import { createFetchHandler, instrumentGlobalFetch } from './instrumentation/fet
2929
import { instrumentGlobalCache } from './instrumentation/cache.js'
3030
import { createQueueHandler } from './instrumentation/queue.js'
3131
import { DOClass, instrumentDOClass } from './instrumentation/do.js'
32+
import { createScheduledHandler } from './instrumentation/scheduled.js'
3233

3334
type FetchHandler = ExportedHandlerFetchHandler<unknown, unknown>
35+
type ScheduledHandler = ExportedHandlerScheduledHandler<unknown>
3436
type QueueHandler = ExportedHandlerQueueHandler
3537

3638
export type ResolveConfigFn = (env: any, trigger: Trigger) => TraceConfig
@@ -170,6 +172,11 @@ export function instrument<E, Q, C>(
170172
handler.fetch = createFetchHandler(fetcher, initialiser)
171173
}
172174

175+
if (handler.scheduled) {
176+
const scheduler = unwrap(handler.scheduled) as ScheduledHandler
177+
handler.scheduled = createScheduledHandler(scheduler, initialiser)
178+
}
179+
173180
if (handler.queue) {
174181
const queuer = unwrap(handler.queue) as QueueHandler
175182
handler.queue = createQueueHandler(queuer, initialiser)

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,4 @@ export interface DOConstructorTrigger {
6666
name?: string
6767
}
6868

69-
export type Trigger = Request | MessageBatch | DOConstructorTrigger | 'do-alarm'
69+
export type Trigger = Request | MessageBatch | ScheduledController | DOConstructorTrigger | 'do-alarm'

0 commit comments

Comments
 (0)