workleap-logging
Workleap Logging (@workleap/logging)
A structured logging library for Workleap frontend applications. Provides composable, styled console logging with support for scopes, multiple log levels, and integration with telemetry tools.
Installation
pnpm add @workleap/logging
Core Concepts
Loggers
- BrowserConsoleLogger: Outputs to browser console with styling support
- CompositeLogger: Forwards logs to multiple underlying loggers
Log Levels (lowest to highest severity)
debug- Detailed diagnostic info for developmentinformation- General application flow eventswarning- Non-critical issues needing attentionerror- Failures preventing functionalitycritical- Severe errors risking data integrity
Scopes
Group related log entries under a label. Useful for tracing operations or correlating events.
IMPORTANT: Only a RootLogger instance can start a scope. If the logger is typed as a Logger (e.g., when using useLogger() from Squide), you must cast it to RootLogger before starting a scope:
// Squide example:
import { useLogger } from "@squide/firefly";
import type { RootLogger } from "@workleap/logging";
const logger = useLogger();
(logger as RootLogger).startScope("User signup");
API Reference
BrowserConsoleLogger
import { BrowserConsoleLogger, LogLevel } from "@workleap/logging";
// Basic usage
const logger = new BrowserConsoleLogger();
// With minimum log level
const logger = new BrowserConsoleLogger({ logLevel: LogLevel.information });
CompositeLogger
import { BrowserConsoleLogger, CompositeLogger } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"
const logger = new CompositeLogger([
new BrowserConsoleLogger(),
new LogRocketLogger()
]);
Logger Methods
Simple logging:
logger.debug("message");
logger.information("message");
logger.warning("message"); // or logger.warn("message")
logger.error("message");
logger.critical("message");
Chained segments (complete chain with log method):
logger
.withText("Processing order")
.withObject({ orderId: 123 })
.withError(new Error("Failed"))
.error();
Styled text:
logger.withText("Success", {
style: { color: "green", fontWeight: "bold" }
}).information();
Line breaks:
logger
.withText("Line 1")
.withLineChange()
.withText("Line 2")
.debug();
Scopes
const scope = logger.startScope("User signup");
scope.information("Form loaded");
scope.debug("Validating...");
scope.withText("Failed").withError(err).error();
// Output all scope entries
scope.end();
// Or dismiss without output
scope.end({ dismiss: true });
Styled scope labels:
// At creation
const scope = logger.startScope("Label", {
labelStyle: { backgroundColor: "purple", color: "white" }
});
// At end (useful for status-based styling)
scope.end({
labelStyle: { backgroundColor: "green", color: "white" }
});
createCompositeLogger
Factory function to create a CompositeLogger instance from Workleap libraries standard logging API.
import { createCompositeLogger, BrowserConsoleLogger } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"
const logger = createCompositeLogger(false, [new BrowserConsoleLogger(), new LogRocketLogger()]);
Parameters:
verbose: Whether debug information should be logged. If no loggers are provided, creates with aBrowserConsoleLoggerby default.loggers: Array of loggers to create theCompositeLoggerwith.
LogRocket Integration
By default, LogRocket session replays exclude console output. To send log entries to LogRocket, use LogRocketLogger from @workleap/telemetry or @workleap/logrocket:
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"
const logger = new LogRocketLogger();
logger.debug("Application started!");
Use CompositeLogger to send logs to both browser console and LogRocket:
import { BrowserConsoleLogger, CompositeLogger } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"
const logger = new CompositeLogger([
new BrowserConsoleLogger(),
new LogRocketLogger()
]);
logger.debug("Application started!"); // Processed by both loggers
Common Patterns
Application logger setup
import { BrowserConsoleLogger, CompositeLogger, LogLevel } from "@workleap/logging";
const isDev = process.env.NODE_ENV === "development";
const logger = new BrowserConsoleLogger({
logLevel: isDev ? LogLevel.debug : LogLevel.information
});
Error logging
try {
await processOrder(orderId);
} catch (error) {
logger
.withText("Failed to process order")
.withObject({ orderId })
.withError(error as Error)
.error();
}
Feature/operation scoping
async function registerModule(moduleName: string) {
const scope = logger.startScope(`${moduleName} registration`);
try {
scope.debug("Registering routes...");
await registerRoutes();
scope.debug("Routes registered");
scope.debug("Fetching data...");
await fetchData();
scope.debug("Data loaded");
scope.end({ labelStyle: { color: "green" } });
} catch (error) {
scope.withText("Registration failed").withError(error as Error).error();
scope.end({ labelStyle: { color: "red" } });
throw error;
}
}
Multi-destination logging
import { BrowserConsoleLogger, CompositeLogger, LogLevel } from "@workleap/logging";
import { LogRocketLogger } from "@workleap/telemetry"; // or from "@workleap/logrocket"
const logger = new CompositeLogger([
new BrowserConsoleLogger({
logLevel: LogLevel.error
}),
new LogRocketLogger({
logLevel: LogLevel.debug
})
]);
Log Level Guidelines
| Environment | Recommended Level |
|---|---|
| Development | debug |
| Production | information |
PR Review Checklist
When reviewing logging changes:
- Verify appropriate log levels (debug for diagnostics, error for failures)
- Check that errors include context (withObject) and stack traces (withError)
- Ensure scopes are properly ended (end() or end({ dismiss: true }))
- Confirm no sensitive data in log messages
- Verify CompositeLogger filters are set per environment
Common Mistakes
- Forgetting to call log method: Chained segments require
.debug(),.error(), etc. to output - Not ending scopes: Always call
scope.end()or logs won't output - Using wrong log level: Use
errorfor failures, notwarning - Logging sensitive data: Never log passwords, tokens, or PII
- Missing error context: Always include
withObject()for relevant data andwithError()for exceptions - Calling startScope on a non-RootLogger: Only
RootLoggerinstances can start scopes. When usinguseLogger()from Squide, cast toRootLoggerfirst:(logger as RootLogger).startScope("Label")