feat(logger): add jsonOutput to internal logger#2347
Merged
Conversation
Adds an opt-in `jsonOutput` mode that emits a single-line JSON object
per log call instead of ANSI-coloured text. Each entry carries the
`level` ("debug" | "info" | "warn" | "error") and `message`; when
`shouldIncludeTimestamp` is also set, an ISO `timestamp` field is
included alongside. This addresses the case in DataDog#1991 where a Datadog
ingestion pipeline (or any other log consumer) sees every line as
unstructured text and defaults `error` records to `info`.
The third constructor parameter is now `boolean | LoggerOptions`. The
boolean form preserves the existing call sites verbatim
(`new Logger(stdout, INFO, true)`); the options form unlocks the new
`jsonOutput` flag and remains forward-compatible. A `setJsonOutput`
runtime toggle is also exposed alongside the existing
`setShouldIncludeTime`.
Tests cover both modes — log-level gating, timestamp inclusion / omission,
ANSI-stripping in JSON mode, and a JSON→text toggle.
🎉 All green!🧪 All tests passed 🔗 Commit SHA: 376c7b4 | Docs | Datadog PR Page | Give us feedback! |
jsonOutput to internal logger
Drarig29
approved these changes
Jun 11, 2026
Drarig29
left a comment
Contributor
There was a problem hiding this comment.
LGTM. Will merge and think about the best way to support this in all commands. We already have internal conversations ongoing for a shared base command class.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Related to #1991.
The existing
Loggeronly emits ANSI-coloured text. When the CLI runs inside a log pipeline (Datadog itself, or any other ingestion path), every line is unstructured —errorrecords show up asinfobecause the consumer can't tell them apart without parsing the colour codes or prefixes.What
Add a third constructor shape that accepts
LoggerOptions({ shouldIncludeTimestamp?, jsonOutput? }). The legacybooleanform (new Logger(write, INFO, true)) still works unchanged, so existing call sites compile without edits.When
jsonOutputis true, each log call writes a single-line JSON object:{"level":"error","message":"deploy failed","timestamp":"2026-06-10T15:09:00.123Z"}levelis the canonical lowercase name.timestampis only emitted whenshouldIncludeTimestampis also enabled (matching how the text-mode prefix works). ANSI colours are intentionally skipped so the line parses cleanly withJSON.parse.Expose
setJsonOutput(boolean)alongside the existingsetShouldIncludeTimeso plugins can flip the format at runtime (e.g. based on a CLI flag or env var).Tests
packages/base/src/helpers/__tests__/logger.test.ts— 9 cases covering:yarn build,yarn lint,yarn test packages/base/src/helpers/__tests__/logger.test.ts— all clean.Follow-up
Wiring a
--json(orDATADOG_CI_JSON_LOG=1) toggle into the individual plugin commands would close the loop end-to-end, but I've left that out of this PR since it touches every plugin and the right pattern (per-plugin flag vs. global) is a separate design call. Happy to do it as a follow-up if you want — flagging here so reviewers know it's intentional scope, not an oversight.