feat: child loggers, structured meta, Error serialization, timers, TTY-aware color#55
Conversation
…Y-aware color
- Add logger.child(meta) for context-binding child loggers; merges static
and dynamic meta from parent, supports arbitrary nesting
- Widen meta type from Record<string,string> to Record<string,unknown> so
numbers, booleans, and objects pass through to JSON/logfmt sinks
- Serialize Error arguments to { name, message, stack } on HellogMessage.error
for structured sinks; human content unchanged via util.format
- Add time(label)/timeEnd(label) duration timers using process.hrtime.bigint()
with durationMs in meta; no memory leak on unknown labels
- Add isLevelEnabled(level) guard for expensive argument construction
- Make HellogColorizeDefaultPlugin TTY-aware: auto-detects process.stdout.isTTY,
NO_COLOR, FORCE_COLOR; accepts { enabled? } override
- Convert DefaultPlugins static array to per-instance factory to prevent
cross-logger state bleed as plugins gain state
- New lib/errors.ts with SerializedError interface and serializeError() util
- 32 tests, 97% coverage (up from 17 tests, 88% coverage)
- Zero new runtime dependencies; full backward compatibility
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LMaxence
left a comment
There was a problem hiding this comment.
Thanks for the contribution !
The Hellog class is getting a big bigger than what I'd like it to be, but it's still fine for now.
Note for myself later, I'd like to move the timers and metas into isolated classes handling their logic and serde layers
| if (process.env['FORCE_COLOR'] === '0') return false; | ||
| if (process.env['NO_COLOR'] !== undefined) return false; | ||
| return process.stdout.isTTY === true; | ||
| } |
There was a problem hiding this comment.
I'd prefer this to be a getter in the plugin please :)
Address review feedback: fold the standalone _colorEnabled function into a private getter on HellogColorizeDefaultPlugin. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
81f2641 to
9518991
Compare
|
🎉 This PR is included in version 3.6.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
I agree, it would allow for better separation of concerns and make the code easier to test. Would you like me to do it @LMaxence? |
|
I already made a commit for that in the weekend: #57 We're all good here :) |
Hello @LMaxence à dispo pour en parler (je t'ai contacté sur LinkedIN)
Summary
logger.child(meta)— create child loggers that inherit level + plugins and merge parent meta. Supports static and dynamic meta, nests arbitrarily.metafromRecord<string,string>toRecord<string,unknown>so numbers, booleans, and objects pass through to JSON/logfmt sinks without pre-stringifying.{ name, message, stack }from anyErrorargument and attaches it toHellogMessage.errorfor structured sinks.logger.time(label)/logger.timeEnd(label)usingprocess.hrtime.bigint()(monotonic, sub-ms). Logs elapsed time and includesdurationMsin meta.isLevelEnabled(level)— guard expensive argument construction without duplicating the level-filter logic.HellogColorizeDefaultPluginnow auto-detectsprocess.stdout.isTTY,NO_COLOR, andFORCE_COLOR. Accepts an explicit{ enabled?: boolean }override.DefaultPlugins→ factory — converts the shared static plugin array to a per-instance factory to prevent cross-logger state bleed.Backward compatibility
All changes are additive.
Record<string,string>meta is a subtype ofRecord<string,unknown>so existing callers compile unchanged. Default plugin stack and output formats are unaffected. Zero new runtime dependencies.Test plan
npm run build— TypeScript compiles clean under@tsconfig/strictestnpm test— 32/32 tests pass (up from 17), coverage 97%npm run lint— oxlint cleannpm run format:check— oxfmt clean