Scoped Loggers
Organize logs by context using scoped loggers
Last updated:
Scoped Loggers
Scoped loggers allow you to organize logs by context, making it easier to track messages from specific modules, features, or components.
Basic Scoping
Create a scoped logger by calling scope():
import { pail } from "@visulima/pail";
const authLogger = pail.scope("auth");
authLogger.info("User logged in"); // Includes scope: ["auth"]Multiple Scope Levels
You can nest scopes for hierarchical organization:
const apiLogger = pail.scope("api", "users");
apiLogger.info("Fetching user"); // Includes scope: ["api", "users"]Scoped Logger Instance
The scope() method returns a new logger instance with the scope applied:
const logger = createPail();
const scopedLogger = logger.scope("database");
scopedLogger.info("Connected"); // Has scope
logger.info("General message"); // No scope (original logger unchanged)Removing Scope
Remove the current scope:
const scopedLogger = logger.scope("auth");
scopedLogger.info("Has scope");
scopedLogger.unscope();
scopedLogger.info("No scope");Usage Patterns
Module-Level Scoping
// auth.ts
import { pail } from "@visulima/pail";
const logger = pail.scope("auth");
export const login = (username: string) => {
logger.info("Attempting login", { username });
// ...
};Feature-Level Scoping
// payment.ts
import { pail } from "@visulima/pail";
const paymentLogger = pail.scope("payment", "processing");
export const processPayment = (amount: number) => {
paymentLogger.info("Processing payment", { amount });
// ...
};Request-Level Scoping
// middleware.ts
import { createPail } from "@visulima/pail";
const logger = createPail();
export const requestLogger = (req: Request, res: Response, next: NextFunction) => {
const requestId = req.headers["x-request-id"] || generateId();
const scopedLogger = logger.scope("request", requestId);
req.logger = scopedLogger; // Attach to request
scopedLogger.info("Request received", { method: req.method, path: req.path });
next();
};Component Scoping (React Example)
// UserProfile.tsx
import { pail } from "@visulima/pail";
const logger = pail.scope("UserProfile");
const UserProfile = () => {
useEffect(() => {
logger.info("Component mounted");
return () => {
logger.info("Component unmounted");
};
}, []);
// ...
};Scope Display
Scopes are displayed differently depending on the reporter:
PrettyReporter
[api] [users] info Fetching userJsonReporter
{
"scope": ["api", "users"],
"type": { "name": "info", "level": "informational" },
"message": "Fetching user"
}Best Practices
- Use descriptive names - Make scopes meaningful
- Keep scope hierarchy shallow - Usually 1-3 levels is enough
- Scope at module boundaries - Create scopes at module/feature level
- Consistent naming - Use consistent scope naming across your app
- Don't over-scope - Not every function needs its own scope
Advanced: Dynamic Scoping
You can create scopes dynamically:
const createModuleLogger = (moduleName: string) => {
return pail.scope("module", moduleName);
};
const authLogger = createModuleLogger("auth");
const userLogger = createModuleLogger("user");Related
- Basic Usage - Basic logging operations
- Configuration - Configure logger behavior