@autotracer/logger
v1.0.0-alpha.33
Published
Logging library for auto-tracer
Readme
@autotracer/logger
Structured, theme-able logging for JavaScript applications.
A lightweight, zero-dependency logging library with named instances, configurable themes, performance tracking, and tree-style nested output—perfect for debugging complex applications.
What You Get
- Named logger instances - Create independent loggers per module/component
- Flexible theming - Console groups, colored text, or plain output
- Performance tracking - Built-in enter/exit timing
- Tree visualization - See call hierarchies at a glance
- Zero dependencies - Pure JavaScript with TypeScript support
Features
- Named Logger Instances: Create multiple independent loggers with
getLogger(name) - Per-Logger Configuration: Each logger has its own log level, theme, and display settings
- Global Log Level Control: Set minimum log level once, filter all output automatically
- 8 Log Levels:
fatal,error,warn,log,info,debug,verbose,trace - Themed Output: Choose between
console.groupor UTF-8 text modes with optional colors and prefixes - Safe Console Wrappers: Never throws, even if console methods fail
- Performance Tracking:
enter/exitwith automatic timing measurement and mismatch detection - Tree-Style Grouping: Nested logging with visual indentation (└─, ├─, │)
- Zero Dependencies: Pure JavaScript with TypeScript definitions
- 100% Test Coverage: Fully tested with Vitest
Installation
pnpm add @autotracer/logger
# or
npm install @autotracer/logger
# or
yarn add @autotracer/loggerQuick Start
Named Loggers (Recommended)
import { getLogger, themes } from "@autotracer/logger";
// Create a named logger
const logger = getLogger("app");
// Configure this logger independently
logger.setLogLevel("debug");
logger.setTheme(themes.emoji);
logger.setShowName(true); // Show [app] prefix
// Log messages
logger.log("Application started");
logger.debug("Debug info");
// Create another independent logger
const dbLogger = getLogger("database");
dbLogger.setLogLevel("error"); // Only errors
dbLogger.setShowName(false); // Hide [database] prefixGlobal API
import { log, setLogLevel, setTheme, themes } from "@autotracer/logger";
// Set global log level (default: "log")
setLogLevel("debug");
// Use themed output
setTheme(themes.emoji);
// Log messages
log("Application started");Named Logger Instances
Named loggers allow you to create multiple independent logger instances, each with its own configuration. This is useful for:
- Module-specific logging (e.g., "database", "api", "auth")
- Component-level filtering (show only errors from "payment" module)
- Per-logger themes (emoji for development logs, minimal for system logs)
Creating Loggers
import { getLogger } from "@autotracer/logger";
const appLogger = getLogger("app");
const dbLogger = getLogger("database");
const apiLogger = getLogger("api");
// Calling getLogger with the same name returns the same instance
const appLogger2 = getLogger("app");
console.log(appLogger === appLogger2); // trueLogger API
Each logger instance has the following methods:
Configuration:
setLogLevel(level: LogLevel): void- Set minimum log level for this loggersetTheme(theme: Theme): void- Set theme for this loggersetShowName(show: boolean): void- Show/hide logger name prefix
Logging Methods:
fatal(...args: unknown[]): voiderror(...args: unknown[]): voidwarn(...args: unknown[]): voidlog(...args: unknown[]): voidinfo(...args: unknown[]): voiddebug(...args: unknown[]): voidverbose(...args: unknown[]): voidtrace(...args: unknown[]): void
Grouping:
group(label?: string): voidgroupEnd(): void
Performance Tracking:
enter(label: string, ...optionalParams: unknown[]): ExitHandleexit(handle: ExitHandle, ...optionalParams: unknown[]): voidenterStyled(rawLabel: string, styledLabel: string, ...optionalParams: unknown[]): StyledExitHandleexitStyled(handle: StyledExitHandle, styledLabel: string, ...optionalParams: unknown[]): void
Example: Per-Module Configuration
import { getLogger, themes } from "@autotracer/logger";
// Development logger - verbose, with emoji
const devLogger = getLogger("dev");
devLogger.setLogLevel("trace");
devLogger.setTheme(themes.emoji);
devLogger.setShowName(true);
devLogger.trace("Detailed trace"); // → 🔍 [dev] Detailed trace
devLogger.debug("Debug info"); // → 🐛 [dev] Debug info
devLogger.log("Standard message"); // → 📝 [dev] Standard message
// Production logger - errors only, minimal style
const prodLogger = getLogger("prod");
prodLogger.setLogLevel("error");
prodLogger.setTheme(themes.minimal);
prodLogger.setShowName(false);
prodLogger.trace("Hidden"); // (not shown)
prodLogger.error("Critical issue"); // → [ERROR] Critical issue
// Database logger - info level, monochrome
const dbLogger = getLogger("database");
dbLogger.setLogLevel("info");
dbLogger.setTheme(themes.monochrome);
dbLogger.setShowName(true);
dbLogger.debug("Query details"); // (not shown - below info)
dbLogger.info("Connection opened"); // → [i] [database] Connection openedExample: Component-Level Filtering
import { getLogger } from "@autotracer/logger";
function processPayment(amount: number) {
const logger = getLogger("payment");
logger.setLogLevel("info");
logger.info("Processing payment:", amount);
logger.debug("Detailed payment info"); // Hidden (below info)
logger.error("Payment failed"); // Shown
}
function renderUI() {
const logger = getLogger("ui");
logger.setLogLevel("debug"); // More verbose for UI
logger.debug("Rendering component"); // Shown
logger.trace("Render timing details"); // Hidden (below debug)
}API Reference
Named Logger API
Creating Loggers:
import { getLogger } from "@autotracer/logger";
const logger = getLogger("module-name");Logger Interface:
Each logger has these methods:
- Configuration:
setLogLevel(),setTheme(),setShowName() - Logging:
fatal(),error(),warn(),log(),info(),debug(),verbose(),trace() - Grouping:
group(),groupEnd() - Tracking:
enter(),exit()
See "Named Logger Instances" section above for detailed examples.
Global API
You can also use standalone functions instead of logger instances.
Log Levels
Log levels form a hierarchy. Setting a level enables all messages at that level and below:
fatal < error < warn < log < info < debug < verbose < traceFunctions:
fatal(...args: unknown[]): void- Critical failures (always shown)error(...args: unknown[]): void- Errorswarn(...args: unknown[]): void- Warningslog(...args: unknown[]): void- Standard outputinfo(...args: unknown[]): void- Informational messagesdebug(...args: unknown[]): void- Debug outputverbose(...args: unknown[]): void- Verbose detailstrace(...args: unknown[]): void- Finest-grained tracing
Example:
import {
setLogLevel,
fatal,
error,
info,
debug,
trace,
} from "@autotracer/logger";
setLogLevel("info");
fatal("Critical error"); // ✅ Shown
error("Error occurred"); // ✅ Shown
info("Info message"); // ✅ Shown
debug("Debug details"); // ❌ Hidden
trace("Trace data"); // ❌ HiddenGlobal Log Level
Set the minimum log level:
import { setLogLevel } from "@autotracer/logger";
setLogLevel("debug"); // Show debug, info, log, warn, error, fatal (hide verbose, trace)Get the current log level:
import { getLogLevel } from "@autotracer/logger";
const level = getLogLevel(); // Returns LogLevelGrouping
Create collapsible or visually nested log sections:
Functions:
group(label?: string): void- Start a groupgroupEnd(): void- End the current group
Example (console.group mode):
import { group, groupEnd, log, setLogLevel } from "@autotracer/logger";
setLogLevel("log");
group("Processing items");
log("Item 1");
log("Item 2");
groupEnd();Example (text mode with UTF-8 box drawing):
import { setTheme, themes, group, groupEnd, log } from "@autotracer/logger";
setTheme(themes.minimal);
group("Outer");
log("Level 1");
group("Inner");
log("Level 2");
groupEnd();
groupEnd();
// Output:
// ├─ Outer
// │ Level 1
// │ ├─ Inner
// │ │ Level 2Theming
Themes control visual output with colors and prefixes using console %c directives. Each theme has three properties:
- groupMode:
"default"(console.group) or"text"(UTF-8 box-drawing) - colors: CSS color values (e.g.,
#ff0000) applied via%cdirective for each log level - prefixes: Text/emoji prefixes prepended to log messages
Set a theme:
import { setTheme, themes } from "@autotracer/logger";
setTheme(themes.emoji);Get the current theme:
import { getTheme } from "@autotracer/logger";
const theme = getTheme();Built-in Themes
1. default - No styling
setTheme(themes.default);
// Minimal overhead, raw console output
log("Hello"); // → Hello2. minimal - Text prefixes only
setTheme(themes.minimal);
// Text prefixes, no colors
fatal("Critical error"); // → [FATAL] Critical error
error("Error occurred"); // → [ERROR] Error occurred
warn("Warning message"); // → [WARN] Warning message
log("Log message"); // → [LOG] Log message
info("Info message"); // → [INFO] Info message
debug("Debug data"); // → [DEBUG] Debug data
verbose("Verbose output"); // → [V] Verbose output
trace("Trace detail"); // → [T] Trace detail3. emoji - Colors + emoji prefixes
setTheme(themes.emoji);
// console.group mode with colored emoji prefixes
fatal("Critical"); // → 🔥 Critical (red: #ff0000)
error("Error"); // → ❌ Error (red: #ff4444)
warn("Warning"); // → ⚠️ Warning (yellow: #ffaa00)
log("Message"); // → 📝 Message (gray: #888888)
info("Info"); // → ℹ️ Info (blue: #00aaff)
debug("Debug"); // → 🐛 Debug (cyan: #00ffaa)
verbose("Verbose"); // → 💬 Verbose (gray: #999999)
trace("Trace"); // → 🔍 Trace (gray: #666666)
const h = enter("Task"); // → ▶️ Task
// ... work ...
exit(h); // → ◀️ Task (elapsed: 12.34ms)4. monochrome - ASCII prefixes, no colors
setTheme(themes.monochrome);
// ASCII-safe prefixes for environments without color support
fatal("Critical"); // → [X] Critical
error("Error"); // → [!] Error
warn("Warning"); // → [*] Warning
log("Message"); // → [ ] Message
info("Info"); // → [i] Info
debug("Debug"); // → [d] Debug
verbose("Verbose"); // → [v] Verbose
trace("Trace"); // → [t] TraceHow Theme Styling Works
Themes apply colors using the console %c directive:
// When theme.colors.error = "#ff0000" and theme.prefixes.error = "❌"
error("Something failed", data);
// Outputs to console as:
console.error("%c❌ Something failed", "color: #ff0000", data);User-provided %c directives coexist with theme styling:
setTheme(themes.emoji);
error("Status: %cOK%c", "color: green", "");
// → ❌ Status: OK (emoji is red, OK is green)The theme's %c directive is always first, followed by user-provided directives in order.
Custom theme:
import { setTheme } from "@autotracer/logger";
import type { Theme } from "@autotracer/logger";
const myTheme: Theme = {
groupMode: "text",
colors: {
error: "#FF0000",
warn: "#FFA500",
},
prefixes: {
error: "[ERR]",
warn: "[WRN]",
enter: ">>",
exit: "<<",
},
};
setTheme(myTheme);Built-in Themes
themes.default (default)
- groupMode:
"default"(console.group) - No colors or prefixes
themes.minimal
- groupMode:
"text" - Text prefixes:
[FATAL],[ERROR],[WARN],→,←
themes.emoji
- groupMode:
"default" - HTML colors for all levels
- Emoji prefixes: 🔥💥⚠️📝ℹ️🐛📊🔍▶◀
themes.monochrome
- groupMode:
"text" - ASCII prefixes:
[!],[X],[*],>,< - No colors
Performance Tracking
Track execution time of code sections with automatic timing and enter/exit matching.
Functions:
enter(label: string, level?: LogLevel): ExitHandle- Start performance trackingexit(handle: ExitHandle): void- End tracking and log elapsed time
Parameters:
label: Description of the tracked sectionlevel: Log level for enter/exit messages (default:"trace")
Returns:
ExitHandle: Object with{ label: string, startTime: number, level: LogLevel }
Example:
import { enter, exit, setLogLevel } from "@autotracer/logger";
setLogLevel("trace");
function processData() {
const handle = enter("processData");
// ... do work ...
exit(handle);
}
// Output:
// ▶ Enter: processData
// (nested logs here)
// ◀ Exit: processData (elapsed: 123.45 ms)Mismatch Detection:
If exit is called with a different label than the most recent enter, a warning is logged:
const h1 = enter("A");
const h2 = enter("B");
exit(h1); // ⚠️ Warning: Expected to exit "B", but got "A"Styled Enter/Exit API
The styled enter/exit API allows you to provide both raw and styled labels, enabling use cases where:
- The raw label is needed for identification/matching
- The styled label contains presentation formatting (icons, colors, etc.)
- Exit messages can be re-themed differently from enter messages
This is useful when building higher-level tracing libraries that need to apply different theming to enter vs. exit messages.
Functions:
enterStyled(rawLabel: string, styledLabel: string, ...optionalParams: unknown[]): StyledExitHandleexitStyled(handle: StyledExitHandle, styledLabel: string, ...optionalParams: unknown[]): void
Key Differences from enter/exit:
| Feature | enter/exit | enterStyled/exitStyled |
|---------|----------------|----------------------------|
| Label storage | Single label | Both raw and styled labels |
| Exit label | Reuses enter label | Caller provides exit label |
| Use case | Simple tracking | Custom theming per message |
| Handle type | ExitHandle | StyledExitHandle (extends ExitHandle with rawLabel) |
Example: Custom Enter/Exit Icons
import { getLogger } from "@autotracer/logger";
const logger = getLogger("tracer");
logger.setLogLevel("trace");
function myFunction() {
// Enter with → icon
const handle = logger.enterStyled(
"myFunction", // Raw label (for matching)
"→ myFunction", // Styled label (with icon)
"color: blue" // Optional styling
);
// ... do work ...
// Exit with ← icon (different from enter!)
logger.exitStyled(
handle,
"← myFunction", // Different styled label for exit
"color: green" // Different styling
);
}
// Output:
// → myFunction (in blue)
// ← myFunction (elapsed: 123ms) (in green)Example: Storing Parameters
const logger = getLogger("app");
const handle = logger.enterStyled(
"processData",
"%cprocessData",
"font-weight: bold",
{ id: 123 }
);
// Exit reuses stored params if no override provided
logger.exitStyled(handle, "%cprocessData completed");
// Output: processData completed (elapsed: 45ms) font-weight: bold {id: 123}
// OR override params
logger.exitStyled(handle, "%cprocessData completed", "color: green");
// Output: processData completed (elapsed: 45ms) color: greenUse Cases:
- Different enter/exit theming: Apply → for enter, ← for exit
- Custom formatting: Store raw name, display formatted version
- Conditional styling: Enter in blue, exit in green/red based on result
- Higher-level tracers: Build framework-specific tracers with custom icons/colors
Type: StyledExitHandle
interface StyledExitHandle extends ExitHandle {
readonly rawLabel: string; // Original unformatted label
}The StyledExitHandle extends ExitHandle with a rawLabel field that stores the original unformatted label, while label stores the styled version.
Types
LogLevel
type LogLevel =
| "fatal"
| "error"
| "warn"
| "log"
| "info"
| "debug"
| "verbose"
| "trace";Theme
interface Theme {
groupMode: "default" | "text";
colors: Partial<Record<LogLevel, string>>;
prefixes: Partial<Record<LogLevel | "enter" | "exit", string>>;
}ExitHandle
interface ExitHandle {
label: string;
startTime: number;
level: LogLevel;
}Usage Examples
Basic Logging
import { log, info, error } from "@autotracer/logger";
log("Server started on port 3000");
info("User logged in:", { userId: 123 });
error("Failed to connect:", new Error("Timeout"));Level Filtering
import { setLogLevel, debug, trace } from "@autotracer/logger";
setLogLevel("debug");
debug("This appears"); // ✅
trace("This is hidden"); // ❌ (trace is below debug)Nested Groups with Text Mode
import { setTheme, themes, group, groupEnd, log, debug } from "@autotracer/logger";
setTheme(themes.minimal);
group("HTTP Request");
log("URL: /api/users");
debug("Method: GET");
group("Headers");
log("Content-Type: application/json");
debug("Authorization: Bearer token");
groupEnd();
group("Response");
log("Status: 200");
debug("Body size: 1024 bytes");
groupEnd();
groupEnd();
// Output:
// ├─ HTTP Request
// │ URL: /api/users
// │ Method: GET
// │ ├─ Headers
// │ │ Content-Type: application/json
// │ │ Authorization: Bearer token
// │ ├─ Response
// │ │ Status: 200
// │ │ Body size: 1024 bytesNote: In text mode, all log messages (debug, info, log, etc.) inside a group are automatically indented to match the group nesting level. This provides clear visual hierarchy for nested operations.
Performance Tracking
import { enter, exit, setLogLevel } from "@autotracer/logger";
setLogLevel("debug");
async function fetchData() {
const h = enter("fetchData", "debug");
const response = await fetch("/api/data");
const data = await response.json();
exit(h);
return data;
}
// Output:
// ▶ Enter: fetchData
// ◀ Exit: fetchData (elapsed: 234.56 ms)Custom Theme
import { setTheme, fatal, error, warn } from "@autotracer/logger";
import type { Theme } from "@autotracer/logger";
const prodTheme: Theme = {
groupMode: "text",
colors: {},
prefixes: {
fatal: "[FATAL]",
error: "[ERROR]",
warn: "[WARN]",
},
};
setTheme(prodTheme);
fatal("System failure"); // [FATAL] System failure
error("Request failed"); // [ERROR] Request failed
warn("Deprecated API"); // [WARN] Deprecated APITesting
The package has 100% test coverage with 115 unit tests using Vitest.
Run tests:
pnpm --filter @autotracer/logger testBuild:
pnpm --filter @autotracer/logger buildLicense
MIT
