openclaw - ✅(Solved) Fix [Bug]: Subsystem File Logger Writes to Stale Date-Rolled Log File [4 pull requests, 2 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#62381Fetched 2026-04-08 03:05:10
View on GitHub
Comments
2
Participants
1
Timeline
11
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×4referenced ×4commented ×2labeled ×1

Subsystem File Logger Writes to Stale Date-Rolled Log File。 Root cause: createSubsystemLogger() caches its fileLogger (a tslog child logger) in a closure variable. Once assigned on first use, it is never refreshed. The cached child logger inherits the parent logger's file transport, which is bound to the log file path resolved at build time (e.g., openclaw-2026-04-03.log).

Root Cause

Subsystem File Logger Writes to Stale Date-Rolled Log File。 Root cause: createSubsystemLogger() caches its fileLogger (a tslog child logger) in a closure variable. Once assigned on first use, it is never refreshed. The cached child logger inherits the parent logger's file transport, which is bound to the log file path resolved at build time (e.g., openclaw-2026-04-03.log).

Fix Action

Fix / Workaround

Observed behavior: In a long-running gateway process started on April 3rd with logging.level: debug, the diagnostic subsystem (created at module top-level) continued writing to openclaw-2026-04-03.log on April 7th. Meanwhile, plugin loggers like feishu/card/reply-dispatcher — whose subsystem logger instances were created after the parent rebuild — correctly wrote to openclaw-2026-04-07.log.

PR fix notes

PR #62415: fix(logging): refresh subsystem file logger on parent date-roll rebuild (#62381)

Description (problem / solution / changelog)

Summary

Subsystem file loggers write to stale date-stamped log files in long-running gateway processes because the cached child logger is never refreshed after the parent logger rebuilds for a new date.

Root Cause

createSubsystemLogger() in src/logging/subsystem.ts caches its fileLogger (a tslog child logger) in a closure variable. Once assigned on first use (line 326-328), it is never refreshed. The cached child inherits the parent logger's file transport bound to the log file path resolved at build time (e.g. openclaw-2026-04-03.log). When the parent logger rebuilds for a new date, the stale child keeps writing to the old file.

Changes

  • src/logging/subsystem.ts: Track the parent logger reference (loggingState.cachedLogger) alongside the cached child. On each file log call, compare the current parent reference — if it changed (date-roll rebuild), refresh the child logger via getChildLogger(). Extract a getFileLogger() helper to centralize this logic for both emitLog and raw.
  • src/logging/subsystem.test.ts: Add regression tests verifying child logger refresh on parent rebuild and stable reuse when parent hasn't changed.

Test

pnpm test src/logging/subsystem.test.ts — 13 tests pass (exit 0)

Closes #62381

Changed files

  • src/logging/subsystem.test.ts (modified, +60/-2)
  • src/logging/subsystem.ts (modified, +15/-8)

PR #62458: fix(logging): refresh subsystem file logger when parent logger is rebuilt on date roll

Description (problem / solution / changelog)

Fixes #62381

Problem

createSubsystemLogger() caches its tslog child logger in a closure variable (fileLogger). Once created on first use, it is never refreshed. The child logger inherits the parent logger's file transport, which is bound to the log file path resolved at creation time (e.g. openclaw-2026-04-03.log).

When the gateway runs across a date boundary, the parent logger in getLogger() detects the file path change (via defaultRollingPathForToday()settingsChanged()) and rebuilds itself with the new date-rolled file. However, subsystem loggers still hold a reference to a child of the old parent, so they keep writing to the stale file indefinitely.

Root Cause

The staleness chain:

  1. createSubsystemLogger("diagnostic") is called at module top-level
  2. First log call → getChildLogger() → creates child of current parent logger → cached in closure
  3. Date rolls → getLogger() rebuilds parent with new file path
  4. Subsystem logger still uses cached child of old parent → writes to old file
  5. Only subsystem loggers created after the rebuild (e.g. plugin loggers) pick up the new file

Fix

Add a loggerGeneration counter to loggingState:

  • logger.ts: increment loggerGeneration every time getLogger() rebuilds the parent logger (settings changed → new file path, level change, etc.)
  • subsystem.ts: track the generation when the child file logger was created. Before each log write, compare the cached generation against the current loggingState.loggerGeneration. On mismatch, discard the stale child and request a fresh one from the new parent.

This is a lightweight check (one integer comparison per log call) with zero overhead when no rebuild has occurred.

Files changed

FileChange
src/logging/state.tsAdd loggerGeneration counter
src/logging/logger.tsIncrement generation on parent rebuild
src/logging/subsystem.tsTrack generation, refresh child on mismatch
src/logging/subsystem.test.tsAdd staleness and reuse tests

Test Plan

  • Added: "picks up a new parent logger after the parent is rebuilt (generation bump)" — verifies generation advances after reset+rebuild
  • Added: "reuses the cached child file logger when generation has not changed" — verifies no unnecessary child recreation
  • All existing subsystem logger tests pass

Changed files

  • src/logging/logger.ts (modified, +20/-0)
  • src/logging/state.ts (modified, +1/-0)
  • src/logging/subsystem.test.ts (modified, +43/-0)
  • src/logging/subsystem.ts (modified, +17/-9)

PR #62449: fix(logging):Subsystem File Logger Writes to Stale Date-Rolled Log File (Closes #62381)

Description (problem / solution / changelog)

Fixes https://github.com/openclaw/openclaw/issues/62381

Summary

  • Problem: Subsystem loggers (e.g. diagnostic, plugins) cache their tslog child logger on first use. The child logger inherits the root logger's file transport, which captures the log file path at build time. After a midnight date rollover, the transport closure still writes to the stale date-stamped file (e.g. openclaw-2026-04-03.log) instead of the current day's file.
  • Why it matters: In long-running gateway processes, all log lines (including diagnostic events with current timestamps) silently accumulate in the wrong file after midnight. Operators looking at today's log file see no output, making production debugging impossible.
  • What changed: The file transport closure inside buildLogger() now checks defaultRollingPathForToday() on each write and switches currentFile automatically when the date rolls over, resetting byte counts and size-cap warnings.
  • What did NOT change (scope boundary): No changes to subsystem logger caching, console logging, config resolution, or any code outside src/logging/logger.ts. The subsystem logger layer is completely unaware of the fix.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #
  • Related #
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: buildLogger() captured settings.file (the date-stamped log path) as an immutable value in the transport closure. After midnight, defaultRollingPathForToday() returns a new path, but the transport closure continued writing to the old path. Subsystem loggers cached their child logger indefinitely, so they never triggered a root logger rebuild that would create a new transport with the updated path.
  • Missing detection / guardrail: No mechanism existed to detect date changes within the transport's write path. The rolling filename logic only ran at logger construction time, not at write time.
  • Contributing context (if known): The bug is only observable in long-running processes (gateway) that span midnight. Short-lived CLI commands are unaffected.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/logging/subsystem.test.ts
  • Scenario the test should lock in: Subsystem logger continues to function correctly after root logger setup, validating cached child logger reuse.
  • Why this is the smallest reliable guardrail: The date-rolling switch happens inside a fs.appendFileSync transport closure that is difficult to unit-test without filesystem mocking. The smoke test validates the logger pipeline end-to-end in test env.
  • Existing test that already covers this (if any): None — this is a new test.
  • If no new test is added, why not: A new smoke test is added. A true midnight-crossover test would require mocking new Date() inside the transport closure and verifying file paths, which is better suited for an integration test.

User-visible / Behavior Changes

None. Log files will now correctly rotate at midnight in long-running gateway processes. No config or CLI changes.

Diagram (if applicable)

Before:
[midnight crossover] -> transport still holds stale "openclaw-2026-04-03.log"
  -> all writes go to old file -> today's file is empty

After:
[midnight crossover] -> transport detects defaultRollingPathForToday() changed
  -> switches currentFile to "openclaw-2026-04-04.log" -> writes go to correct file

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Linux (sandbox-ide container, production gateway)
  • Runtime/container: Node 24.14.0
  • Model/provider: N/A
  • Integration/channel (if any): Feishu (but affects all channels)
  • Relevant config (redacted): logging.level: debug

Steps

  1. Start openclaw gateway with logging.level: debug on April 3
  2. Wait until April 7 (process stays running)
  3. Check openclaw-2026-04-03.log — find log lines with "date":"2026-04-07T..." timestamps
  4. Check openclaw-2026-04-07.log — file is empty or missing

Expected

  • Logs with April 7 timestamps appear in openclaw-2026-04-07.log

Actual

  • Logs with April 7 timestamps written to openclaw-2026-04-03.log; today's file is empty

Evidence

  • Trace/log snippets

Production evidence: diagnostic log entry in openclaw-2026-04-03.log with "date":"2026-04-07T06:04:35.236Z", confirming the transport was writing to a 4-day-old file.

Human Verification (required)

  • Verified scenarios: Code review of transport closure lifecycle; confirmed defaultRollingPathForToday() returns date-dependent path; confirmed tslog child loggers propagate writes to parent transport; confirmed appendLogLine uses the closure variable currentFile.
  • Edge cases checked: Non-rolling paths (custom logging.file) — useRolling is false, new code path is skipped entirely. Silent level — transport is not attached, no change. Size cap after date switch — warnedAboutSizeCap and currentFileBytes both reset correctly.
  • What you did NOT verify: Actual midnight crossover in a live long-running process (requires multi-day test).

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: defaultRollingPathForToday() is called on every log write when using rolling filenames, adding a new Date() + string concatenation per write.
    • Mitigation: This is microsecond-level overhead, negligible compared to the existing JSON.stringify + fs.appendFileSync in the same transport. The call is skipped entirely for non-rolling paths (useRolling === false).

Changed files

  • src/logging/logger.ts (modified, +29/-11)
  • src/logging/subsystem.test.ts (modified, +58/-0)

PR #62482: fix: refresh subsystem file loggers across daily log rollovers [AI-assisted]

Description (problem / solution / changelog)

Summary

This PR fixes #62381 by ensuring subsystem loggers do not stay bound to a stale date-rolled file transport.

What changed

  • stops caching a stale child file logger inside createSubsystemLogger
  • refreshes the subsystem child logger before each file write so it follows the current day-rolled transport
  • adds a regression test that simulates the parent logger switching files across a day boundary

Why

Before this change, long-running subsystem loggers could keep writing to the original day's log file even after the parent logger rebuilt its file transport for a new day. After this change, subsystem logs follow the current parent file transport and land in the current day's log file.

Testing

  • Ran corepack pnpm exec vitest run --config vitest.logging.config.ts src/logging/subsystem.test.ts src/logging/subsystem.rollover.test.ts
  • Verified the rollover regression case in subsystem.rollover.test.ts

AI usage

This PR was AI-assisted. I reviewed the change manually, understand the implementation, and validated it locally.

Fixes #62381

Changed files

  • src/agents/subagent-registry.steer-restart.test.ts (modified, +11/-1)
  • src/logging/subsystem.rollover.test.ts (added, +103/-0)
  • src/logging/subsystem.ts (modified, +23/-8)
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

Subsystem File Logger Writes to Stale Date-Rolled Log File。 Root cause: createSubsystemLogger() caches its fileLogger (a tslog child logger) in a closure variable. Once assigned on first use, it is never refreshed. The cached child logger inherits the parent logger's file transport, which is bound to the log file path resolved at build time (e.g., openclaw-2026-04-03.log).

Steps to reproduce

Observed behavior: In a long-running gateway process started on April 3rd with logging.level: debug, the diagnostic subsystem (created at module top-level) continued writing to openclaw-2026-04-03.log on April 7th. Meanwhile, plugin loggers like feishu/card/reply-dispatcher — whose subsystem logger instances were created after the parent rebuild — correctly wrote to openclaw-2026-04-07.log.

Expected behavior

Users expect diagnostic logs were in the current-day file.

Actual behavior

Logs were wrote into initial log file (e.g., openclaw-2026-04-03.log), but now is 2026-04-07.

OpenClaw version

2026.3.8

Operating system

macos

Install method

docker

Model

minimax

Provider / routing chain

openclaw

Additional provider/model setup details

No response

Logs, screenshots, and evidence

Impact and severity

No response

Additional information

No response

extent analysis

TL;DR

The issue can be fixed by updating the createSubsystemLogger function to refresh the cached fileLogger instance daily.

Guidance

  • Identify the createSubsystemLogger function and its closure variable that caches the fileLogger instance.
  • Modify the function to refresh the fileLogger instance daily, ensuring it points to the current date-rolled log file.
  • Verify the fix by checking the log files after the update and confirming that new logs are written to the correct current-day file.
  • Consider implementing a mechanism to handle log file rotation, such as using a logging library that supports daily log rotation.

Example

// Pseudo-code example of refreshing the fileLogger instance daily
function createSubsystemLogger() {
  const today = new Date();
  const logFilePath = `openclaw-${today.getFullYear()}-${padZero(today.getMonth() + 1)}-${padZero(today.getDate())}.log`;
  const fileLogger = tslog.getChildLogger(logFilePath);
  // ...
}

function padZero(num) {
  return num.toString().padStart(2, '0');
}

Notes

The provided solution assumes that the tslog library is being used for logging. The actual implementation may vary depending on the specific logging library and framework used in the project.

Recommendation

Apply a workaround by modifying the createSubsystemLogger function to refresh the fileLogger instance daily, as this will ensure that logs are written to the correct current-day file without requiring a full version upgrade.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Users expect diagnostic logs were in the current-day file.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING