Skip to content

fix(core): log errors inside event fields instead of {} - #778

Merged
HugoRCD merged 1 commit into
evloghq:mainfrom
adelrodriguez:fix/serialize-errors-in-event-fields
Oct 6, 2026
Merged

HugoRCD merged 1 commit into
evloghq:mainfrom
adelrodriguez:fix/serialize-errors-in-event-fields

Conversation

@adelrodriguez

@adelrodriguez adelrodriguez commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

🔗 Linked issue

Resolves #776

📚 Description

An Error inside a wide event was logged as {}. log.error({ error, message: 'Job failed' }) kept the message and lost the error that explained the failure, at any key and any depth. set({ error }) on a request logger lost it the same way. JSON.stringify received the raw Error, and its name, message, and stack are not enumerable.

Only log.error(err) went through serializeError. Now the object form of log.* and every request-logger entry point that merges caller fields (set() and the context argument of error, fatal, info, warn, trace) pass the fields through removeErrorCycles first. That function already serializes an Error at any depth and returns the input unchanged when it holds none.

 log.error({ ... })
-  emitWideEvent(level, event)
+  emitWideEvent(level, removeErrorCycles(event))

 logger.set(fields) / logger.warn(msg, fields) / ...
-  mergeInto(context, fields)
+  mergeFields(context, fields)   // mergeInto(context, removeErrorCycles(fields))

The level-method contexts are a step past the two call sites confirmed in the issue. They had the same bug: logger.warn('Retrying', { failure: err }) also wrote {}.

The reproduction from the issue, before:

drain: {}
drain: {"failure":{}}
drain: {}

After, against a fresh build:

drain: {"name":"Error","message":"Failed query","stack":"...","cause":{"name":"Error","message":"Connection terminated due to connection timeout","stack":"..."}}
drain: {"failure":{"name":"Error","message":"Failed query","stack":"...","cause":{"name":"Error","message":"Connection terminated due to connection timeout","stack":"..."}}}
drain: {"name":"Error","message":"Failed query","stack":"...","cause":{"name":"Error","message":"Connection terminated due to connection timeout","stack":"..."}}

Two regression tests cover both paths. They failed before the fix and pass now. pnpm run lint, pnpm run typecheck, pnpm run test (2010 evlog tests), and pnpm test:coverage pass. A patch changeset is included.

Merge risk: two-way door. The blast radius is the core logger. A caller that read a raw Error back from getContext() after set() now gets the serialized object, the same shape logger.error(err) already stores.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly. (No docs or skills describe the old behavior.)

Summary by CodeRabbit

  • Bug Fixes
    • Errors included in log fields and context are now serialized with their name, message, stack, and cause, including when nested inside objects. Other event fields remain intact.

@vercel

vercel Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

@adelrodriguez is attempting to deploy a commit to the HRCD Projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added the bug Something isn't working label Oct 5, 2026
@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (3)
packages/evlog/test/README.md — auto-discovered
AGENTS.md — auto-discovered
.agents/skills/create-enricher/SKILL.md — Agent Skill

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2e4aa816-3345-4936-a7af-19c54dcbf7fb
📥 Commits

Reviewing files that changed from the base of the PR and between a053adb and 0b8162c.

📒 Files selected for processing (4)
  • .changeset/serialize-errors-in-event-fields.md
  • packages/evlog/src/logger.ts
  • packages/evlog/test/core/logger-request-logger.test.ts
  • packages/evlog/test/core/logger.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

Logger event fields and request-logger context fields now sanitize Error values before emission or merging. Serialized errors include their name, message, stack, and cause. Tests cover nested errors in event objects, set(), and level-method context.

Changes

Error field serialization

Layer / File(s) Summary
Sanitize event and context fields
packages/evlog/src/logger.ts, packages/evlog/test/core/logger.test.ts, packages/evlog/test/core/logger-request-logger.test.ts, .changeset/serialize-errors-in-event-fields.md
Logger methods sanitize object-form events and context fields before emission or merging. Tests verify nested error causes and preservation of other event fields. The changeset describes the serialized error properties.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: hugorcd

Merge Risk: ⚪ Minimal · up to 0b816

Nested errors now retain useful details in event and request-logger output instead of becoming empty objects, while ordinary acyclic fields remain unchanged. No concrete merge-blocking risk remains.

Architecture Summary

Architecture risk: 🟠 High · up to 0b816

The change affects 1 system.

Changed systems: packages/evlog

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — packages/evlog (library) was modified; 3 changed files map to changed impact.

Before / after behavior

  • observed — Modified behavior in packages/evlog/src/logger.ts: Added mergeFields, which removes error cycles from supplied fields before merging them into the target.
  • observed — Modified behavior in packages/evlog/src/logger.ts: Object arguments to log methods are now cycle-sanitized before emission instead of being emitted directly.
  • observed — Modified behavior in packages/evlog/src/logger.ts: set now sanitizes incoming fields for error cycles before merging them into context.
  • observed — Modified behavior in packages/evlog/src/logger.ts: error now sanitizes optional error-context fields before merging them into context.

Reliability and maintainability

  • inferred — Risk-relevant change factors for packages/evlog: blast_radius_4; direct_dependents_4
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 3 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the core change: logging errors inside event fields instead of emitting empty objects. It also follows the repository’s conventional commit format.
Description check ✅ Passed The description identifies the linked issue, explains the bug and fix, outlines affected logger entry points, provides before-and-after examples, reports tests, and completes the checklist.
Linked Issues check ✅ Passed Issue #776 requires errors in object-form wide events and request-logger fields to serialize instead of becoming {}. logger.ts now passes object events through removeErrorCycles and sanitizes fi…
Out of Scope Changes check ✅ Passed All changes support issue #776. The additional request-logger level-method contexts use the same field-merging path and address the same error-serialization defect. The regression tests and patch chan…
Full details: Docstring Coverage

Explanation

Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 3 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Thank you for following the naming conventions! 🙏

@HugoRCD
HugoRCD merged commit 88d2c9f into evloghq:main Oct 6, 2026
13 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] An Error inside a wide event is logged as {}

2 participants