From 94c7d4a1dacfc1aefd977ef971f98cc52508620e Mon Sep 17 00:00:00 2001 From: Tiara Rodney Date: Sat, 14 Mar 2026 00:23:09 +0100 Subject: [PATCH 1/3] feat(manager): set manager on created loggers, fix clear for placeholders getLogger() now assigns manager on newly created loggers so isEnabledFor can check manager.disable. When replacing a Placeholder, _fixupParents is also called to establish the parent chain. clear() skips Placeholder entries that lack a clear method. --- src/manager.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/manager.ts b/src/manager.ts index 1d6828d..ff73f5d 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -79,8 +79,10 @@ export class Manager { if (existing instanceof Placeholder) { rv = new (this._loggerClass ?? loggerClass)(scope, NOTSET); + rv.manager = this; this.loggers[scope] = rv; this._fixupChildren(existing, rv); + this._fixupParents(rv); } else { rv = existing; @@ -88,6 +90,7 @@ export class Manager { } else { rv = new (this._loggerClass ?? loggerClass)(scope, NOTSET); + rv.manager = this; this.loggers[scope] = rv; this._fixupParents(rv); } @@ -168,7 +171,9 @@ export class Manager { */ public clear() { Object.values(this.loggers).forEach((logger) => { - logger.clear() + if (!(logger instanceof Placeholder)) { + logger.clear(); + } }); } } From 5022a1e0bc24b933ee7fdd97192a4853891c7c4a Mon Sep 17 00:00:00 2001 From: Tiara Rodney Date: Sat, 14 Mar 2026 02:32:20 +0100 Subject: [PATCH 2/3] test(manager): add tests for manager assignment and propagation --- tests/unit/manager.test.ts | 83 +++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/tests/unit/manager.test.ts b/tests/unit/manager.test.ts index 2df1fbb..43765da 100644 --- a/tests/unit/manager.test.ts +++ b/tests/unit/manager.test.ts @@ -1,7 +1,9 @@ import {expect, jest, test} from '@jest/globals'; import { Manager } from '../../src/manager'; import { Logger, RootLogger } from '../../src/logger'; -import { WARNING, DEBUG, NOTSET } from '../../src/log-level'; +import { Handler } from '../../src/handler'; +import { LogRecord } from '../../src/log-record'; +import { WARNING, DEBUG, INFO, NOTSET } from '../../src/log-level'; function makeManager(): Manager { return new Manager(new RootLogger(WARNING)); @@ -117,6 +119,79 @@ describe('Manager', () => { }); }); + describe('getLogger - manager assignment', () => { + test('sets manager on newly created loggers', () => { + const manager = makeManager(); + const logger = manager.getLogger('app'); + expect(logger.manager).toBe(manager); + }); + + test('sets manager on loggers created from placeholders', () => { + const manager = makeManager(); + manager.getLogger('a.b.c'); + const mid = manager.getLogger('a.b'); + expect(mid.manager).toBe(manager); + }); + + test('isEnabledFor respects manager.disable via getLogger', () => { + const manager = makeManager(); + const logger = manager.getLogger('app'); + logger.setLevel(DEBUG); + + expect(logger.isEnabledFor(DEBUG)).toBe(true); + + manager.disable = WARNING; + logger.clear(); + + expect(logger.isEnabledFor(DEBUG)).toBe(false); + expect(logger.isEnabledFor(WARNING)).toBe(false); + }); + }); + + describe('propagation through hierarchy', () => { + test('child logger propagates records to parent handler', () => { + const manager = makeManager(); + const parent = manager.getLogger('app'); + const child = manager.getLogger('app.module'); + + parent.setLevel(DEBUG); + child.setLevel(DEBUG); + + const emitted: string[] = []; + const handler = new (class extends Handler { + emit(record: LogRecord) { + emitted.push(record.msg); + } + })(DEBUG); + + parent.addHandler(handler); + child.info('propagated message'); + + expect(emitted).toContain('propagated message'); + }); + + test('child does not propagate to unrelated logger', () => { + const manager = makeManager(); + const unrelated = manager.getLogger('other'); + const child = manager.getLogger('app.module'); + + unrelated.setLevel(DEBUG); + child.setLevel(DEBUG); + + const emitted: string[] = []; + const handler = new (class extends Handler { + emit(record: LogRecord) { + emitted.push(record.msg); + } + })(DEBUG); + + unrelated.addHandler(handler); + child.info('should not reach'); + + expect(emitted).not.toContain('should not reach'); + }); + }); + describe('clear', () => { test('clears logger caches', () => { const manager = makeManager(); @@ -124,5 +199,11 @@ describe('Manager', () => { logger.isEnabledFor(DEBUG); manager.clear(); }); + + test('does not throw when placeholders exist', () => { + const manager = makeManager(); + manager.getLogger('a.b.c'); + expect(() => manager.clear()).not.toThrow(); + }); }); }); From 743e8ffc3b6d3f4aeef641d19482bf7e3a784f09 Mon Sep 17 00:00:00 2001 From: Tiara Rodney Date: Sat, 14 Mar 2026 02:32:35 +0100 Subject: [PATCH 3/3] todo(9): done --- TODO | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO b/TODO index db94604..0e0bf86 100644 --- a/TODO +++ b/TODO @@ -140,7 +140,7 @@ Content-Type: application/issue ID: 9 Type: feature Title: implement Manager logger hierarchy -Status: in-progress +Status: done Priority: high Created: 2026-03-13 Relationships: dependsOn:5