Skip to content

Commit 0787869

Browse files
committed
Add console support for logfire-cf-workers
Fixes #52
1 parent 5e54d9c commit 0787869

File tree

6 files changed

+100
-13
lines changed

6 files changed

+100
-13
lines changed

.changeset/soft-items-sort.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pydantic/logfire-cf-workers": minor
3+
---
4+
5+
Support console logging of spans for CF workers

examples/cf-worker/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { instrument } from '@pydantic/logfire-cf-workers';
1515

1616
const handler = {
1717
async fetch(): Promise<Response> {
18-
logfire.info('info span from inside the worker body');
18+
logfire.info('span from inside the worker body', { foo: 'bar' });
1919
return new Response('Hello World!');
2020
},
2121
} satisfies ExportedHandler;
@@ -26,4 +26,5 @@ export default instrument(handler, {
2626
namespace: '',
2727
version: '1.0.0',
2828
},
29+
console: true,
2930
});

package-lock.json

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/logfire-cf-workers/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
},
5151
"dependencies": {
5252
"@pydantic/logfire-api": "*",
53-
"@pydantic/otel-cf-workers": "^1.0.0-rc.53"
53+
"@pydantic/otel-cf-workers": "^1.0.0-rc.54"
5454
},
5555
"devDependencies": {
5656
"@cloudflare/workers-types": "4.20250311.0",
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { ExportResult, ExportResultCode, hrTimeToMicroseconds } from '@opentelemetry/core'
2+
import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'
3+
4+
const LevelLabels = {
5+
1: 'trace',
6+
5: 'debug',
7+
9: 'info',
8+
10: 'notice',
9+
13: 'warning',
10+
17: 'error',
11+
21: 'fatal',
12+
} as const
13+
14+
/**
15+
* Prints spans in the terminal, using the respective color sequences.
16+
*/
17+
export class LogfireCloudflareConsoleSpanExporter implements SpanExporter {
18+
export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void {
19+
this.sendSpans(spans, resultCallback)
20+
}
21+
22+
forceFlush(): Promise<void> {
23+
return Promise.resolve()
24+
}
25+
shutdown(): Promise<void> {
26+
this.sendSpans([])
27+
return this.forceFlush()
28+
}
29+
30+
/**
31+
* converts span info into more readable format
32+
* @param span
33+
*/
34+
private exportInfo(span: ReadableSpan) {
35+
return {
36+
attributes: span.attributes,
37+
duration: hrTimeToMicroseconds(span.duration),
38+
events: span.events,
39+
id: span.spanContext().spanId,
40+
instrumentationScope: span.instrumentationScope,
41+
kind: span.kind,
42+
links: span.links,
43+
name: span.name,
44+
parentSpanContext: span.parentSpanContext,
45+
resource: {
46+
attributes: span.resource.attributes,
47+
},
48+
status: span.status,
49+
timestamp: hrTimeToMicroseconds(span.startTime),
50+
traceId: span.spanContext().traceId,
51+
traceState: span.spanContext().traceState?.serialize(),
52+
}
53+
}
54+
55+
private sendSpans(spans: ReadableSpan[], done?: (result: ExportResult) => void): void {
56+
for (const span of spans) {
57+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
58+
const type = LevelLabels[span.attributes['logfire.level_num'] as keyof typeof LevelLabels] ?? 'info'
59+
60+
const { attributes, name, ...rest } = this.exportInfo(span)
61+
console.log(`Logfire: ${type} >> ${name}`)
62+
console.log('Attributes:')
63+
console.log(JSON.stringify(attributes, null, 2))
64+
console.log('---')
65+
console.log('Span details:')
66+
console.log(JSON.stringify(rest, null, 2))
67+
console.log('---')
68+
}
69+
if (done) {
70+
done({ code: ExportResultCode.SUCCESS })
71+
}
72+
}
73+
}

packages/logfire-cf-workers/src/index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import type { ReadableSpan } from '@opentelemetry/sdk-trace-base'
2-
3-
import { resolveBaseUrl, serializeAttributes, ULIDGenerator } from '@pydantic/logfire-api'
1+
import { type ReadableSpan, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
42
import * as logfireApi from '@pydantic/logfire-api'
3+
import { resolveBaseUrl, serializeAttributes, ULIDGenerator } from '@pydantic/logfire-api'
54
import { instrument as baseInstrument, TraceConfig } from '@pydantic/otel-cf-workers'
65

6+
import { LogfireCloudflareConsoleSpanExporter } from './LogfireCloudflareConsoleSpanExporter'
77
import { TailWorkerExporter } from './TailWorkerExporter'
88
export * from './exportTailEventsToLogfire'
99

@@ -16,6 +16,10 @@ type ConfigOptionsBase = Pick<
1616

1717
export interface InProcessConfigOptions extends ConfigOptionsBase {
1818
baseUrl?: string
19+
/**
20+
* Whether to log the spans to the console in addition to sending them to the Logfire API.
21+
*/
22+
console?: boolean
1923
/**
2024
* Options for scrubbing sensitive data. Set to False to disable.
2125
*/
@@ -32,15 +36,18 @@ function getInProcessConfig(config: InProcessConfigOptions): (env: Env) => Trace
3236
const baseUrl = resolveBaseUrl(env, config.baseUrl, token)
3337
const resolvedEnvironment = config.environment ?? envDeploymentEnvironment
3438

39+
const additionalSpanProcessors = config.console ? [new SimpleSpanProcessor(new LogfireCloudflareConsoleSpanExporter())] : []
40+
3541
return Object.assign({}, config, {
42+
additionalSpanProcessors,
3643
environment: resolvedEnvironment,
3744
exporter: {
3845
headers: { Authorization: token },
3946
url: `${baseUrl}/v1/traces`,
4047
},
4148
idGenerator: new ULIDGenerator(),
4249
postProcessor: (spans: ReadableSpan[]) => postProcessAttributes(spans),
43-
})
50+
}) satisfies TraceConfig
4451
}
4552
}
4653

0 commit comments

Comments
 (0)