#22478: fix(diagnostics-otel): wire OTLP exporter to emit traffic to configured endpoint #18794
size: S
Cluster:
Plugin Enhancements and Fixes
**PR Title:** Fix logger singleton split across bundles and harden global logging state validation
## Summary
Describe the problem and fix in 2–5 bullets:
- Problem: `registerLogTransport` and logger cache/state could live in different bundled module copies, so transports registered from plugin-sdk were not reliably attached to the active logger.
- Why it matters: extension log exporters (for example OTEL log transport) could silently miss runtime logs even when configured correctly.
- What changed: moved logging singletons to process-global storage with `Symbol.for(...)` and added runtime validation/self-healing for both shared objects.
- What did NOT change (scope boundary): no config schema changes, no new CLI flags, no diagnostics event-bus or OTEL metrics/traces logic changes; only logging state/transport storage logic in `/Users/saurabh/Downloads/finalSetup/TheFixedVerion0/openclaw/src/logging/state.ts` and `/Users/saurabh/Downloads/finalSetup/TheFixedVerion0/openclaw/src/logging/logger.ts`.
## Change Type (select all)
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Docs
- [x] Security hardening
- [ ] Chore/infra
## Scope (select all touched areas)
- [x] Gateway / orchestration
- [ ] Skills / tool execution
- [ ] Auth / tokens
- [ ] Memory / storage
- [ ] Integrations
- [ ] API / contracts
- [ ] UI / DX
- [ ] CI/CD / infra
## Linked Issue/PR
- Closes #18794
## User-visible / Behavior Changes
- Log transports registered via plugin-sdk now reliably attach to the active logger in bundled/runtime-split builds.
- Invalid/poisoned global logging singleton values are repaired automatically at startup.
- No user-facing config/default changes.
## Security Impact (required)
- New permissions/capabilities? (`Yes/No`): No
- Secrets/tokens handling changed? (`Yes/No`): No
- New/changed network calls? (`Yes/No`): No
- Command/tool execution surface changed? (`Yes/No`): No
- Data access scope changed? (`Yes/No`): No
- If any `Yes`, explain risk + mitigation: N/A
## Repro + Verification
### Environment
- OS: macOS + Linux VPS
- Runtime/container: Node.js 22+
- Model/provider: N/A (logging transport path)
- Integration/channel (if any): plugin-sdk log transport path (diagnostics exporter scenario)
- Relevant config (redacted): logging + diagnostics enabled (standard)
### Steps
1. Build/install OpenClaw from source and enable a plugin transport registration path.
2. Start gateway/runtime where logger is initialized from one bundled entry.
3. Register transport from plugin-sdk path and generate logs.
### Expected
- Registered transport receives runtime logs consistently.
### Actual
- Before fix: transport could miss logs due to split singleton state.
- After fix: transport attaches and receives logs.
## Evidence
Attach at least one:
- [ ] Failing test/log before + passing after
- [x] Trace/log snippets
- [ ] Screenshot/recording
- [ ] Perf numbers (if relevant)
## Human Verification (required)
What you personally verified (not just CI), and how:
- Verified scenarios: plugin-sdk transport registration path receives logs after logger is already initialized.
- Edge cases checked: invalid existing global singleton value is rejected and replaced with a valid shape.
- What you did **not** verify: no additional changes outside `/Users/saurabh/Downloads/finalSetup/TheFixedVerion0/openclaw/src/logging/state.ts` and `/Users/saurabh/Downloads/finalSetup/TheFixedVerion0/openclaw/src/logging/logger.ts`.
## Compatibility / Migration
- Backward compatible? (`Yes/No`): Yes
- Config/env changes? (`Yes/No`): No
- Migration needed? (`Yes/No`): No
- If yes, exact upgrade steps: N/A
## Failure Recovery (if this breaks)
- How to disable/revert this change quickly: revert `/Users/saurabh/Downloads/finalSetup/TheFixedVerion0/openclaw/src/logging/state.ts` and `/Users/saurabh/Downloads/finalSetup/TheFixedVerion0/openclaw/src/logging/logger.ts` to previous module-local singleton behavior.
- Files/config to restore: only those two files.
- Known bad symptoms reviewers should watch for: duplicate logger state across chunks, transports not receiving logs, or missing file-log export hooks.
## Risks and Mitigations
List only real risks for this PR. Add/remove entries as needed. If none, write `None`.
- Risk: shared global singleton could be pre-populated with malformed data by in-process code.
- Mitigation: runtime shape validation and self-healing replacement logic.
- Risk: stale invalid transport set entries could break transport iteration.
- Mitigation: transport-set validation plus preserving only valid function entries during repair.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Moved logging singleton state and external transports to process-global storage using `Symbol.for(...)` to fix issues with logger state being split across bundled modules.
Key changes:
- Replaced module-local `externalTransports` Set with global singleton accessed via `Symbol.for("openclaw.externalLogTransports")` in `src/logging/logger.ts:38`
- Replaced module-local `loggingState` object with global singleton accessed via `Symbol.for("openclaw.loggingState")` in `src/logging/state.ts:1`
- Added runtime validation and self-healing for both singletons to detect and repair invalid global state
- Simplified `createRequire` usage by removing unnecessary `resolveNodeRequire` function
This ensures log transports registered via plugin-sdk reliably attach to the active logger even when multiple bundled copies exist.
<h3>Confidence Score: 4/5</h3>
- This PR is safe to merge with minimal risk
- The changes are well-structured and follow a clear pattern for moving singletons to global storage. The validation logic includes self-healing for invalid state. However, the validation in `isLoggingState` doesn't check for the existence of `cachedLogger`, `cachedSettings`, `cachedConsoleSettings`, and `overrideSettings` properties, which could theoretically cause issues if the global object is malformed (though the code appears resilient to this). The removal of `resolveNodeRequire` in favor of direct `createRequire` import is safe and simplifies the code.
- No files require special attention
<sub>Last reviewed commit: a4dc9f5</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#16865: fix(diagnostics-otel): share listeners/transports across module bun...
by leonnardo · 2026-02-15
85.8%
#12475: fix(logging): use Symbol.for for externalTransports to survive jiti...
by Yida-Dev · 2026-02-09
82.7%
#11281: fix(logging): prevent subsystem loggers from bypassing file log lev...
by janckerchen · 2026-02-07
79.1%
#19353: fix(diagnostics-otel): fix cross-chunk module isolation breaking even…
by nez · 2026-02-17
78.6%
#22475: fix(logging): correct levelToMinLevel mapping to match tslog numbering
by ronaldslc · 2026-02-21
76.3%
#23019: fix(hooks): use globalThis singleton for internal hooks handlers Map
by karmafeast · 2026-02-21
76.0%
#4255: fix(diagnostics-otel): complete OpenTelemetry v2.x compatibility
by arbgjr · 2026-01-29
75.6%
#23669: refactor(logging): migrate node-host and tailscale console calls to...
by kevinWangSheng · 2026-02-22
75.4%
#18179: CLI: add sessions --json-debug diagnostics
by p6l-richard · 2026-02-16
75.2%
#20402: Pr/load openclaw plugins async
by ramarnat · 2026-02-18
74.8%