diff --git a/src/client/createSorokitClient.ts b/src/client/createSorokitClient.ts index acffbb5..ce4556c 100644 --- a/src/client/createSorokitClient.ts +++ b/src/client/createSorokitClient.ts @@ -128,6 +128,12 @@ export interface SorokitClientConfig { * @deprecated Prefer `logLevel: "debug"` */ debug?: boolean; + /** + * Prefix for built-in console log lines. Defaults to `"[sorokit]"`. + * Use distinct values (e.g. `"[sorokit:testnet]"`) when running multiple clients. + * Ignored when a custom `logger` is provided. + */ + logPrefix?: string; /** Custom logger — overrides the built-in console logger */ logger?: SorokitLogger; /** @@ -534,6 +540,7 @@ export function createSorokitClient( config.logger ?? createLogger({ logLevel: config.logLevel ?? (config.debug ? "debug" : "off"), + ...(config.logPrefix !== undefined ? { prefix: config.logPrefix } : {}), }); const logger = createTracedLogger(baseLogger, { traceId }); diff --git a/src/shared/logger.ts b/src/shared/logger.ts index 658d435..e955cb3 100644 --- a/src/shared/logger.ts +++ b/src/shared/logger.ts @@ -20,6 +20,12 @@ export interface LoggerOptions { logLevel?: LogLevel; debug?: boolean; logger?: SorokitLogger; + /** + * Prefix prepended to every console log line for the built-in logger. + * Defaults to `"[sorokit]"`. Ignored when a custom `logger` is provided. + * Useful for distinguishing multiple client instances (e.g. `"[sorokit:testnet]"`). + */ + prefix?: string; } const LOG_LEVEL_PRIORITY: Record = { @@ -60,12 +66,36 @@ export function sanitizeLogMeta(meta?: StructuredLogMeta): StructuredLogMeta | u return sanitized; } -function createConsoleLogger(): SorokitLogger { +function createConsoleLogger(prefix = "[sorokit]"): SorokitLogger { return { - debug: (message, meta) => console.debug("[sorokit]", { level: "debug", message, ...meta, timestamp: new Date().toISOString() }), - info: (message, meta) => console.info("[sorokit]", { level: "info", message, ...meta, timestamp: new Date().toISOString() }), - warn: (message, meta) => console.warn("[sorokit]", { level: "warn", message, ...meta, timestamp: new Date().toISOString() }), - error: (message, meta) => console.error("[sorokit]", { level: "error", message, ...meta, timestamp: new Date().toISOString() }), + debug: (message, meta) => + console.debug(prefix, { + level: "debug", + message, + ...meta, + timestamp: new Date().toISOString(), + }), + info: (message, meta) => + console.info(prefix, { + level: "info", + message, + ...meta, + timestamp: new Date().toISOString(), + }), + warn: (message, meta) => + console.warn(prefix, { + level: "warn", + message, + ...meta, + timestamp: new Date().toISOString(), + }), + error: (message, meta) => + console.error(prefix, { + level: "error", + message, + ...meta, + timestamp: new Date().toISOString(), + }), }; } @@ -103,7 +133,8 @@ function createLevelLogger(level: LogLevel, sink: SorokitLogger): SorokitLogger export function createLogger(options?: LoggerOptions): SorokitLogger { const level: LogLevel = options?.logLevel ?? (options?.debug ? "debug" : "off"); if (level === "off") return createNoopLogger(); - return createLevelLogger(level, options?.logger ?? createConsoleLogger()); + const sink = options?.logger ?? createConsoleLogger(options?.prefix); + return createLevelLogger(level, sink); } /** Add trace identifiers to all entries emitted by a logger. */ diff --git a/src/tests/logger.test.ts b/src/tests/logger.test.ts index 39d152a..dedcedf 100644 --- a/src/tests/logger.test.ts +++ b/src/tests/logger.test.ts @@ -93,6 +93,28 @@ describe("shared/logger", () => { infoSpy.mockRestore(); }); + + it("applies a custom prefix to console output (#257)", () => { + const debugSpy = vi + .spyOn(console, "debug") + .mockImplementation(() => undefined); + const logger = createLogger({ + logLevel: "debug", + prefix: "[sorokit:testnet]", + }); + + logger.debug("prefixed"); + + expect(debugSpy).toHaveBeenCalledWith( + "[sorokit:testnet]", + expect.objectContaining({ + level: "debug", + message: "prefixed", + }), + ); + + debugSpy.mockRestore(); + }); }); describe("withLogging", () => { @@ -157,6 +179,43 @@ describe("createSorokitClient logger integration", () => { ); }); + it("uses logPrefix in console output when debug is enabled (#257)", () => { + const debugSpy = vi + .spyOn(console, "debug") + .mockImplementation(() => undefined); + + const result = createSorokitClient({ + network: "testnet", + debug: true, + logPrefix: "[app:testnet]", + cache: { + get: () => undefined, + set: () => undefined, + invalidate: () => undefined, + clear: () => undefined, + }, + }); + + expect(result.status).toBe("ok"); + // client.create emits info; cache probe emits debug — both use logPrefix + expect(consoleInfoSpy).toHaveBeenCalledWith( + "[app:testnet]", + expect.objectContaining({ + operation: "client.create", + status: "ok", + network: "testnet", + }), + ); + expect(debugSpy).toHaveBeenCalledWith( + "[app:testnet]", + expect.objectContaining({ + message: "client.create: checked cache for recovered wallet state", + }), + ); + + debugSpy.mockRestore(); + }); + it("does not log wallet.emptyState calls", () => { const { logger: custom, calls } = createCapturingLogger(); const result = createSorokitClient({