Skip to content

route Axiom logs through FireLens#2364

Open
og2701 wants to merge 1 commit into
devfrom
feat/firelens-log-transport
Open

route Axiom logs through FireLens#2364
og2701 wants to merge 1 commit into
devfrom
feat/firelens-log-transport

Conversation

@og2701

@og2701 og2701 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary by cubic

Route application logs through FireLens to Axiom in ECS, with an ECS-aware auto-switch and a direct Axiom transport as fallback. Adds Fluent Bit config and tests; dev/test behavior is unchanged.

  • New Features

    • Fluent Bit config to parse json, send structured records to Axiom express, and console records to ecs; TLS on, retry_limit 5.
    • Logger supports transport: "direct" | "firelens" via AXIOM_LOG_TRANSPORT; auto-enables FireLens only in production ECS.
    • Keeps direct Axiom transport when not in ECS or in dev/test; maintains dual mode.
    • Added unit tests for FireLens config and transport selection.
    • Updated .env.example; CI allowlist includes feat/firelens-log-transport.
  • Migration

    • In ECS, set AXIOM_LOG_TRANSPORT=firelens and ensure the FireLens/Fluent Bit sidecar is enabled.
    • No changes needed for local dev/test; default remains direct.

Written for commit adbb124. Summary will update on new commits.

Review in cubic

Greptile Summary

This PR routes structured application logs through FireLens while keeping direct Axiom delivery as a fallback. The main changes are:

  • Improvements: Select FireLens only in configured production ECS environments.
  • Improvements: Parse and route structured Pino records to the Axiom express dataset.
  • Improvements: Route unmatched container records to the Axiom ecs dataset.
  • Improvements: Add unit tests for logger selection and FireLens configuration.
  • Improvements: Allow the feature branch to deploy to staging.

Confidence Score: 5/5

This looks safe to merge.

  • FireLens activation is limited to configured production ECS environments.
  • Other environments retain the existing direct Axiom path.
  • The Pino level formatter matches the FireLens rewrite rule.
  • No blocking issues were found in the changed code.

Important Files Changed

Filename Overview
firelens.conf Adds the parser, tag rewriting, bounded emitter, and separate Axiom outputs for structured and container records.
server/src/utils/logging/initLogger.ts Adds explicit and environment-driven transport selection with direct delivery as the non-ECS fallback.
server/tests/unit/logging/firelens-config.test.ts Checks the main parser, routing, output, and retry settings in the FireLens configuration.
server/tests/unit/logging/init-logger-transport.test.ts Covers FireLens output, direct transport, structured record preservation, and the non-ECS fallback.
server/.env.example Documents direct Axiom delivery as the default transport.
.github/workflows/build.yml Adds the feature branch to the staging deployment allowlist.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[Pino logger] --> B{FireLens configured and running in production ECS?}
    B -- No --> C[Direct Axiom transport]
    B -- Yes --> D[Container stdout]
    D --> E[FireLens JSON parser]
    E --> F{Recognized Pino level?}
    F -- Yes --> G[Retag as axiom_express]
    G --> H[Axiom express dataset]
    F -- No --> I[Keep FireLens tag]
    I --> J[Axiom ecs dataset]
Loading

Reviews (1): Last reviewed commit: "route Axiom logs through FireLens" | Re-trigger Greptile

Context used (4)

  • Context used - CLAUDE.md (source)
  • Context used - AGENTS.md (source)
  • Context used - server/CLAUDE.md (source)
  • Context used - When generating the key changes section of the sum... (source)

@og2701
og2701 requested review from ay-rod and johnyeocx as code owners July 22, 2026 17:46
@capy-ai

capy-ai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Capy auto-review is paused for this organization because the usage-cycle auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@og2701 og2701 changed the title feat(logging): route Axiom logs through FireLens route Axiom logs through FireLens Jul 22, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 6 files

Confidence score: 2/5

  • firelens.conf is the highest-risk item: if Flightcontrol prepends its own [SERVICE] block, Fluent Bit can reject the config (single-service-section rule), which can break FireLens logging at runtime — move Parsers_File into Flightcontrol’s existing service block (or otherwise guarantee only one [SERVICE]) before merging.
  • In server/src/utils/logging/initLogger.ts, dual mode with shouldUseFirelens=true switches away from createConsoleJsonStream() to raw process.stdout, which can drop trigger.dev run UI visibility even though logs still emit — restore the console-routed sink in dual mode or add an equivalent path that preserves UI ingestion before merge.
  • server/tests/unit/logging/firelens-config.test.ts can throw a null TypeError on zero matches, masking the real assertion signal and making regressions harder to diagnose — add a null-safe fallback in the matcher so failures show a clear diff.
  • server/tests/unit/logging/init-logger-transport.test.ts currently validates spy call counts but not the third logger’s actual output, leaving the fallback transport behavior under-verified — assert emitted output/content for that branch to de-risk future logging regressions.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="server/tests/unit/logging/firelens-config.test.ts">

<violation number="1" location="server/tests/unit/logging/firelens-config.test.ts:29">
P2: The `config.match(...).toHaveLength(...)` pattern throws `TypeError: Cannot read properties of null` instead of a clean assertion failure when there are zero matches. Use a fallback so the test surfaces a clear diff. For example: `expect(config.match(/retry_limit 5/g) ?? []).toHaveLength(2)`.</violation>
</file>

<file name="server/src/utils/logging/initLogger.ts">

<violation number="1" location="server/src/utils/logging/initLogger.ts:191">
P2: Dual mode with FireLens loses trigger.dev run UI log visibility. When `shouldUseFirelens` is true in dual mode, the stdout sink switches from `createConsoleJsonStream()` (which routes via `console.log`) to raw `process.stdout`. trigger.dev's run UI instruments `console.*` calls but not `process.stdout.write`, so production task logs won't appear in the trigger.dev timeline when FireLens is active. Since `console.log` still writes to stdout, FireLens will read the lines just fine — use `createConsoleJsonStream()` here regardless of `shouldUseFirelens` to retain trigger.dev compatibility.</violation>
</file>

<file name="server/tests/unit/logging/init-logger-transport.test.ts">

<violation number="1" location="server/tests/unit/logging/init-logger-transport.test.ts:98">
P2: The third `initLogger()` call (transport=firelens without ECS metadata, falling back to direct) creates a logger whose output is never verified. The test only asserts on the spy call count — an implementation detail — but never logs anything with this logger or checks that its structured output is correct. Without exercising the logger, a regression in the fallback path (e.g., misconfigured level, broken formatters, missing mixin) would not be caught here.</violation>
</file>

<file name="firelens.conf">

<violation number="1" location="firelens.conf:1">
P1: FireLens will fail to load this config when Flightcontrol prepends its `[SERVICE]` block, because Fluent Bit permits only one service section. Put `Parsers_File` in Flightcontrol's existing service block (or ensure this is the sole `[SERVICE]` block).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread firelens.conf
@@ -1,3 +1,9 @@
[SERVICE]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: FireLens will fail to load this config when Flightcontrol prepends its [SERVICE] block, because Fluent Bit permits only one service section. Put Parsers_File in Flightcontrol's existing service block (or ensure this is the sole [SERVICE] block).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At firelens.conf, line 1:

<comment>FireLens will fail to load this config when Flightcontrol prepends its `[SERVICE]` block, because Fluent Bit permits only one service section. Put `Parsers_File` in Flightcontrol's existing service block (or ensure this is the sole `[SERVICE]` block).</comment>

<file context>
@@ -1,3 +1,9 @@
+[SERVICE]
+    # Flightcontrol prepends its own SERVICE block. The production AWS Fluent
+    # Bit image accepts an additional block and needs this file registered
</file context>

expect(config).toContain("Emitter_Mem_Buf_Limit 10M");
expect(config).toContain("URI /v1/ingest/express");
expect(config).toContain("URI /v1/ingest/ecs");
expect(config.match(/retry_limit 5/g)).toHaveLength(2);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The config.match(...).toHaveLength(...) pattern throws TypeError: Cannot read properties of null instead of a clean assertion failure when there are zero matches. Use a fallback so the test surfaces a clear diff. For example: expect(config.match(/retry_limit 5/g) ?? []).toHaveLength(2).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/tests/unit/logging/firelens-config.test.ts, line 29:

<comment>The `config.match(...).toHaveLength(...)` pattern throws `TypeError: Cannot read properties of null` instead of a clean assertion failure when there are zero matches. Use a fallback so the test surfaces a clear diff. For example: `expect(config.match(/retry_limit 5/g) ?? []).toHaveLength(2)`.</comment>

<file context>
@@ -0,0 +1,32 @@
+		expect(config).toContain("Emitter_Mem_Buf_Limit 10M");
+		expect(config).toContain("URI /v1/ingest/express");
+		expect(config).toContain("URI /v1/ingest/ecs");
+		expect(config.match(/retry_limit 5/g)).toHaveLength(2);
+		expect(config).not.toMatch(/\[OUTPUT\]\s+Name http\s+Match \*(?:\s|$)/);
+	},
</file context>
Suggested change
expect(config.match(/retry_limit 5/g)).toHaveLength(2);
expect((config.match(/retry_limit 5/g) ?? [])).toHaveLength(2);

useConsoleLog: true,
})
: createConsoleJsonStream(),
: shouldUseFirelens

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Dual mode with FireLens loses trigger.dev run UI log visibility. When shouldUseFirelens is true in dual mode, the stdout sink switches from createConsoleJsonStream() (which routes via console.log) to raw process.stdout. trigger.dev's run UI instruments console.* calls but not process.stdout.write, so production task logs won't appear in the trigger.dev timeline when FireLens is active. Since console.log still writes to stdout, FireLens will read the lines just fine — use createConsoleJsonStream() here regardless of shouldUseFirelens to retain trigger.dev compatibility.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/utils/logging/initLogger.ts, line 191:

<comment>Dual mode with FireLens loses trigger.dev run UI log visibility. When `shouldUseFirelens` is true in dual mode, the stdout sink switches from `createConsoleJsonStream()` (which routes via `console.log`) to raw `process.stdout`. trigger.dev's run UI instruments `console.*` calls but not `process.stdout.write`, so production task logs won't appear in the trigger.dev timeline when FireLens is active. Since `console.log` still writes to stdout, FireLens will read the lines just fine — use `createConsoleJsonStream()` here regardless of `shouldUseFirelens` to retain trigger.dev compatibility.</comment>

<file context>
@@ -181,9 +188,11 @@ export const initLogger = (options: InitLoggerOptions = {}) => {
 						useConsoleLog: true,
 					})
-				: createConsoleJsonStream(),
+				: shouldUseFirelens
+					? process.stdout
+					: createConsoleJsonStream(),
</file context>


process.env.AXIOM_LOG_TRANSPORT = "firelens";
delete process.env.ECS_CONTAINER_METADATA_URI_V4;
initLogger();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The third initLogger() call (transport=firelens without ECS metadata, falling back to direct) creates a logger whose output is never verified. The test only asserts on the spy call count — an implementation detail — but never logs anything with this logger or checks that its structured output is correct. Without exercising the logger, a regression in the fallback path (e.g., misconfigured level, broken formatters, missing mixin) would not be caught here.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/tests/unit/logging/init-logger-transport.test.ts, line 98:

<comment>The third `initLogger()` call (transport=firelens without ECS metadata, falling back to direct) creates a logger whose output is never verified. The test only asserts on the spy call count — an implementation detail — but never logs anything with this logger or checks that its structured output is correct. Without exercising the logger, a regression in the fallback path (e.g., misconfigured level, broken formatters, missing mixin) would not be caught here.</comment>

<file context>
@@ -0,0 +1,135 @@
+
+		process.env.AXIOM_LOG_TRANSPORT = "firelens";
+		delete process.env.ECS_CONTAINER_METADATA_URI_V4;
+		initLogger();
+		expect(transportSpy).toHaveBeenCalledTimes(2);
+	} finally {
</file context>

@og2701
og2701 force-pushed the feat/firelens-log-transport branch from adbb124 to 6be954b Compare July 22, 2026 18:47
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
checkout Ignored Ignored Jul 22, 2026 6:47pm
landing-page Ignored Ignored Jul 22, 2026 6:47pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant