Skip to content

Commit f6a3a25

Browse files
Recommend batched span export for Firebase instrumentation (#40)
1 parent 5e7895a commit f6a3a25

6 files changed

Lines changed: 50 additions & 6 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"agentpond": patch
3+
---
4+
5+
Recommend batched OpenTelemetry span export for Firebase instrumentation so each stored object can contain multiple spans.

docs/direct-object-store-export.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ sdk.start();
5757
await sdk.shutdown();
5858
```
5959

60+
NodeSDK wraps `traceExporter` in a `BatchSpanProcessor`. AgentPond preserves each exporter invocation as one immutable object-store object, so a batch of spans is written as one object. If you configure span processors directly, prefer `BatchSpanProcessor` for production and force-flush at the application's real lifecycle boundary.
61+
6062
## Object Stores
6163

6264
Use the matching adapter for the deployment:

packages/otel/tests/otel.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { resourceFromAttributes } from "@opentelemetry/resources";
1717
import { NodeSDK } from "@opentelemetry/sdk-node";
1818
import {
1919
BasicTracerProvider,
20+
BatchSpanProcessor,
2021
InMemorySpanExporter,
2122
type ReadableSpan,
2223
SimpleSpanProcessor,
@@ -144,6 +145,40 @@ test("empty exports succeed without writing an object", async () => {
144145
await exporter.shutdown();
145146
});
146147

148+
test("a batched exporter invocation writes multiple spans in one object", async () => {
149+
const store = new MemoryObjectStore();
150+
const exporter = new AgentPondSpanExporter({
151+
store,
152+
projectId: "project-a",
153+
});
154+
const provider = new BasicTracerProvider({
155+
resource: resourceFromAttributes({ "service.name": "batched-export-test" }),
156+
spanProcessors: [new BatchSpanProcessor(exporter)],
157+
});
158+
const tracer = provider.getTracer("batched-export-test");
159+
160+
tracer.startSpan("first span").end();
161+
tracer.startSpan("second span").end();
162+
await provider.forceFlush();
163+
164+
const keys = await store.listKeys("otel/project-a/");
165+
assert.equal(keys.length, 1);
166+
const resourceSpans = await store.getJson<
167+
Array<{ scopeSpans?: Array<{ spans?: unknown[] }> }>
168+
>(keys[0]);
169+
const storedSpanCount = resourceSpans.reduce(
170+
(resourceCount, resourceSpan) =>
171+
resourceCount +
172+
(resourceSpan.scopeSpans ?? []).reduce(
173+
(scopeCount, scopeSpan) => scopeCount + (scopeSpan.spans?.length ?? 0),
174+
0,
175+
),
176+
0,
177+
);
178+
assert.equal(storedSpanCount, 2);
179+
await provider.shutdown();
180+
});
181+
147182
test("storage failures are returned through the exporter callback", async () => {
148183
class FailingStore extends MemoryObjectStore {
149184
override async putJson(): Promise<void> {

skills/agentpond-instrumentation/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Stop after presenting the proposal and ask for explicit confirmation before inst
6363
3. Create or update one centralized server instrumentation module.
6464
4. Reuse an existing default Firebase Admin app. Add default initialization only when it is absent; follow [references/firebase.md](references/firebase.md).
6565
5. Create `createFirebaseSpanExporter()` after the default app exists.
66-
6. Add the exporter to the existing provider. When no provider exists, create one using APIs supported by the installed OpenTelemetry version and a processor appropriate for direct export.
66+
6. Add the exporter to the existing provider. When no provider exists, create one using APIs supported by the installed OpenTelemetry version and prefer NodeSDK's batched `traceExporter` configuration or an explicit `BatchSpanProcessor`.
6767
7. Register the selected OpenInference instrumentation before AI clients are created.
6868
8. Add manual CHAIN and TOOL spans only where auto-instrumentation leaves important application behavior invisible.
6969
9. Preserve one `session.id` across all turns in the same conversation.

skills/agentpond-instrumentation/references/firebase.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,9 @@ When no provider exists, a typical Node SDK shape is:
4141
```ts
4242
import { createFirebaseSpanExporter } from "@agentpond/firebase";
4343
import { NodeSDK } from "@opentelemetry/sdk-node";
44-
import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
4544

4645
const sdk = new NodeSDK({
47-
spanProcessors: [
48-
new SimpleSpanProcessor(createFirebaseSpanExporter()),
49-
],
46+
traceExporter: createFirebaseSpanExporter(),
5047
instrumentations: [
5148
// Add the integration selected for the detected AI SDK or framework.
5249
],
@@ -55,7 +52,9 @@ const sdk = new NodeSDK({
5552
sdk.start();
5653
```
5754

58-
Adapt the construction to the installed SDK API and project lifecycle. Initialize the module before instrumented clients. Force-flush at a real lifecycle boundary when required; do not shut down a reusable Functions instance after every request.
55+
NodeSDK wraps `traceExporter` in a `BatchSpanProcessor`, so each exporter invocation can contain multiple spans and AgentPond writes one object per exported batch. When constructing a provider manually or tuning queue and batch settings, create a `BatchSpanProcessor` explicitly instead. Do not use `SimpleSpanProcessor` for normal production export because it invokes the exporter separately for every ended span.
56+
57+
Adapt the construction to the installed SDK API and project lifecycle. Initialize the module before instrumented clients. Force-flush at a real lifecycle boundary when required so queued batches finish exporting; do not shut down a reusable Functions instance after every request.
5958

6059
## Storage Rules review
6160

tests/skills.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ test("Firebase instrumentation skill preserves the setup and verification workfl
5151
/Storage Rules/i,
5252
/nested block/i,
5353
/framework-native/i,
54+
/traceExporter/,
55+
/BatchSpanProcessor/,
5456
/CHAIN/,
5557
/TOOL/,
5658
/session\.id/,
@@ -60,4 +62,5 @@ test("Firebase instrumentation skill preserves the setup and verification workfl
6062
]) {
6163
assert.match(content, required);
6264
}
65+
assert.doesNotMatch(content, /new SimpleSpanProcessor/);
6366
});

0 commit comments

Comments
 (0)