Skip to content

feat: pluggable per-instance logger via WechatyOptions - #120

Merged
hcfw007 merged 17 commits into
mainfrom
feat/pluggable-logger
Jul 1, 2026
Merged

feat: pluggable per-instance logger via WechatyOptions#120
hcfw007 merged 17 commits into
mainfrom
feat/pluggable-logger

Conversation

@hcfw007

@hcfw007 hcfw007 commented Jul 1, 2026

Copy link
Copy Markdown
Member

Summary

Add WechatyOptions.logger and route it through to the puppet layer. Wechaty.log, Contact.log, Message.log, Room.log and every other user module now resolve to whatever logger the caller supplied — with brolog fallback preserved end-to-end.

Motivation

Multi-bot hosts need to attach botId to logs emitted deep inside wechaty and every user module. Making the logger pluggable at the WechatyOptions level threads that context through the whole stack without any per-module surgery.

Changes

  • src/schemas/logger.ts: re-export LoggerLike from @juzi/wechaty-puppet
  • src/schemas/wechaty-options.ts: logger?: LoggerLike
  • src/wechaty/wechaty-skeleton.ts: __log: LoggerLike + get log(): LoggerLike
  • src/wechaty-mixins/puppet-mixin.ts: forward WechatyOptions.logger into PuppetOptions.logger and adopt puppet.log after the puppet is built (structural cast because PuppetInterface hides log)
  • src/user-mixins/wechatify.ts: single-place static get log() + instance getter — every user module gets it for free
  • 284 instance-method call sites migrated log.xxxthis.log.xxx (90 in wechaty core, 194 in user modules)

Compatibility

Zero-break: callers who do not pass options.logger get the historical brolog behaviour verbatim. Wechaty.log still falls back to brolog when a third-party puppet doesn't expose .log.

Verified

  • npm test: 33/33 tap spec pass (linked to local wechaty-puppet 1.0.147)
  • Lint (es/ts/md/sh): clean
  • tsc build against linked wechaty-puppet: clean

🤖 Generated with Claude Code

hcfw007 added 17 commits June 30, 2026 16:48
Pick up the schema additions for the optional `callId` field on call
events. No business code change in this repo — downstream consumers need
the new puppet types to consume callId end-to-end.
- Add a local LoggerLike alias (temporary stub for the field
  wechaty-puppet is publishing in parallel).
- Add optional logger?: LoggerLike to WechatyOptions.
- In puppet-mixin.init(), forward WechatyOptions.logger down into
  PuppetOptions.logger before resolvePuppet, so a caller-supplied
  logger reaches the puppet layer through the same code path as
  the rest of puppet options.
- WechatySkeleton grows a mutable __log (LoggerLike, initialised to
  brolog) backing a public readonly `log` getter. Static log stays
  bound to brolog for pre-instance callers.
- puppet-mixin adopts the puppet's own `log` onto __log right after
  resolvePuppet succeeds — with a permissive access shape so this
  compiles against puppet versions before they publish puppet.log.
- wechatifyMixin adds static + instance `log` getters that delegate
  to `this.wechaty.log` with a try/catch fall back to brolog. That
  gives every wechatified user module (Contact/Message/Room/...) a
  `log` handle without touching each file individually.
Route Wechaty's own and every user module's per-instance log calls
through `this.log` so they follow the caller-supplied logger set up
in the previous two commits. Static factories / classmethods keep
using the module-imported brolog `log`, and pre-`super()` calls
inside constructors do too (no `this` yet).

Also widen the local LoggerLike shape from brolog's strict
`Loggable` (2 required args) to a Brolog-class-compatible interface
(1 required arg, ...rest). The rest of the codebase has historically
relied on single-arg calls like `log.warn('unknown payload type ' + x)`;
the strict shape would surface these as fresh type errors under
`this.log` even though they were valid against the concrete Brolog
class the puppet re-exports.
wechaty-puppet 1.0.147+ exports `LoggerLike` and ships `log` on the
Puppet base with a typed `options.logger` field, so wechaty can
re-export the type directly and use `LoggerLike` in the adopt cast
instead of `typeof log`. Comments updated to reflect that the cast
persists because `PuppetInterface` hides `log`, not because of
backward compat.
Hoist the `__log` adoption from after `emit('puppet')` to right after
`resolvePuppet` returns. Previously wechaty's own init-time verbose
logs (setMemory / setupPuppetEvents / the sync `emit('puppet')`
listeners in wechaty-redux and friends) all ran through the brolog
fallback while the rest of the lifecycle ran through the
caller-supplied logger — a half-brolog, half-caller-logger observation
gap on the exact path most needed for init failure triage.

Also:
- WechatySkeleton.static log: `Loggable` -> `LoggerLike`, so the
  static and instance log surfaces expose the same contract to
  callers (a Brolog instance already satisfies LoggerLike).
- WechatyOptions.logger JSDoc: spell out the scope so callers know
  embedded libs (state-switch, memory-card, gerror) still emit via
  the process-wide brolog and are not rerouted by this option.
Route static-method log calls through the wechatified class's
`static get log()` (defined in user-mixins/wechatify.ts), so
per-wechaty logger overrides also apply to Tag.list / Message.find /
Contact.load and every other user-module static entrypoint.

Module-top factory `log.verbose(...)` calls (executed once at mixin
definition time, before any wechaty instance exists) are intentionally
untouched. The one non-wechatified helper (PostBuilder in post.ts) keeps
its brolog import.
The Io class stores `options` (with wechaty) via its constructor
parameter property, so every method — including the constructor body
past the parameter assignment — can route logging through the
per-wechaty logger.

The class-field initializer `new StateSwitch('Io', { log })` still
depends on the module-imported brolog because it runs before `this` is
constructed. That import stays for that one call site.
The `get isLoggedIn` fallback branch was still logging via the
module-imported brolog. Route it through `this.log` so pluggable
loggers see it too.

Module-top factory calls and constructor pre-super log calls keep
their brolog import (nothing to migrate elsewhere in wechaty-mixins).
Aligns with the wechaty-puppet re-release under 1.0.148 (the earlier
1.0.147 slot went to cache-hardening from main, not pluggable-logger).
Aligns with puppet-service re-release under 1.0.124 (its 1.0.123 slot
went to cache-hardening from main, not pluggable-logger). Pin style
preserved.
Slot 1.0.159 was published from main (cache-hardening pipeline) without
the pluggable-logger changes. Re-release under 1.0.160.
Same fix as juzibot/wechaty-puppet#105 — unpinned @types/node ships
newer syntax (e.g. 'using' declarations) that TS 4.7.4 cannot parse.
Aligning the smoke test with the dev pin (^20.8.6) keeps CI green.
@hcfw007
hcfw007 merged commit dfe4968 into main Jul 1, 2026
15 of 20 checks passed
@hcfw007
hcfw007 deleted the feat/pluggable-logger branch July 1, 2026 15:33
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