Logging
Setting the log level, and the reason DevTools links every entry to your code instead of to the library.
Four functions, one level, one switch:
import { LogLevel, configureLogger, debug, info, warn, error } from "@sinequa/atomic";
configureLogger({ level: LogLevel.DEBUG });
debug("query sent", { name: "<your-query>" });LogLevel is DEBUG (0), INFO (1), WARN (2), ERROR (3), NONE (4) — and the default is INFO, so
debug() is silent until you ask for it.
Why the source link is right
This is the part worth understanding, because it explains a constraint further down.
The four functions are not wrappers. They are the bound console methods, reassigned by configureLogger:
The browser attributes a console entry to the frame that called console.*. Routing every level through one
helper made every line of every application link to logger.ts, and the real caller was unreachable. Exporting
the bound method makes the caller the invoking frame.
Two consequences follow, and both are observable:
- The level prefix is a separate argument —
console.error("[ERROR]", message, data)rather than a concatenated string. It renders the same, but a%cor%sdirective inside your message is no longer interpreted, and a call with nodatano longer logs a trailing"". - The console method is captured when the binding is built. Anything replacing
console.*afterwards is bypassed — see the pitfalls, this one bites in tests.
Turning it down in production
import { LogLevel, configureLogger } from "@sinequa/atomic";
const isProduction = process.env.NODE_ENV === "production";
// Call this before anything else logs — the library logs during sign-in.
configureLogger({
level: isProduction ? LogLevel.WARN : LogLevel.DEBUG,
enableConsoleOutput: true,
});import { provideEnvironmentInitializer } from "@angular/core";
import { LogLevel, configureLogger } from "@sinequa/atomic";
import { environment } from "../environments/environment";
/**
* Runs before the application initializers, so the level is in force by the time
* the sign-in starts logging.
*
* Note: `logLevel` on the client configuration is read by nothing — this is the
* only thing that sets the level.
*/
export function provideLogging() {
return provideEnvironmentInitializer(() => {
configureLogger({
level: environment.production ? LogLevel.WARN : LogLevel.DEBUG,
enableConsoleOutput: true,
});
});
}import { LogLevel, configureLogger } from "@sinequa/atomic";
/**
* Imported first in the entry point, before the client module — the library logs
* during sign-in, and the level has to be in force by then.
*/
configureLogger({
level: import.meta.env.PROD ? LogLevel.WARN : LogLevel.DEBUG,
enableConsoleOutput: true,
});What the library logs, and where
Worth knowing when a level is too quiet to diagnose something:
| Level | What |
|---|---|
debug | Best-effort failures it deliberately absorbs — a CSRF probe that failed, a recovery hook that threw, a refused cookie exchange in bearer mode |
info | Decisions with visible consequences — falling back to a credentials form, continuing on the token alone |
warn | A stored token that could not be parsed, an impersonation refusal |
error | A logout the server did not confirm, empty credentials |
The info lines are the ones to read first when a sign-in behaves unexpectedly: the fallback to a credentials
form is announced there rather than happening silently.
Injecting a logger instead
configureLogger is global. For a per-client logger — two backends, or a test asserting on what was logged —
pass one through the configuration:
const client = createAtomicClient({
app: "<your-app>",
auth: {
logger: {
debug: (message, data) => sink.push(["debug", message, data]),
info: (message, data) => sink.push(["info", message, data]),
warn: (message, data) => sink.push(["warn", message, data]),
error: (message, data) => sink.push(["error", message, data]),
},
},
});That is the AtomicLogger interface, and it is what the authentication layer uses. Without one it falls back to
the module-level functions — through getters, so the fallback follows a later configureLogger rather than
freezing the level in force when the module was evaluated.