Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/client/createSorokitClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
/**
Expand Down Expand Up @@ -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 });

Expand Down
43 changes: 37 additions & 6 deletions src/shared/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<LogLevel, number> = {
Expand Down Expand Up @@ -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(),
}),
};
}

Expand Down Expand Up @@ -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. */
Expand Down
59 changes: 59 additions & 0 deletions src/tests/logger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", () => {
Expand Down Expand Up @@ -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({
Expand Down
Loading