LogTape is a zero-dependency logging library for JavaScript and TypeScript that provides a simple yet flexible logging system. It supports multiple JavaScript runtimes (Deno, Node.js, Bun, browsers, and edge functions), features hierarchical categories, structured logging, and offers seamless integration for both applications and libraries.
What's New in 0.12.0
Trace Log Level
LogTape now includes a trace severity level, which sits below debug in the verbosity hierarchy. This addition provides finer-grained control over logging output, particularly useful for detailed execution flow tracking during development and debugging.
All standard RFC 5424 facilities (kern, user, mail, daemon, local0–7, etc.)
Automatic priority calculation based on log levels
Structured data support for log record properties
Cross-runtime compatibility with Deno, Node.js, and Bun
Configurable connection timeouts, custom hostnames, and application names
Logger Method Alias
Added Logger.warning() as an alias for Logger.warn() to ensure consistency with the LogLevel type definition. This change addresses the naming mismatch where the LogLevel union type uses "warning" while the logger method was named warn(), making metaprogramming and dynamic method invocation more straightforward.
Unified Package Releases
Starting with version 0.12.0, all LogTape packages including @logtape/otel, @logtape/sentry, and @logtape/syslog share the same version number and are released together. This ensures compatibility between packages and simplifies version management for users.
Improved Build Infrastructure
LogTape has migrated from dnt to tsdown for npm package bundling. tsdown is a library-focused bundler built on top of Rolldown, a Rust-based bundler that powers the next generation of Vite. Unlike general-purpose bundlers, tsdown is specifically optimized for building TypeScript and JavaScript libraries with minimal configuration. This change brings several benefits:
Elimination of bundler warnings in Webpack, Vite, and other build tools
Improved compatibility with modern JavaScript toolchains
Better tree-shaking support
Cleaner package output
Faster build times through Rust-based performance optimizations
Migration Guide
Updating to Trace Level
If you have code that relies on debug being the lowest severity level, you may need to update your log level configurations:
// Before{ lowestLevel: "debug" } // This was the most verbose setting// After{ lowestLevel: "trace" } // Now includes trace messages
Leveraging Buffer Configuration
To optimize file sink performance in high-throughput scenarios:
getFileSink("app.log", { bufferSize: 16384, // Larger buffer for better performance flushInterval: 10_000 // Flush every 10 seconds})
LogTape is a zero-dependency logging library for JavaScript and TypeScript that provides a simple yet flexible logging system. It supports multiple JavaScript runtimes (Deno, Node.js, Bun, browsers, and edge functions), features hierarchical categories, structured logging, and offers seamless integration for both applications and libraries.
What's New in 0.12.0
Trace Log Level
LogTape now includes a trace severity level, which sits below debug in the verbosity hierarchy. This addition provides finer-grained control over logging output, particularly useful for detailed execution flow tracking during development and debugging.
All standard RFC 5424 facilities (kern, user, mail, daemon, local0–7, etc.)
Automatic priority calculation based on log levels
Structured data support for log record properties
Cross-runtime compatibility with Deno, Node.js, and Bun
Configurable connection timeouts, custom hostnames, and application names
Logger Method Alias
Added Logger.warning() as an alias for Logger.warn() to ensure consistency with the LogLevel type definition. This change addresses the naming mismatch where the LogLevel union type uses "warning" while the logger method was named warn(), making metaprogramming and dynamic method invocation more straightforward.
Unified Package Releases
Starting with version 0.12.0, all LogTape packages including @logtape/otel, @logtape/sentry, and @logtape/syslog share the same version number and are released together. This ensures compatibility between packages and simplifies version management for users.
Improved Build Infrastructure
LogTape has migrated from dnt to tsdown for npm package bundling. tsdown is a library-focused bundler built on top of Rolldown, a Rust-based bundler that powers the next generation of Vite. Unlike general-purpose bundlers, tsdown is specifically optimized for building TypeScript and JavaScript libraries with minimal configuration. This change brings several benefits:
Elimination of bundler warnings in Webpack, Vite, and other build tools
Improved compatibility with modern JavaScript toolchains
Better tree-shaking support
Cleaner package output
Faster build times through Rust-based performance optimizations
Migration Guide
Updating to Trace Level
If you have code that relies on debug being the lowest severity level, you may need to update your log level configurations:
// Before{ lowestLevel: "debug" } // This was the most verbose setting// After{ lowestLevel: "trace" } // Now includes trace messages
Leveraging Buffer Configuration
To optimize file sink performance in high-throughput scenarios:
getFileSink("app.log", { bufferSize: 16384, // Larger buffer for better performance flushInterval: 10_000 // Flush every 10 seconds})
LogTape is a zero-dependency logging library for JavaScript and TypeScript that provides a simple yet flexible logging system. It supports multiple JavaScript runtimes (Deno, Node.js, Bun, browsers, and edge functions), features hierarchical categories, structured logging, and offers seamless integration for both applications and libraries.
What's New in 0.12.0
Trace Log Level
LogTape now includes a trace severity level, which sits below debug in the verbosity hierarchy. This addition provides finer-grained control over logging output, particularly useful for detailed execution flow tracking during development and debugging.
All standard RFC 5424 facilities (kern, user, mail, daemon, local0–7, etc.)
Automatic priority calculation based on log levels
Structured data support for log record properties
Cross-runtime compatibility with Deno, Node.js, and Bun
Configurable connection timeouts, custom hostnames, and application names
Logger Method Alias
Added Logger.warning() as an alias for Logger.warn() to ensure consistency with the LogLevel type definition. This change addresses the naming mismatch where the LogLevel union type uses "warning" while the logger method was named warn(), making metaprogramming and dynamic method invocation more straightforward.
Unified Package Releases
Starting with version 0.12.0, all LogTape packages including @logtape/otel, @logtape/sentry, and @logtape/syslog share the same version number and are released together. This ensures compatibility between packages and simplifies version management for users.
Improved Build Infrastructure
LogTape has migrated from dnt to tsdown for npm package bundling. tsdown is a library-focused bundler built on top of Rolldown, a Rust-based bundler that powers the next generation of Vite. Unlike general-purpose bundlers, tsdown is specifically optimized for building TypeScript and JavaScript libraries with minimal configuration. This change brings several benefits:
Elimination of bundler warnings in Webpack, Vite, and other build tools
Improved compatibility with modern JavaScript toolchains
Better tree-shaking support
Cleaner package output
Faster build times through Rust-based performance optimizations
Migration Guide
Updating to Trace Level
If you have code that relies on debug being the lowest severity level, you may need to update your log level configurations:
// Before{ lowestLevel: "debug" } // This was the most verbose setting// After{ lowestLevel: "trace" } // Now includes trace messages
Leveraging Buffer Configuration
To optimize file sink performance in high-throughput scenarios:
getFileSink("app.log", { bufferSize: 16384, // Larger buffer for better performance flushInterval: 10_000 // Flush every 10 seconds})
LogTape is a zero-dependency logging library for JavaScript and TypeScript that provides a simple yet flexible logging system. It supports multiple JavaScript runtimes (Deno, Node.js, Bun, browsers, and edge functions), features hierarchical categories, structured logging, and offers seamless integration for both applications and libraries.
What's New in 0.12.0
Trace Log Level
LogTape now includes a trace severity level, which sits below debug in the verbosity hierarchy. This addition provides finer-grained control over logging output, particularly useful for detailed execution flow tracking during development and debugging.
All standard RFC 5424 facilities (kern, user, mail, daemon, local0–7, etc.)
Automatic priority calculation based on log levels
Structured data support for log record properties
Cross-runtime compatibility with Deno, Node.js, and Bun
Configurable connection timeouts, custom hostnames, and application names
Logger Method Alias
Added Logger.warning() as an alias for Logger.warn() to ensure consistency with the LogLevel type definition. This change addresses the naming mismatch where the LogLevel union type uses "warning" while the logger method was named warn(), making metaprogramming and dynamic method invocation more straightforward.
Unified Package Releases
Starting with version 0.12.0, all LogTape packages including @logtape/otel, @logtape/sentry, and @logtape/syslog share the same version number and are released together. This ensures compatibility between packages and simplifies version management for users.
Improved Build Infrastructure
LogTape has migrated from dnt to tsdown for npm package bundling. tsdown is a library-focused bundler built on top of Rolldown, a Rust-based bundler that powers the next generation of Vite. Unlike general-purpose bundlers, tsdown is specifically optimized for building TypeScript and JavaScript libraries with minimal configuration. This change brings several benefits:
Elimination of bundler warnings in Webpack, Vite, and other build tools
Improved compatibility with modern JavaScript toolchains
Better tree-shaking support
Cleaner package output
Faster build times through Rust-based performance optimizations
Migration Guide
Updating to Trace Level
If you have code that relies on debug being the lowest severity level, you may need to update your log level configurations:
// Before{ lowestLevel: "debug" } // This was the most verbose setting// After{ lowestLevel: "trace" } // Now includes trace messages
Leveraging Buffer Configuration
To optimize file sink performance in high-throughput scenarios:
getFileSink("app.log", { bufferSize: 16384, // Larger buffer for better performance flushInterval: 10_000 // Flush every 10 seconds})
LogTape is a zero-dependency logging library for JavaScript and TypeScript that provides a simple yet flexible logging system. It supports multiple JavaScript runtimes (Deno, Node.js, Bun, browsers, and edge functions), features hierarchical categories, structured logging, and offers seamless integration for both applications and libraries.
What's New in 0.12.0
Trace Log Level
LogTape now includes a trace severity level, which sits below debug in the verbosity hierarchy. This addition provides finer-grained control over logging output, particularly useful for detailed execution flow tracking during development and debugging.
All standard RFC 5424 facilities (kern, user, mail, daemon, local0–7, etc.)
Automatic priority calculation based on log levels
Structured data support for log record properties
Cross-runtime compatibility with Deno, Node.js, and Bun
Configurable connection timeouts, custom hostnames, and application names
Logger Method Alias
Added Logger.warning() as an alias for Logger.warn() to ensure consistency with the LogLevel type definition. This change addresses the naming mismatch where the LogLevel union type uses "warning" while the logger method was named warn(), making metaprogramming and dynamic method invocation more straightforward.
Unified Package Releases
Starting with version 0.12.0, all LogTape packages including @logtape/otel, @logtape/sentry, and @logtape/syslog share the same version number and are released together. This ensures compatibility between packages and simplifies version management for users.
Improved Build Infrastructure
LogTape has migrated from dnt to tsdown for npm package bundling. tsdown is a library-focused bundler built on top of Rolldown, a Rust-based bundler that powers the next generation of Vite. Unlike general-purpose bundlers, tsdown is specifically optimized for building TypeScript and JavaScript libraries with minimal configuration. This change brings several benefits:
Elimination of bundler warnings in Webpack, Vite, and other build tools
Improved compatibility with modern JavaScript toolchains
Better tree-shaking support
Cleaner package output
Faster build times through Rust-based performance optimizations
Migration Guide
Updating to Trace Level
If you have code that relies on debug being the lowest severity level, you may need to update your log level configurations:
// Before{ lowestLevel: "debug" } // This was the most verbose setting// After{ lowestLevel: "trace" } // Now includes trace messages
Leveraging Buffer Configuration
To optimize file sink performance in high-throughput scenarios:
getFileSink("app.log", { bufferSize: 16384, // Larger buffer for better performance flushInterval: 10_000 // Flush every 10 seconds})
LogTape is a zero-dependency logging library for JavaScript and TypeScript that provides a simple yet flexible logging system. It supports multiple JavaScript runtimes (Deno, Node.js, Bun, browsers, and edge functions), features hierarchical categories, structured logging, and offers seamless integration for both applications and libraries.
What's New in 0.12.0
Trace Log Level
LogTape now includes a trace severity level, which sits below debug in the verbosity hierarchy. This addition provides finer-grained control over logging output, particularly useful for detailed execution flow tracking during development and debugging.
All standard RFC 5424 facilities (kern, user, mail, daemon, local0–7, etc.)
Automatic priority calculation based on log levels
Structured data support for log record properties
Cross-runtime compatibility with Deno, Node.js, and Bun
Configurable connection timeouts, custom hostnames, and application names
Logger Method Alias
Added Logger.warning() as an alias for Logger.warn() to ensure consistency with the LogLevel type definition. This change addresses the naming mismatch where the LogLevel union type uses "warning" while the logger method was named warn(), making metaprogramming and dynamic method invocation more straightforward.
Unified Package Releases
Starting with version 0.12.0, all LogTape packages including @logtape/otel, @logtape/sentry, and @logtape/syslog share the same version number and are released together. This ensures compatibility between packages and simplifies version management for users.
Improved Build Infrastructure
LogTape has migrated from dnt to tsdown for npm package bundling. tsdown is a library-focused bundler built on top of Rolldown, a Rust-based bundler that powers the next generation of Vite. Unlike general-purpose bundlers, tsdown is specifically optimized for building TypeScript and JavaScript libraries with minimal configuration. This change brings several benefits:
Elimination of bundler warnings in Webpack, Vite, and other build tools
Improved compatibility with modern JavaScript toolchains
Better tree-shaking support
Cleaner package output
Faster build times through Rust-based performance optimizations
Migration Guide
Updating to Trace Level
If you have code that relies on debug being the lowest severity level, you may need to update your log level configurations:
// Before{ lowestLevel: "debug" } // This was the most verbose setting// After{ lowestLevel: "trace" } // Now includes trace messages
Leveraging Buffer Configuration
To optimize file sink performance in high-throughput scenarios:
getFileSink("app.log", { bufferSize: 16384, // Larger buffer for better performance flushInterval: 10_000 // Flush every 10 seconds})
LogTape is a zero-dependency logging library for JavaScript and TypeScript that works across all runtimes.
We're excited to announce the release of LogTape 0.11.0, which introduces significant enhancements to structured logging capabilities and adds a new JSON Lines formatter for better log processing.
New features and enhancements
Enhanced structured logging
LogTape 0.11.0 brings major improvements to structured logging, making it easier and more flexible to work with structured data in your logs.
Direct object logging
You can now log structured data directly by passing an object as the first argument to any log method:
logger.info({ userId: 123456, username: "johndoe", loginTime: new Date(),});
This creates a log entry with the object properties as structured fields, making your logs more machine-readable and searchable.
Universal property interpolation with {*}
A new special placeholder {*} allows you to interpolate all properties from your structured data at once:
logger.info("User logged in with properties {*}", { userId: 123456, username: "johndoe", loginTime: new Date(),});
This is particularly useful when you want to include all available context without explicitly naming each property in your message template.
Streamlined logging methods
All logging methods (debug, info, warn, error, fatal) now support the new object-first syntax as a convenient shorthand for structured logging with the {*} placeholder.
JSON Lines formatter
LogTape now includes built-in support for JSON Lines (also known as JSONL or NDJSON) format, a popular choice for structured logging in modern applications:
import { jsonLinesFormatter } from "@logtape/logtape";import { getFileSink } from "@logtape/file";await configure({ sinks: { jsonl: getFileSink("app.jsonl", { formatter: jsonLinesFormatter }), }, // ... rest of configuration});
The JSON Lines formatter outputs each log record as a JSON object on a separate line, making it ideal for log aggregation systems and analysis tools.
LogTape is a zero-dependency logging library for JavaScript and TypeScript that works across all runtimes.
We're excited to announce the release of LogTape 0.11.0, which introduces significant enhancements to structured logging capabilities and adds a new JSON Lines formatter for better log processing.
New features and enhancements
Enhanced structured logging
LogTape 0.11.0 brings major improvements to structured logging, making it easier and more flexible to work with structured data in your logs.
Direct object logging
You can now log structured data directly by passing an object as the first argument to any log method:
logger.info({ userId: 123456, username: "johndoe", loginTime: new Date(),});
This creates a log entry with the object properties as structured fields, making your logs more machine-readable and searchable.
Universal property interpolation with {*}
A new special placeholder {*} allows you to interpolate all properties from your structured data at once:
logger.info("User logged in with properties {*}", { userId: 123456, username: "johndoe", loginTime: new Date(),});
This is particularly useful when you want to include all available context without explicitly naming each property in your message template.
Streamlined logging methods
All logging methods (debug, info, warn, error, fatal) now support the new object-first syntax as a convenient shorthand for structured logging with the {*} placeholder.
JSON Lines formatter
LogTape now includes built-in support for JSON Lines (also known as JSONL or NDJSON) format, a popular choice for structured logging in modern applications:
import { jsonLinesFormatter } from "@logtape/logtape";import { getFileSink } from "@logtape/file";await configure({ sinks: { jsonl: getFileSink("app.jsonl", { formatter: jsonLinesFormatter }), }, // ... rest of configuration});
The JSON Lines formatter outputs each log record as a JSON object on a separate line, making it ideal for log aggregation systems and analysis tools.
In the diverse and ever-evolving JavaScript ecosystem, logging remains a critical component for development, debugging, and monitoring applications. While numerous logging libraries exist, LogTape stands out with its unique combination of simplicity, flexibility, and cross-runtime compatibility. Let's explore why LogTape deserves consideration for your next JavaScript or TypeScript project—whether you're building an application or a library.
Zero Dependencies: A Lightweight Footprint
One of LogTape's most compelling features is its complete absence of dependencies. In an era where “dependency hell” plagues many JavaScript projects, LogTape offers a refreshing alternative:
// No additional packages to install beyond LogTape itselfimport { configure, getConsoleSink, getLogger } from "@logtape/logtape";
This zero-dependency approach provides several advantages:
Reduced bundle size
No transitive dependencies means smaller packages
Enhanced stability
No risk of breaking changes from upstream dependencies
Simplified security
Fewer potential vulnerabilities from third-party code
Lower integration overhead
Particularly valuable for library authors who don't want to burden users with additional dependencies
Runtime Diversity: Write Once, Log Everywhere
While many popular logging libraries focus primarily on Node.js, LogTape provides seamless support across diverse JavaScript runtimes:
This runtime flexibility means you can use consistent logging patterns regardless of your deployment environment:
// Same API works seamlessly across all JavaScript runtimesimport { getLogger } from "@logtape/logtape";const logger = getLogger(["my-service", "user-management"]);// Works in Node.js, Deno, Bun, browsers, or edge functionslogger.info`User ${userId} logged in successfully`;
For teams working across multiple platforms or projects transitioning between runtimes, this consistency is invaluable. No need to learn different logging libraries or approaches—LogTape works the same way everywhere.
Hierarchical Categories: Fine-Grained Control
LogTape's hierarchical category system represents a standout feature that's surprisingly rare among JavaScript logging libraries. Categories allow you to organize logs in a tree-like structure:
This hierarchical approach offers powerful benefits:
Targeted filtering
Configure different log levels for different parts of your application
Inheritance
Child loggers inherit settings from parents, reducing configuration overhead
Organizational clarity
Logs naturally follow your application's module structure
Here's how you might configure logging levels for different categories:
await configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log"), }, loggers: [ // Base configuration for all app logs { category: ["my-app"], lowestLevel: "info", sinks: ["console", "file"] }, // More verbose logging just for database components { category: ["my-app", "database"], lowestLevel: "debug", sinks: ["file"] } ]});
With this configuration, all application logs at "info" level and above go to both console and file, while database-specific logs include more detailed "debug" level information, but only in the log file.
Structured Logging: Beyond Simple Text
Modern logging goes beyond simple text strings. LogTape embraces structured logging, which treats log entries as data objects rather than plain text:
The function is only evaluated if the debug level is enabled, preventing unnecessary computation for suppressed log levels.
Extremely Simple Sinks and Filters: Minimal Boilerplate
LogTape's approach to extensibility is remarkably straightforward. Creating custom sinks (output destinations) and filters requires minimal boilerplate code.
Dead Simple Sinks
A sink in LogTape is just a function that receives a log record:
// Creating a custom sink is as simple as defining a functionconst mySink = (record) => { const timestamp = new Date(record.timestamp).toISOString(); const level = record.level.toUpperCase(); const category = record.category.join('.'); // Send to your custom destination myCustomLogService.send({ time: timestamp, priority: level, component: category, message: record.message, ...record.properties });};// Use your custom sink in configurationawait configure({ sinks: { console: getConsoleSink(), custom: mySink }, loggers: [ { category: ["my-app"], sinks: ["console", "custom"] } ]});
Compare this with other libraries that require extending classes, implementing multiple methods, or following specific patterns. LogTape's approach is refreshingly straightforward.
Simple Filters
Similarly, filters in LogTape are just functions that return a Boolean:
// Filter that only passes high-priority or specific component logsconst importantLogsFilter = (record) => { // Always include errors if (record.level === "error" || record.level === "fatal") { return true; } // Always include payment-related logs if (record.category.includes("payments")) { return true; } // Filter out other logs return false;};await configure({ // ...sinks configuration filters: { important: importantLogsFilter }, loggers: [ { category: ["my-app"], sinks: ["alertSystem"], filters: ["important"] } ]});
LogTape also provides a convenient shorthand for level-based filtering:
await configure({ // ...sinks configuration filters: { // This creates a filter for "warning" level and above warningAndAbove: "warning" }, loggers: [ { category: ["my-app"], sinks: ["console"], filters: ["warningAndAbove"] } ]});
Perfect for Library Authors
LogTape is uniquely well-suited for library authors who want to incorporate logging without burdening their users. The core philosophy is simple:
The key point is that the library never calls configure(). Instead, it provides useful log output points with appropriate levels and contextual data.
Applications using the library can then decide exactly how to handle these logs:
// Application codeimport { configure, getConsoleSink } from "@logtape/logtape";import { Database } from "my-awesome-lib";// Configure how logs should be handledawait configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log") }, loggers: [ // Handle all library logs { category: ["my-awesome-lib"], lowestLevel: "info", sinks: ["file"] }, // More verbose for database component during development { category: ["my-awesome-lib", "database"], lowestLevel: "debug", sinks: ["console", "file"] } ]});// Use the libraryconst db = new Database("localhost", 5432, "user");db.connect();
This separation of concerns offers several benefits:
Library users have complete control over log handling
Libraries can provide rich logging without imposing implementation details
No risk of conflict with application logging configurations
Libraries can be "noisy" internally while allowing applications to filter as needed
Contexts for Richer Logging
LogTape provides context mechanisms for adding consistent properties across multiple log messages. This is particularly valuable for tracing requests through a system:
Explicit Contexts
const logger = getLogger(["my-app", "api"]);// Create a logger with contextconst requestLogger = logger.with({ requestId: "abc-123", userId: 42, endpoint: "/users"});// All logs from this logger include the context propertiesrequestLogger.info("Processing request");requestLogger.debug("Validating input");requestLogger.info("Request completed", { durationMs: 120 });
Implicit Contexts (v0.7.0+)
For cases where you want context to apply across function calls without explicit passing:
import { getLogger, withContext } from "@logtape/logtape";function handleRequest(req, res) { withContext({ requestId: req.id, userId: req.user?.id }, () => { // All logs within this function and any functions it calls // will automatically include the context properties processRequest(req, res); });}function processRequest(req, res) { // No need to pass context - it's automatically available getLogger(["my-app", "processor"]).info("Processing data"); // Call other functions that will also inherit the context validateInput(req.body);}function validateInput(data) { // This log also gets the requestId and userId getLogger(["my-app", "validator"]).debug("Validating input", { data });}
This implicit context capability is invaluable for tracing requests through multiple layers of code without manually threading context through every function call.
When LogTape Might Not Be Your Best Choice
While LogTape offers compelling advantages for many use cases, it's not universally the best choice:
Extreme performance requirements
If your application logs tens of thousands of entries per second and raw performance is the top priority, specialized high-performance libraries like Pino may be more suitable with their focus on optimized logging throughput.
Extensive pre-built integrations
If you need immediate integration with numerous specific systems (Elasticsearch, Graylog, etc.) without writing any custom code, Winston's rich ecosystem of transports might provide a faster starting point.
Legacy systems with specific logging requirements
If you're maintaining systems built around specific logging patterns from Java or other environments, purpose-built libraries like Log4js might offer more familiar APIs.
Web browser-only applications with minimal logging needs
For extremely simple web browser-only logging needs where you just want basic console output with levels, even simpler libraries like loglevel might be sufficient.
Conclusion
LogTape stands out in the crowded JavaScript logging landscape by offering a unique combination of features that address real-world development challenges:
Zero dependencies for a lightweight, secure foundation
Runtime diversity supporting Node.js, Deno, Bun, browsers, and edge functions
Hierarchical categories for better log organization and filtering
Structured logging for improved analysis and searchability
Simple extension mechanisms with minimal boilerplate
Library-friendly design that respects separation of concerns
Whether you're building applications or libraries, working across multiple JavaScript runtimes, or simply seeking a clean, well-designed logging solution, LogTape deserves serious consideration. Its thoughtful design balances simplicity with powerful features, avoiding common pitfalls of JavaScript logging libraries.
We're pleased to announce the release of LogTape 0.10.0 today. This version introduces several significant enhancements to improve security, flexibility, and usability.
What is LogTape?
LogTape is a simple, zero-dependency logging library for JavaScript with support for multiple runtimes (Node.js, Deno, Bun, browsers, and edge functions). It features structured logging, hierarchical categories, template literals, and is designed to be used in both applications and libraries.
Key Highlights
New Data Redaction Package
The most notable addition in this release is the new @logtape/redaction package, designed to help protect sensitive information in your logs. This package provides two complementary approaches to redaction:
Pattern-based redaction: Uses regular expressions to identify and redact sensitive patterns like credit card numbers, email addresses, and tokens in formatted log output.
Field-based redaction: Identifies and redacts sensitive fields by their names in structured log data.
The package includes several built-in patterns for common sensitive data types:
Credit card numbers
Email addresses
JSON Web Tokens (JWTs)
U.S. Social Security numbers
South Korean resident registration numbers
Both approaches can be used independently or combined for maximum security. Comprehensive documentation for these features is available in the Data redaction section of the manual.
Timestamp Formatting Improvements
Text formatters now support omitting timestamps entirely from formatted messages. The TextFormatterOptions.timestamp option has been extended to include "none" and "disabled" values, giving you more control over log output format.
Lazy File Sink Option
A new FileSinkOptions.lazy option has been added, allowing file sinks to open files only when actually needed, which can improve resource utilization.
Config Error Detection
The configure() and configureSync() functions now check for duplicate logger configurations with the same category and throw a ConfigError when detected. This prevents unintended overriding of logger configurations.
Acknowledgments
We'd like to thank our external contributors who helped make this release possible:
Ooker for implementing the ability to omit timestamps from formatted messages (#35)
We're pleased to announce the release of LogTape 0.10.0 today. This version introduces several significant enhancements to improve security, flexibility, and usability.
What is LogTape?
LogTape is a simple, zero-dependency logging library for JavaScript with support for multiple runtimes (Node.js, Deno, Bun, browsers, and edge functions). It features structured logging, hierarchical categories, template literals, and is designed to be used in both applications and libraries.
Key Highlights
New Data Redaction Package
The most notable addition in this release is the new @logtape/redaction package, designed to help protect sensitive information in your logs. This package provides two complementary approaches to redaction:
Pattern-based redaction: Uses regular expressions to identify and redact sensitive patterns like credit card numbers, email addresses, and tokens in formatted log output.
Field-based redaction: Identifies and redacts sensitive fields by their names in structured log data.
The package includes several built-in patterns for common sensitive data types:
Credit card numbers
Email addresses
JSON Web Tokens (JWTs)
U.S. Social Security numbers
South Korean resident registration numbers
Both approaches can be used independently or combined for maximum security. Comprehensive documentation for these features is available in the Data redaction section of the manual.
Timestamp Formatting Improvements
Text formatters now support omitting timestamps entirely from formatted messages. The TextFormatterOptions.timestamp option has been extended to include "none" and "disabled" values, giving you more control over log output format.
Lazy File Sink Option
A new FileSinkOptions.lazy option has been added, allowing file sinks to open files only when actually needed, which can improve resource utilization.
Config Error Detection
The configure() and configureSync() functions now check for duplicate logger configurations with the same category and throw a ConfigError when detected. This prevents unintended overriding of logger configurations.
Acknowledgments
We'd like to thank our external contributors who helped make this release possible:
Ooker for implementing the ability to omit timestamps from formatted messages (#35)
We're pleased to announce the release of LogTape 0.10.0 today. This version introduces several significant enhancements to improve security, flexibility, and usability.
What is LogTape?
LogTape is a simple, zero-dependency logging library for JavaScript with support for multiple runtimes (Node.js, Deno, Bun, browsers, and edge functions). It features structured logging, hierarchical categories, template literals, and is designed to be used in both applications and libraries.
Key Highlights
New Data Redaction Package
The most notable addition in this release is the new @logtape/redaction package, designed to help protect sensitive information in your logs. This package provides two complementary approaches to redaction:
Pattern-based redaction: Uses regular expressions to identify and redact sensitive patterns like credit card numbers, email addresses, and tokens in formatted log output.
Field-based redaction: Identifies and redacts sensitive fields by their names in structured log data.
The package includes several built-in patterns for common sensitive data types:
Credit card numbers
Email addresses
JSON Web Tokens (JWTs)
U.S. Social Security numbers
South Korean resident registration numbers
Both approaches can be used independently or combined for maximum security. Comprehensive documentation for these features is available in the Data redaction section of the manual.
Timestamp Formatting Improvements
Text formatters now support omitting timestamps entirely from formatted messages. The TextFormatterOptions.timestamp option has been extended to include "none" and "disabled" values, giving you more control over log output format.
Lazy File Sink Option
A new FileSinkOptions.lazy option has been added, allowing file sinks to open files only when actually needed, which can improve resource utilization.
Config Error Detection
The configure() and configureSync() functions now check for duplicate logger configurations with the same category and throw a ConfigError when detected. This prevents unintended overriding of logger configurations.
Acknowledgments
We'd like to thank our external contributors who helped make this release possible:
Ooker for implementing the ability to omit timestamps from formatted messages (#35)
We're pleased to announce the release of LogTape 0.10.0 today. This version introduces several significant enhancements to improve security, flexibility, and usability.
What is LogTape?
LogTape is a simple, zero-dependency logging library for JavaScript with support for multiple runtimes (Node.js, Deno, Bun, browsers, and edge functions). It features structured logging, hierarchical categories, template literals, and is designed to be used in both applications and libraries.
Key Highlights
New Data Redaction Package
The most notable addition in this release is the new @logtape/redaction package, designed to help protect sensitive information in your logs. This package provides two complementary approaches to redaction:
Pattern-based redaction: Uses regular expressions to identify and redact sensitive patterns like credit card numbers, email addresses, and tokens in formatted log output.
Field-based redaction: Identifies and redacts sensitive fields by their names in structured log data.
The package includes several built-in patterns for common sensitive data types:
Credit card numbers
Email addresses
JSON Web Tokens (JWTs)
U.S. Social Security numbers
South Korean resident registration numbers
Both approaches can be used independently or combined for maximum security. Comprehensive documentation for these features is available in the Data redaction section of the manual.
Timestamp Formatting Improvements
Text formatters now support omitting timestamps entirely from formatted messages. The TextFormatterOptions.timestamp option has been extended to include "none" and "disabled" values, giving you more control over log output format.
Lazy File Sink Option
A new FileSinkOptions.lazy option has been added, allowing file sinks to open files only when actually needed, which can improve resource utilization.
Config Error Detection
The configure() and configureSync() functions now check for duplicate logger configurations with the same category and throw a ConfigError when detected. This prevents unintended overriding of logger configurations.
Acknowledgments
We'd like to thank our external contributors who helped make this release possible:
Ooker for implementing the ability to omit timestamps from formatted messages (#35)
We're pleased to announce the release of LogTape 0.10.0 today. This version introduces several significant enhancements to improve security, flexibility, and usability.
What is LogTape?
LogTape is a simple, zero-dependency logging library for JavaScript with support for multiple runtimes (Node.js, Deno, Bun, browsers, and edge functions). It features structured logging, hierarchical categories, template literals, and is designed to be used in both applications and libraries.
Key Highlights
New Data Redaction Package
The most notable addition in this release is the new @logtape/redaction package, designed to help protect sensitive information in your logs. This package provides two complementary approaches to redaction:
Pattern-based redaction: Uses regular expressions to identify and redact sensitive patterns like credit card numbers, email addresses, and tokens in formatted log output.
Field-based redaction: Identifies and redacts sensitive fields by their names in structured log data.
The package includes several built-in patterns for common sensitive data types:
Credit card numbers
Email addresses
JSON Web Tokens (JWTs)
U.S. Social Security numbers
South Korean resident registration numbers
Both approaches can be used independently or combined for maximum security. Comprehensive documentation for these features is available in the Data redaction section of the manual.
Timestamp Formatting Improvements
Text formatters now support omitting timestamps entirely from formatted messages. The TextFormatterOptions.timestamp option has been extended to include "none" and "disabled" values, giving you more control over log output format.
Lazy File Sink Option
A new FileSinkOptions.lazy option has been added, allowing file sinks to open files only when actually needed, which can improve resource utilization.
Config Error Detection
The configure() and configureSync() functions now check for duplicate logger configurations with the same category and throw a ConfigError when detected. This prevents unintended overriding of logger configurations.
Acknowledgments
We'd like to thank our external contributors who helped make this release possible:
Ooker for implementing the ability to omit timestamps from formatted messages (#35)
In the diverse and ever-evolving JavaScript ecosystem, logging remains a critical component for development, debugging, and monitoring applications. While numerous logging libraries exist, LogTape stands out with its unique combination of simplicity, flexibility, and cross-runtime compatibility. Let's explore why LogTape deserves consideration for your next JavaScript or TypeScript project—whether you're building an application or a library.
Zero Dependencies: A Lightweight Footprint
One of LogTape's most compelling features is its complete absence of dependencies. In an era where “dependency hell” plagues many JavaScript projects, LogTape offers a refreshing alternative:
// No additional packages to install beyond LogTape itselfimport { configure, getConsoleSink, getLogger } from "@logtape/logtape";
This zero-dependency approach provides several advantages:
Reduced bundle size
No transitive dependencies means smaller packages
Enhanced stability
No risk of breaking changes from upstream dependencies
Simplified security
Fewer potential vulnerabilities from third-party code
Lower integration overhead
Particularly valuable for library authors who don't want to burden users with additional dependencies
Runtime Diversity: Write Once, Log Everywhere
While many popular logging libraries focus primarily on Node.js, LogTape provides seamless support across diverse JavaScript runtimes:
This runtime flexibility means you can use consistent logging patterns regardless of your deployment environment:
// Same API works seamlessly across all JavaScript runtimesimport { getLogger } from "@logtape/logtape";const logger = getLogger(["my-service", "user-management"]);// Works in Node.js, Deno, Bun, browsers, or edge functionslogger.info`User ${userId} logged in successfully`;
For teams working across multiple platforms or projects transitioning between runtimes, this consistency is invaluable. No need to learn different logging libraries or approaches—LogTape works the same way everywhere.
Hierarchical Categories: Fine-Grained Control
LogTape's hierarchical category system represents a standout feature that's surprisingly rare among JavaScript logging libraries. Categories allow you to organize logs in a tree-like structure:
This hierarchical approach offers powerful benefits:
Targeted filtering
Configure different log levels for different parts of your application
Inheritance
Child loggers inherit settings from parents, reducing configuration overhead
Organizational clarity
Logs naturally follow your application's module structure
Here's how you might configure logging levels for different categories:
await configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log"), }, loggers: [ // Base configuration for all app logs { category: ["my-app"], lowestLevel: "info", sinks: ["console", "file"] }, // More verbose logging just for database components { category: ["my-app", "database"], lowestLevel: "debug", sinks: ["file"] } ]});
With this configuration, all application logs at "info" level and above go to both console and file, while database-specific logs include more detailed "debug" level information, but only in the log file.
Structured Logging: Beyond Simple Text
Modern logging goes beyond simple text strings. LogTape embraces structured logging, which treats log entries as data objects rather than plain text:
The function is only evaluated if the debug level is enabled, preventing unnecessary computation for suppressed log levels.
Extremely Simple Sinks and Filters: Minimal Boilerplate
LogTape's approach to extensibility is remarkably straightforward. Creating custom sinks (output destinations) and filters requires minimal boilerplate code.
Dead Simple Sinks
A sink in LogTape is just a function that receives a log record:
// Creating a custom sink is as simple as defining a functionconst mySink = (record) => { const timestamp = new Date(record.timestamp).toISOString(); const level = record.level.toUpperCase(); const category = record.category.join('.'); // Send to your custom destination myCustomLogService.send({ time: timestamp, priority: level, component: category, message: record.message, ...record.properties });};// Use your custom sink in configurationawait configure({ sinks: { console: getConsoleSink(), custom: mySink }, loggers: [ { category: ["my-app"], sinks: ["console", "custom"] } ]});
Compare this with other libraries that require extending classes, implementing multiple methods, or following specific patterns. LogTape's approach is refreshingly straightforward.
Simple Filters
Similarly, filters in LogTape are just functions that return a Boolean:
// Filter that only passes high-priority or specific component logsconst importantLogsFilter = (record) => { // Always include errors if (record.level === "error" || record.level === "fatal") { return true; } // Always include payment-related logs if (record.category.includes("payments")) { return true; } // Filter out other logs return false;};await configure({ // ...sinks configuration filters: { important: importantLogsFilter }, loggers: [ { category: ["my-app"], sinks: ["alertSystem"], filters: ["important"] } ]});
LogTape also provides a convenient shorthand for level-based filtering:
await configure({ // ...sinks configuration filters: { // This creates a filter for "warning" level and above warningAndAbove: "warning" }, loggers: [ { category: ["my-app"], sinks: ["console"], filters: ["warningAndAbove"] } ]});
Perfect for Library Authors
LogTape is uniquely well-suited for library authors who want to incorporate logging without burdening their users. The core philosophy is simple:
The key point is that the library never calls configure(). Instead, it provides useful log output points with appropriate levels and contextual data.
Applications using the library can then decide exactly how to handle these logs:
// Application codeimport { configure, getConsoleSink } from "@logtape/logtape";import { Database } from "my-awesome-lib";// Configure how logs should be handledawait configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log") }, loggers: [ // Handle all library logs { category: ["my-awesome-lib"], lowestLevel: "info", sinks: ["file"] }, // More verbose for database component during development { category: ["my-awesome-lib", "database"], lowestLevel: "debug", sinks: ["console", "file"] } ]});// Use the libraryconst db = new Database("localhost", 5432, "user");db.connect();
This separation of concerns offers several benefits:
Library users have complete control over log handling
Libraries can provide rich logging without imposing implementation details
No risk of conflict with application logging configurations
Libraries can be "noisy" internally while allowing applications to filter as needed
Contexts for Richer Logging
LogTape provides context mechanisms for adding consistent properties across multiple log messages. This is particularly valuable for tracing requests through a system:
Explicit Contexts
const logger = getLogger(["my-app", "api"]);// Create a logger with contextconst requestLogger = logger.with({ requestId: "abc-123", userId: 42, endpoint: "/users"});// All logs from this logger include the context propertiesrequestLogger.info("Processing request");requestLogger.debug("Validating input");requestLogger.info("Request completed", { durationMs: 120 });
Implicit Contexts (v0.7.0+)
For cases where you want context to apply across function calls without explicit passing:
import { getLogger, withContext } from "@logtape/logtape";function handleRequest(req, res) { withContext({ requestId: req.id, userId: req.user?.id }, () => { // All logs within this function and any functions it calls // will automatically include the context properties processRequest(req, res); });}function processRequest(req, res) { // No need to pass context - it's automatically available getLogger(["my-app", "processor"]).info("Processing data"); // Call other functions that will also inherit the context validateInput(req.body);}function validateInput(data) { // This log also gets the requestId and userId getLogger(["my-app", "validator"]).debug("Validating input", { data });}
This implicit context capability is invaluable for tracing requests through multiple layers of code without manually threading context through every function call.
When LogTape Might Not Be Your Best Choice
While LogTape offers compelling advantages for many use cases, it's not universally the best choice:
Extreme performance requirements
If your application logs tens of thousands of entries per second and raw performance is the top priority, specialized high-performance libraries like Pino may be more suitable with their focus on optimized logging throughput.
Extensive pre-built integrations
If you need immediate integration with numerous specific systems (Elasticsearch, Graylog, etc.) without writing any custom code, Winston's rich ecosystem of transports might provide a faster starting point.
Legacy systems with specific logging requirements
If you're maintaining systems built around specific logging patterns from Java or other environments, purpose-built libraries like Log4js might offer more familiar APIs.
Web browser-only applications with minimal logging needs
For extremely simple web browser-only logging needs where you just want basic console output with levels, even simpler libraries like loglevel might be sufficient.
Conclusion
LogTape stands out in the crowded JavaScript logging landscape by offering a unique combination of features that address real-world development challenges:
Zero dependencies for a lightweight, secure foundation
Runtime diversity supporting Node.js, Deno, Bun, browsers, and edge functions
Hierarchical categories for better log organization and filtering
Structured logging for improved analysis and searchability
Simple extension mechanisms with minimal boilerplate
Library-friendly design that respects separation of concerns
Whether you're building applications or libraries, working across multiple JavaScript runtimes, or simply seeking a clean, well-designed logging solution, LogTape deserves serious consideration. Its thoughtful design balances simplicity with powerful features, avoiding common pitfalls of JavaScript logging libraries.
In the diverse and ever-evolving JavaScript ecosystem, logging remains a critical component for development, debugging, and monitoring applications. While numerous logging libraries exist, LogTape stands out with its unique combination of simplicity, flexibility, and cross-runtime compatibility. Let's explore why LogTape deserves consideration for your next JavaScript or TypeScript project—whether you're building an application or a library.
Zero Dependencies: A Lightweight Footprint
One of LogTape's most compelling features is its complete absence of dependencies. In an era where “dependency hell” plagues many JavaScript projects, LogTape offers a refreshing alternative:
// No additional packages to install beyond LogTape itselfimport { configure, getConsoleSink, getLogger } from "@logtape/logtape";
This zero-dependency approach provides several advantages:
Reduced bundle size
No transitive dependencies means smaller packages
Enhanced stability
No risk of breaking changes from upstream dependencies
Simplified security
Fewer potential vulnerabilities from third-party code
Lower integration overhead
Particularly valuable for library authors who don't want to burden users with additional dependencies
Runtime Diversity: Write Once, Log Everywhere
While many popular logging libraries focus primarily on Node.js, LogTape provides seamless support across diverse JavaScript runtimes:
This runtime flexibility means you can use consistent logging patterns regardless of your deployment environment:
// Same API works seamlessly across all JavaScript runtimesimport { getLogger } from "@logtape/logtape";const logger = getLogger(["my-service", "user-management"]);// Works in Node.js, Deno, Bun, browsers, or edge functionslogger.info`User ${userId} logged in successfully`;
For teams working across multiple platforms or projects transitioning between runtimes, this consistency is invaluable. No need to learn different logging libraries or approaches—LogTape works the same way everywhere.
Hierarchical Categories: Fine-Grained Control
LogTape's hierarchical category system represents a standout feature that's surprisingly rare among JavaScript logging libraries. Categories allow you to organize logs in a tree-like structure:
This hierarchical approach offers powerful benefits:
Targeted filtering
Configure different log levels for different parts of your application
Inheritance
Child loggers inherit settings from parents, reducing configuration overhead
Organizational clarity
Logs naturally follow your application's module structure
Here's how you might configure logging levels for different categories:
await configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log"), }, loggers: [ // Base configuration for all app logs { category: ["my-app"], lowestLevel: "info", sinks: ["console", "file"] }, // More verbose logging just for database components { category: ["my-app", "database"], lowestLevel: "debug", sinks: ["file"] } ]});
With this configuration, all application logs at "info" level and above go to both console and file, while database-specific logs include more detailed "debug" level information, but only in the log file.
Structured Logging: Beyond Simple Text
Modern logging goes beyond simple text strings. LogTape embraces structured logging, which treats log entries as data objects rather than plain text:
The function is only evaluated if the debug level is enabled, preventing unnecessary computation for suppressed log levels.
Extremely Simple Sinks and Filters: Minimal Boilerplate
LogTape's approach to extensibility is remarkably straightforward. Creating custom sinks (output destinations) and filters requires minimal boilerplate code.
Dead Simple Sinks
A sink in LogTape is just a function that receives a log record:
// Creating a custom sink is as simple as defining a functionconst mySink = (record) => { const timestamp = new Date(record.timestamp).toISOString(); const level = record.level.toUpperCase(); const category = record.category.join('.'); // Send to your custom destination myCustomLogService.send({ time: timestamp, priority: level, component: category, message: record.message, ...record.properties });};// Use your custom sink in configurationawait configure({ sinks: { console: getConsoleSink(), custom: mySink }, loggers: [ { category: ["my-app"], sinks: ["console", "custom"] } ]});
Compare this with other libraries that require extending classes, implementing multiple methods, or following specific patterns. LogTape's approach is refreshingly straightforward.
Simple Filters
Similarly, filters in LogTape are just functions that return a Boolean:
// Filter that only passes high-priority or specific component logsconst importantLogsFilter = (record) => { // Always include errors if (record.level === "error" || record.level === "fatal") { return true; } // Always include payment-related logs if (record.category.includes("payments")) { return true; } // Filter out other logs return false;};await configure({ // ...sinks configuration filters: { important: importantLogsFilter }, loggers: [ { category: ["my-app"], sinks: ["alertSystem"], filters: ["important"] } ]});
LogTape also provides a convenient shorthand for level-based filtering:
await configure({ // ...sinks configuration filters: { // This creates a filter for "warning" level and above warningAndAbove: "warning" }, loggers: [ { category: ["my-app"], sinks: ["console"], filters: ["warningAndAbove"] } ]});
Perfect for Library Authors
LogTape is uniquely well-suited for library authors who want to incorporate logging without burdening their users. The core philosophy is simple:
The key point is that the library never calls configure(). Instead, it provides useful log output points with appropriate levels and contextual data.
Applications using the library can then decide exactly how to handle these logs:
// Application codeimport { configure, getConsoleSink } from "@logtape/logtape";import { Database } from "my-awesome-lib";// Configure how logs should be handledawait configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log") }, loggers: [ // Handle all library logs { category: ["my-awesome-lib"], lowestLevel: "info", sinks: ["file"] }, // More verbose for database component during development { category: ["my-awesome-lib", "database"], lowestLevel: "debug", sinks: ["console", "file"] } ]});// Use the libraryconst db = new Database("localhost", 5432, "user");db.connect();
This separation of concerns offers several benefits:
Library users have complete control over log handling
Libraries can provide rich logging without imposing implementation details
No risk of conflict with application logging configurations
Libraries can be "noisy" internally while allowing applications to filter as needed
Contexts for Richer Logging
LogTape provides context mechanisms for adding consistent properties across multiple log messages. This is particularly valuable for tracing requests through a system:
Explicit Contexts
const logger = getLogger(["my-app", "api"]);// Create a logger with contextconst requestLogger = logger.with({ requestId: "abc-123", userId: 42, endpoint: "/users"});// All logs from this logger include the context propertiesrequestLogger.info("Processing request");requestLogger.debug("Validating input");requestLogger.info("Request completed", { durationMs: 120 });
Implicit Contexts (v0.7.0+)
For cases where you want context to apply across function calls without explicit passing:
import { getLogger, withContext } from "@logtape/logtape";function handleRequest(req, res) { withContext({ requestId: req.id, userId: req.user?.id }, () => { // All logs within this function and any functions it calls // will automatically include the context properties processRequest(req, res); });}function processRequest(req, res) { // No need to pass context - it's automatically available getLogger(["my-app", "processor"]).info("Processing data"); // Call other functions that will also inherit the context validateInput(req.body);}function validateInput(data) { // This log also gets the requestId and userId getLogger(["my-app", "validator"]).debug("Validating input", { data });}
This implicit context capability is invaluable for tracing requests through multiple layers of code without manually threading context through every function call.
When LogTape Might Not Be Your Best Choice
While LogTape offers compelling advantages for many use cases, it's not universally the best choice:
Extreme performance requirements
If your application logs tens of thousands of entries per second and raw performance is the top priority, specialized high-performance libraries like Pino may be more suitable with their focus on optimized logging throughput.
Extensive pre-built integrations
If you need immediate integration with numerous specific systems (Elasticsearch, Graylog, etc.) without writing any custom code, Winston's rich ecosystem of transports might provide a faster starting point.
Legacy systems with specific logging requirements
If you're maintaining systems built around specific logging patterns from Java or other environments, purpose-built libraries like Log4js might offer more familiar APIs.
Web browser-only applications with minimal logging needs
For extremely simple web browser-only logging needs where you just want basic console output with levels, even simpler libraries like loglevel might be sufficient.
Conclusion
LogTape stands out in the crowded JavaScript logging landscape by offering a unique combination of features that address real-world development challenges:
Zero dependencies for a lightweight, secure foundation
Runtime diversity supporting Node.js, Deno, Bun, browsers, and edge functions
Hierarchical categories for better log organization and filtering
Structured logging for improved analysis and searchability
Simple extension mechanisms with minimal boilerplate
Library-friendly design that respects separation of concerns
Whether you're building applications or libraries, working across multiple JavaScript runtimes, or simply seeking a clean, well-designed logging solution, LogTape deserves serious consideration. Its thoughtful design balances simplicity with powerful features, avoiding common pitfalls of JavaScript logging libraries.
In the diverse and ever-evolving JavaScript ecosystem, logging remains a critical component for development, debugging, and monitoring applications. While numerous logging libraries exist, LogTape stands out with its unique combination of simplicity, flexibility, and cross-runtime compatibility. Let's explore why LogTape deserves consideration for your next JavaScript or TypeScript project—whether you're building an application or a library.
Zero Dependencies: A Lightweight Footprint
One of LogTape's most compelling features is its complete absence of dependencies. In an era where “dependency hell” plagues many JavaScript projects, LogTape offers a refreshing alternative:
// No additional packages to install beyond LogTape itselfimport { configure, getConsoleSink, getLogger } from "@logtape/logtape";
This zero-dependency approach provides several advantages:
Reduced bundle size
No transitive dependencies means smaller packages
Enhanced stability
No risk of breaking changes from upstream dependencies
Simplified security
Fewer potential vulnerabilities from third-party code
Lower integration overhead
Particularly valuable for library authors who don't want to burden users with additional dependencies
Runtime Diversity: Write Once, Log Everywhere
While many popular logging libraries focus primarily on Node.js, LogTape provides seamless support across diverse JavaScript runtimes:
This runtime flexibility means you can use consistent logging patterns regardless of your deployment environment:
// Same API works seamlessly across all JavaScript runtimesimport { getLogger } from "@logtape/logtape";const logger = getLogger(["my-service", "user-management"]);// Works in Node.js, Deno, Bun, browsers, or edge functionslogger.info`User ${userId} logged in successfully`;
For teams working across multiple platforms or projects transitioning between runtimes, this consistency is invaluable. No need to learn different logging libraries or approaches—LogTape works the same way everywhere.
Hierarchical Categories: Fine-Grained Control
LogTape's hierarchical category system represents a standout feature that's surprisingly rare among JavaScript logging libraries. Categories allow you to organize logs in a tree-like structure:
This hierarchical approach offers powerful benefits:
Targeted filtering
Configure different log levels for different parts of your application
Inheritance
Child loggers inherit settings from parents, reducing configuration overhead
Organizational clarity
Logs naturally follow your application's module structure
Here's how you might configure logging levels for different categories:
await configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log"), }, loggers: [ // Base configuration for all app logs { category: ["my-app"], lowestLevel: "info", sinks: ["console", "file"] }, // More verbose logging just for database components { category: ["my-app", "database"], lowestLevel: "debug", sinks: ["file"] } ]});
With this configuration, all application logs at "info" level and above go to both console and file, while database-specific logs include more detailed "debug" level information, but only in the log file.
Structured Logging: Beyond Simple Text
Modern logging goes beyond simple text strings. LogTape embraces structured logging, which treats log entries as data objects rather than plain text:
The function is only evaluated if the debug level is enabled, preventing unnecessary computation for suppressed log levels.
Extremely Simple Sinks and Filters: Minimal Boilerplate
LogTape's approach to extensibility is remarkably straightforward. Creating custom sinks (output destinations) and filters requires minimal boilerplate code.
Dead Simple Sinks
A sink in LogTape is just a function that receives a log record:
// Creating a custom sink is as simple as defining a functionconst mySink = (record) => { const timestamp = new Date(record.timestamp).toISOString(); const level = record.level.toUpperCase(); const category = record.category.join('.'); // Send to your custom destination myCustomLogService.send({ time: timestamp, priority: level, component: category, message: record.message, ...record.properties });};// Use your custom sink in configurationawait configure({ sinks: { console: getConsoleSink(), custom: mySink }, loggers: [ { category: ["my-app"], sinks: ["console", "custom"] } ]});
Compare this with other libraries that require extending classes, implementing multiple methods, or following specific patterns. LogTape's approach is refreshingly straightforward.
Simple Filters
Similarly, filters in LogTape are just functions that return a Boolean:
// Filter that only passes high-priority or specific component logsconst importantLogsFilter = (record) => { // Always include errors if (record.level === "error" || record.level === "fatal") { return true; } // Always include payment-related logs if (record.category.includes("payments")) { return true; } // Filter out other logs return false;};await configure({ // ...sinks configuration filters: { important: importantLogsFilter }, loggers: [ { category: ["my-app"], sinks: ["alertSystem"], filters: ["important"] } ]});
LogTape also provides a convenient shorthand for level-based filtering:
await configure({ // ...sinks configuration filters: { // This creates a filter for "warning" level and above warningAndAbove: "warning" }, loggers: [ { category: ["my-app"], sinks: ["console"], filters: ["warningAndAbove"] } ]});
Perfect for Library Authors
LogTape is uniquely well-suited for library authors who want to incorporate logging without burdening their users. The core philosophy is simple:
The key point is that the library never calls configure(). Instead, it provides useful log output points with appropriate levels and contextual data.
Applications using the library can then decide exactly how to handle these logs:
// Application codeimport { configure, getConsoleSink } from "@logtape/logtape";import { Database } from "my-awesome-lib";// Configure how logs should be handledawait configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log") }, loggers: [ // Handle all library logs { category: ["my-awesome-lib"], lowestLevel: "info", sinks: ["file"] }, // More verbose for database component during development { category: ["my-awesome-lib", "database"], lowestLevel: "debug", sinks: ["console", "file"] } ]});// Use the libraryconst db = new Database("localhost", 5432, "user");db.connect();
This separation of concerns offers several benefits:
Library users have complete control over log handling
Libraries can provide rich logging without imposing implementation details
No risk of conflict with application logging configurations
Libraries can be "noisy" internally while allowing applications to filter as needed
Contexts for Richer Logging
LogTape provides context mechanisms for adding consistent properties across multiple log messages. This is particularly valuable for tracing requests through a system:
Explicit Contexts
const logger = getLogger(["my-app", "api"]);// Create a logger with contextconst requestLogger = logger.with({ requestId: "abc-123", userId: 42, endpoint: "/users"});// All logs from this logger include the context propertiesrequestLogger.info("Processing request");requestLogger.debug("Validating input");requestLogger.info("Request completed", { durationMs: 120 });
Implicit Contexts (v0.7.0+)
For cases where you want context to apply across function calls without explicit passing:
import { getLogger, withContext } from "@logtape/logtape";function handleRequest(req, res) { withContext({ requestId: req.id, userId: req.user?.id }, () => { // All logs within this function and any functions it calls // will automatically include the context properties processRequest(req, res); });}function processRequest(req, res) { // No need to pass context - it's automatically available getLogger(["my-app", "processor"]).info("Processing data"); // Call other functions that will also inherit the context validateInput(req.body);}function validateInput(data) { // This log also gets the requestId and userId getLogger(["my-app", "validator"]).debug("Validating input", { data });}
This implicit context capability is invaluable for tracing requests through multiple layers of code without manually threading context through every function call.
When LogTape Might Not Be Your Best Choice
While LogTape offers compelling advantages for many use cases, it's not universally the best choice:
Extreme performance requirements
If your application logs tens of thousands of entries per second and raw performance is the top priority, specialized high-performance libraries like Pino may be more suitable with their focus on optimized logging throughput.
Extensive pre-built integrations
If you need immediate integration with numerous specific systems (Elasticsearch, Graylog, etc.) without writing any custom code, Winston's rich ecosystem of transports might provide a faster starting point.
Legacy systems with specific logging requirements
If you're maintaining systems built around specific logging patterns from Java or other environments, purpose-built libraries like Log4js might offer more familiar APIs.
Web browser-only applications with minimal logging needs
For extremely simple web browser-only logging needs where you just want basic console output with levels, even simpler libraries like loglevel might be sufficient.
Conclusion
LogTape stands out in the crowded JavaScript logging landscape by offering a unique combination of features that address real-world development challenges:
Zero dependencies for a lightweight, secure foundation
Runtime diversity supporting Node.js, Deno, Bun, browsers, and edge functions
Hierarchical categories for better log organization and filtering
Structured logging for improved analysis and searchability
Simple extension mechanisms with minimal boilerplate
Library-friendly design that respects separation of concerns
Whether you're building applications or libraries, working across multiple JavaScript runtimes, or simply seeking a clean, well-designed logging solution, LogTape deserves serious consideration. Its thoughtful design balances simplicity with powerful features, avoiding common pitfalls of JavaScript logging libraries.
In the diverse and ever-evolving JavaScript ecosystem, logging remains a critical component for development, debugging, and monitoring applications. While numerous logging libraries exist, LogTape stands out with its unique combination of simplicity, flexibility, and cross-runtime compatibility. Let's explore why LogTape deserves consideration for your next JavaScript or TypeScript project—whether you're building an application or a library.
Zero Dependencies: A Lightweight Footprint
One of LogTape's most compelling features is its complete absence of dependencies. In an era where “dependency hell” plagues many JavaScript projects, LogTape offers a refreshing alternative:
// No additional packages to install beyond LogTape itselfimport { configure, getConsoleSink, getLogger } from "@logtape/logtape";
This zero-dependency approach provides several advantages:
Reduced bundle size
No transitive dependencies means smaller packages
Enhanced stability
No risk of breaking changes from upstream dependencies
Simplified security
Fewer potential vulnerabilities from third-party code
Lower integration overhead
Particularly valuable for library authors who don't want to burden users with additional dependencies
Runtime Diversity: Write Once, Log Everywhere
While many popular logging libraries focus primarily on Node.js, LogTape provides seamless support across diverse JavaScript runtimes:
This runtime flexibility means you can use consistent logging patterns regardless of your deployment environment:
// Same API works seamlessly across all JavaScript runtimesimport { getLogger } from "@logtape/logtape";const logger = getLogger(["my-service", "user-management"]);// Works in Node.js, Deno, Bun, browsers, or edge functionslogger.info`User ${userId} logged in successfully`;
For teams working across multiple platforms or projects transitioning between runtimes, this consistency is invaluable. No need to learn different logging libraries or approaches—LogTape works the same way everywhere.
Hierarchical Categories: Fine-Grained Control
LogTape's hierarchical category system represents a standout feature that's surprisingly rare among JavaScript logging libraries. Categories allow you to organize logs in a tree-like structure:
This hierarchical approach offers powerful benefits:
Targeted filtering
Configure different log levels for different parts of your application
Inheritance
Child loggers inherit settings from parents, reducing configuration overhead
Organizational clarity
Logs naturally follow your application's module structure
Here's how you might configure logging levels for different categories:
await configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log"), }, loggers: [ // Base configuration for all app logs { category: ["my-app"], lowestLevel: "info", sinks: ["console", "file"] }, // More verbose logging just for database components { category: ["my-app", "database"], lowestLevel: "debug", sinks: ["file"] } ]});
With this configuration, all application logs at "info" level and above go to both console and file, while database-specific logs include more detailed "debug" level information, but only in the log file.
Structured Logging: Beyond Simple Text
Modern logging goes beyond simple text strings. LogTape embraces structured logging, which treats log entries as data objects rather than plain text:
The function is only evaluated if the debug level is enabled, preventing unnecessary computation for suppressed log levels.
Extremely Simple Sinks and Filters: Minimal Boilerplate
LogTape's approach to extensibility is remarkably straightforward. Creating custom sinks (output destinations) and filters requires minimal boilerplate code.
Dead Simple Sinks
A sink in LogTape is just a function that receives a log record:
// Creating a custom sink is as simple as defining a functionconst mySink = (record) => { const timestamp = new Date(record.timestamp).toISOString(); const level = record.level.toUpperCase(); const category = record.category.join('.'); // Send to your custom destination myCustomLogService.send({ time: timestamp, priority: level, component: category, message: record.message, ...record.properties });};// Use your custom sink in configurationawait configure({ sinks: { console: getConsoleSink(), custom: mySink }, loggers: [ { category: ["my-app"], sinks: ["console", "custom"] } ]});
Compare this with other libraries that require extending classes, implementing multiple methods, or following specific patterns. LogTape's approach is refreshingly straightforward.
Simple Filters
Similarly, filters in LogTape are just functions that return a Boolean:
// Filter that only passes high-priority or specific component logsconst importantLogsFilter = (record) => { // Always include errors if (record.level === "error" || record.level === "fatal") { return true; } // Always include payment-related logs if (record.category.includes("payments")) { return true; } // Filter out other logs return false;};await configure({ // ...sinks configuration filters: { important: importantLogsFilter }, loggers: [ { category: ["my-app"], sinks: ["alertSystem"], filters: ["important"] } ]});
LogTape also provides a convenient shorthand for level-based filtering:
await configure({ // ...sinks configuration filters: { // This creates a filter for "warning" level and above warningAndAbove: "warning" }, loggers: [ { category: ["my-app"], sinks: ["console"], filters: ["warningAndAbove"] } ]});
Perfect for Library Authors
LogTape is uniquely well-suited for library authors who want to incorporate logging without burdening their users. The core philosophy is simple:
The key point is that the library never calls configure(). Instead, it provides useful log output points with appropriate levels and contextual data.
Applications using the library can then decide exactly how to handle these logs:
// Application codeimport { configure, getConsoleSink } from "@logtape/logtape";import { Database } from "my-awesome-lib";// Configure how logs should be handledawait configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log") }, loggers: [ // Handle all library logs { category: ["my-awesome-lib"], lowestLevel: "info", sinks: ["file"] }, // More verbose for database component during development { category: ["my-awesome-lib", "database"], lowestLevel: "debug", sinks: ["console", "file"] } ]});// Use the libraryconst db = new Database("localhost", 5432, "user");db.connect();
This separation of concerns offers several benefits:
Library users have complete control over log handling
Libraries can provide rich logging without imposing implementation details
No risk of conflict with application logging configurations
Libraries can be "noisy" internally while allowing applications to filter as needed
Contexts for Richer Logging
LogTape provides context mechanisms for adding consistent properties across multiple log messages. This is particularly valuable for tracing requests through a system:
Explicit Contexts
const logger = getLogger(["my-app", "api"]);// Create a logger with contextconst requestLogger = logger.with({ requestId: "abc-123", userId: 42, endpoint: "/users"});// All logs from this logger include the context propertiesrequestLogger.info("Processing request");requestLogger.debug("Validating input");requestLogger.info("Request completed", { durationMs: 120 });
Implicit Contexts (v0.7.0+)
For cases where you want context to apply across function calls without explicit passing:
import { getLogger, withContext } from "@logtape/logtape";function handleRequest(req, res) { withContext({ requestId: req.id, userId: req.user?.id }, () => { // All logs within this function and any functions it calls // will automatically include the context properties processRequest(req, res); });}function processRequest(req, res) { // No need to pass context - it's automatically available getLogger(["my-app", "processor"]).info("Processing data"); // Call other functions that will also inherit the context validateInput(req.body);}function validateInput(data) { // This log also gets the requestId and userId getLogger(["my-app", "validator"]).debug("Validating input", { data });}
This implicit context capability is invaluable for tracing requests through multiple layers of code without manually threading context through every function call.
When LogTape Might Not Be Your Best Choice
While LogTape offers compelling advantages for many use cases, it's not universally the best choice:
Extreme performance requirements
If your application logs tens of thousands of entries per second and raw performance is the top priority, specialized high-performance libraries like Pino may be more suitable with their focus on optimized logging throughput.
Extensive pre-built integrations
If you need immediate integration with numerous specific systems (Elasticsearch, Graylog, etc.) without writing any custom code, Winston's rich ecosystem of transports might provide a faster starting point.
Legacy systems with specific logging requirements
If you're maintaining systems built around specific logging patterns from Java or other environments, purpose-built libraries like Log4js might offer more familiar APIs.
Web browser-only applications with minimal logging needs
For extremely simple web browser-only logging needs where you just want basic console output with levels, even simpler libraries like loglevel might be sufficient.
Conclusion
LogTape stands out in the crowded JavaScript logging landscape by offering a unique combination of features that address real-world development challenges:
Zero dependencies for a lightweight, secure foundation
Runtime diversity supporting Node.js, Deno, Bun, browsers, and edge functions
Hierarchical categories for better log organization and filtering
Structured logging for improved analysis and searchability
Simple extension mechanisms with minimal boilerplate
Library-friendly design that respects separation of concerns
Whether you're building applications or libraries, working across multiple JavaScript runtimes, or simply seeking a clean, well-designed logging solution, LogTape deserves serious consideration. Its thoughtful design balances simplicity with powerful features, avoiding common pitfalls of JavaScript logging libraries.
In the diverse and ever-evolving JavaScript ecosystem, logging remains a critical component for development, debugging, and monitoring applications. While numerous logging libraries exist, LogTape stands out with its unique combination of simplicity, flexibility, and cross-runtime compatibility. Let's explore why LogTape deserves consideration for your next JavaScript or TypeScript project—whether you're building an application or a library.
Zero Dependencies: A Lightweight Footprint
One of LogTape's most compelling features is its complete absence of dependencies. In an era where “dependency hell” plagues many JavaScript projects, LogTape offers a refreshing alternative:
// No additional packages to install beyond LogTape itselfimport { configure, getConsoleSink, getLogger } from "@logtape/logtape";
This zero-dependency approach provides several advantages:
Reduced bundle size
No transitive dependencies means smaller packages
Enhanced stability
No risk of breaking changes from upstream dependencies
Simplified security
Fewer potential vulnerabilities from third-party code
Lower integration overhead
Particularly valuable for library authors who don't want to burden users with additional dependencies
Runtime Diversity: Write Once, Log Everywhere
While many popular logging libraries focus primarily on Node.js, LogTape provides seamless support across diverse JavaScript runtimes:
This runtime flexibility means you can use consistent logging patterns regardless of your deployment environment:
// Same API works seamlessly across all JavaScript runtimesimport { getLogger } from "@logtape/logtape";const logger = getLogger(["my-service", "user-management"]);// Works in Node.js, Deno, Bun, browsers, or edge functionslogger.info`User ${userId} logged in successfully`;
For teams working across multiple platforms or projects transitioning between runtimes, this consistency is invaluable. No need to learn different logging libraries or approaches—LogTape works the same way everywhere.
Hierarchical Categories: Fine-Grained Control
LogTape's hierarchical category system represents a standout feature that's surprisingly rare among JavaScript logging libraries. Categories allow you to organize logs in a tree-like structure:
This hierarchical approach offers powerful benefits:
Targeted filtering
Configure different log levels for different parts of your application
Inheritance
Child loggers inherit settings from parents, reducing configuration overhead
Organizational clarity
Logs naturally follow your application's module structure
Here's how you might configure logging levels for different categories:
await configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log"), }, loggers: [ // Base configuration for all app logs { category: ["my-app"], lowestLevel: "info", sinks: ["console", "file"] }, // More verbose logging just for database components { category: ["my-app", "database"], lowestLevel: "debug", sinks: ["file"] } ]});
With this configuration, all application logs at "info" level and above go to both console and file, while database-specific logs include more detailed "debug" level information, but only in the log file.
Structured Logging: Beyond Simple Text
Modern logging goes beyond simple text strings. LogTape embraces structured logging, which treats log entries as data objects rather than plain text:
The function is only evaluated if the debug level is enabled, preventing unnecessary computation for suppressed log levels.
Extremely Simple Sinks and Filters: Minimal Boilerplate
LogTape's approach to extensibility is remarkably straightforward. Creating custom sinks (output destinations) and filters requires minimal boilerplate code.
Dead Simple Sinks
A sink in LogTape is just a function that receives a log record:
// Creating a custom sink is as simple as defining a functionconst mySink = (record) => { const timestamp = new Date(record.timestamp).toISOString(); const level = record.level.toUpperCase(); const category = record.category.join('.'); // Send to your custom destination myCustomLogService.send({ time: timestamp, priority: level, component: category, message: record.message, ...record.properties });};// Use your custom sink in configurationawait configure({ sinks: { console: getConsoleSink(), custom: mySink }, loggers: [ { category: ["my-app"], sinks: ["console", "custom"] } ]});
Compare this with other libraries that require extending classes, implementing multiple methods, or following specific patterns. LogTape's approach is refreshingly straightforward.
Simple Filters
Similarly, filters in LogTape are just functions that return a Boolean:
// Filter that only passes high-priority or specific component logsconst importantLogsFilter = (record) => { // Always include errors if (record.level === "error" || record.level === "fatal") { return true; } // Always include payment-related logs if (record.category.includes("payments")) { return true; } // Filter out other logs return false;};await configure({ // ...sinks configuration filters: { important: importantLogsFilter }, loggers: [ { category: ["my-app"], sinks: ["alertSystem"], filters: ["important"] } ]});
LogTape also provides a convenient shorthand for level-based filtering:
await configure({ // ...sinks configuration filters: { // This creates a filter for "warning" level and above warningAndAbove: "warning" }, loggers: [ { category: ["my-app"], sinks: ["console"], filters: ["warningAndAbove"] } ]});
Perfect for Library Authors
LogTape is uniquely well-suited for library authors who want to incorporate logging without burdening their users. The core philosophy is simple:
The key point is that the library never calls configure(). Instead, it provides useful log output points with appropriate levels and contextual data.
Applications using the library can then decide exactly how to handle these logs:
// Application codeimport { configure, getConsoleSink } from "@logtape/logtape";import { Database } from "my-awesome-lib";// Configure how logs should be handledawait configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log") }, loggers: [ // Handle all library logs { category: ["my-awesome-lib"], lowestLevel: "info", sinks: ["file"] }, // More verbose for database component during development { category: ["my-awesome-lib", "database"], lowestLevel: "debug", sinks: ["console", "file"] } ]});// Use the libraryconst db = new Database("localhost", 5432, "user");db.connect();
This separation of concerns offers several benefits:
Library users have complete control over log handling
Libraries can provide rich logging without imposing implementation details
No risk of conflict with application logging configurations
Libraries can be "noisy" internally while allowing applications to filter as needed
Contexts for Richer Logging
LogTape provides context mechanisms for adding consistent properties across multiple log messages. This is particularly valuable for tracing requests through a system:
Explicit Contexts
const logger = getLogger(["my-app", "api"]);// Create a logger with contextconst requestLogger = logger.with({ requestId: "abc-123", userId: 42, endpoint: "/users"});// All logs from this logger include the context propertiesrequestLogger.info("Processing request");requestLogger.debug("Validating input");requestLogger.info("Request completed", { durationMs: 120 });
Implicit Contexts (v0.7.0+)
For cases where you want context to apply across function calls without explicit passing:
import { getLogger, withContext } from "@logtape/logtape";function handleRequest(req, res) { withContext({ requestId: req.id, userId: req.user?.id }, () => { // All logs within this function and any functions it calls // will automatically include the context properties processRequest(req, res); });}function processRequest(req, res) { // No need to pass context - it's automatically available getLogger(["my-app", "processor"]).info("Processing data"); // Call other functions that will also inherit the context validateInput(req.body);}function validateInput(data) { // This log also gets the requestId and userId getLogger(["my-app", "validator"]).debug("Validating input", { data });}
This implicit context capability is invaluable for tracing requests through multiple layers of code without manually threading context through every function call.
When LogTape Might Not Be Your Best Choice
While LogTape offers compelling advantages for many use cases, it's not universally the best choice:
Extreme performance requirements
If your application logs tens of thousands of entries per second and raw performance is the top priority, specialized high-performance libraries like Pino may be more suitable with their focus on optimized logging throughput.
Extensive pre-built integrations
If you need immediate integration with numerous specific systems (Elasticsearch, Graylog, etc.) without writing any custom code, Winston's rich ecosystem of transports might provide a faster starting point.
Legacy systems with specific logging requirements
If you're maintaining systems built around specific logging patterns from Java or other environments, purpose-built libraries like Log4js might offer more familiar APIs.
Web browser-only applications with minimal logging needs
For extremely simple web browser-only logging needs where you just want basic console output with levels, even simpler libraries like loglevel might be sufficient.
Conclusion
LogTape stands out in the crowded JavaScript logging landscape by offering a unique combination of features that address real-world development challenges:
Zero dependencies for a lightweight, secure foundation
Runtime diversity supporting Node.js, Deno, Bun, browsers, and edge functions
Hierarchical categories for better log organization and filtering
Structured logging for improved analysis and searchability
Simple extension mechanisms with minimal boilerplate
Library-friendly design that respects separation of concerns
Whether you're building applications or libraries, working across multiple JavaScript runtimes, or simply seeking a clean, well-designed logging solution, LogTape deserves serious consideration. Its thoughtful design balances simplicity with powerful features, avoiding common pitfalls of JavaScript logging libraries.
Okay, yeah, so, that method was BS for two reasons: I'm formatting the milliseconds from the log record, but the rest of the timestamp from "now" (lol, ouch) – but also, the underlying problem is using time.strftime (like the base class) instead of the more modern datetime.datetime.strftime. Because the latter actually _does_ have a format string placeholder for microseconds (not milliseconds though).
So, after a bit of iteration, I think I'll be going with this one:
ALT text detailsThe class now sets its format string in a class attribute "default_time_format" just like the base class does. It's also using datetime.datetime.fromtimestamp() to convert record.created (a float) into a (naive) datetime object, and then calls .astimezone() without arguments to convert it into an aware datetime for the local timezone. After that, it's just a call to strftime with either the datefmt string passed to the method or the class-level default as its format string.
Code follows:
class ISOTimeFormatter(logging.Formatter):
default_time_format = "%Y-%m-%d %H:%M:%S.%f%z"
def formatTime(
self, record: logging.LogRecord, datefmt: str | None = None
) -> str:
return (
datetime.fromtimestamp(record.created)
.astimezone() # convert to aware object using local timezone
.strftime(datefmt or self.default_time_format)
)
I’ve been logging everything I do in a personal CHANGELOG.md in @obsidian since January, and I absolutely love it.
Every day I add something to the list, and it makes me feel like I'm achieving something. This is an important part of recovering from feelings of inadequacy and workaholic tendencies.
ALT text detailsLife changelog
> [!NOTE] Please note
> Remember the same constants, like days alive, inboxzero, run kms, financial meetings, etc. These are easily missed from day to day updates.
### 1.8.7: 2025-04-18
* Day 13317 alive
* InboxZero
* A bit of work, checking Slack, emails, etc... (yeah, yeah, I know it's Easter day, Good Friday or something and should not be working)
* Low recovery, so rest day today
* Go to movies with the whole family to watch Minecraft
* Install obsidian-share-as-gist and obsidian-imgur-plugin, Closes FUN-6
* Move Sunsama backlog to Linear, Closes FUN-10
### 1.8.6: 2025-04-17
* Day 13316 alive
* InboxZero
* Hard stop *before* 6pm successful 🔥 **9-day streak**
* Quick YNAB
ALT text detailsA Python class ISOTimeFormatter (extending logging.Formatter) that uses an f-string to inject record.msecs into the format string passed to time.strftime. Code follows:
class ISOTimeFormatter(logging.Formatter):
@staticmethod
def formatTime(
record: logging.LogRecord, datefmt: str | None = None
) -> str:
return time.strftime(f"%Y-%m-%d %H:%M:%S.{record.msecs:03.0f}%z")
This version brings two major improvements to our zero-dependency #logging library for #TypeScript:
New Synchronous Configuration API: You can now configure LogTape synchronously with configureSync(), disposeSync(), and resetSync(). Perfect for simpler setups where you don't need async operations!
Better Runtime Compatibility: We've moved all file-related components to a separate @logtape/file package. This means the core package now works flawlessly across all JavaScript environments—browsers, edge functions, and various bundlers without any file system dependencies.
Plus, we've added level mapping options for console sinks, giving you more control over how your logs appear.
Check out the full release notes for migration details:
We're excited to announce the release of LogTape 0.9.0! This version brings important improvements to make LogTape more flexible across different JavaScript environments while simplifying configuration for common use cases.
What's New
Synchronous Configuration API: Added new synchronous configuration functions for environments where async operations aren't needed or desired
Improved Runtime Compatibility: Moved file-system dependent components to a separate package for better cross-runtime support
New Features
Synchronous Configuration API: Simplifying Your Setup
Added synchronous versions of the configuration functions:
These functions offer a simpler API for scenarios where async operations aren't needed, allowing for more straightforward code without awaiting promises. Note that these functions cannot use sinks or filters that require asynchronous disposal (such as stream sinks), but they work perfectly for most common logging configurations.
This architectural change ensures the core @logtape/logtape package is fully compatible with all JavaScript runtimes, including browsers and edge functions, without introducing file system dependencies. You'll now enjoy better compatibility with bundlers like Webpack, Rollup, and Vite that previously had issues with the file system imports.
Migration Guide
If you were using file sinks, update your imports:
// Beforeimport { getFileSink, getRotatingFileSink } from "@logtape/logtape";// Afterimport { getFileSink, getRotatingFileSink } from "@logtape/file";
Don't forget to install the new package:
# For npm, pnpm, Yarn, Bunnpm add @logtape/file# For Denodeno add jsr:@logtape/file
Looking Forward
This release represents our ongoing commitment to making LogTape the most flexible and developer-friendly logging solution for JavaScript and TypeScript applications. We're continuing to improve performance and extend compatibility across the JavaScript ecosystem.
Contributors
Special thanks to Murph Murphy for their valuable contribution to this release.
As always, we welcome your feedback and contributions! Feel free to open issues or pull requests on our GitHub repository.
This version brings two major improvements to our zero-dependency #logging library for #TypeScript:
New Synchronous Configuration API: You can now configure LogTape synchronously with configureSync(), disposeSync(), and resetSync(). Perfect for simpler setups where you don't need async operations!
Better Runtime Compatibility: We've moved all file-related components to a separate @logtape/file package. This means the core package now works flawlessly across all JavaScript environments—browsers, edge functions, and various bundlers without any file system dependencies.
Plus, we've added level mapping options for console sinks, giving you more control over how your logs appear.
Check out the full release notes for migration details:
We're excited to announce the release of LogTape 0.9.0! This version brings important improvements to make LogTape more flexible across different JavaScript environments while simplifying configuration for common use cases.
What's New
Synchronous Configuration API: Added new synchronous configuration functions for environments where async operations aren't needed or desired
Improved Runtime Compatibility: Moved file-system dependent components to a separate package for better cross-runtime support
New Features
Synchronous Configuration API: Simplifying Your Setup
Added synchronous versions of the configuration functions:
These functions offer a simpler API for scenarios where async operations aren't needed, allowing for more straightforward code without awaiting promises. Note that these functions cannot use sinks or filters that require asynchronous disposal (such as stream sinks), but they work perfectly for most common logging configurations.
This architectural change ensures the core @logtape/logtape package is fully compatible with all JavaScript runtimes, including browsers and edge functions, without introducing file system dependencies. You'll now enjoy better compatibility with bundlers like Webpack, Rollup, and Vite that previously had issues with the file system imports.
Migration Guide
If you were using file sinks, update your imports:
// Beforeimport { getFileSink, getRotatingFileSink } from "@logtape/logtape";// Afterimport { getFileSink, getRotatingFileSink } from "@logtape/file";
Don't forget to install the new package:
# For npm, pnpm, Yarn, Bunnpm add @logtape/file# For Denodeno add jsr:@logtape/file
Looking Forward
This release represents our ongoing commitment to making LogTape the most flexible and developer-friendly logging solution for JavaScript and TypeScript applications. We're continuing to improve performance and extend compatibility across the JavaScript ecosystem.
Contributors
Special thanks to Murph Murphy for their valuable contribution to this release.
As always, we welcome your feedback and contributions! Feel free to open issues or pull requests on our GitHub repository.
This version brings two major improvements to our zero-dependency #logging library for #TypeScript:
New Synchronous Configuration API: You can now configure LogTape synchronously with configureSync(), disposeSync(), and resetSync(). Perfect for simpler setups where you don't need async operations!
Better Runtime Compatibility: We've moved all file-related components to a separate @logtape/file package. This means the core package now works flawlessly across all JavaScript environments—browsers, edge functions, and various bundlers without any file system dependencies.
Plus, we've added level mapping options for console sinks, giving you more control over how your logs appear.
Check out the full release notes for migration details:
We're excited to announce the release of LogTape 0.9.0! This version brings important improvements to make LogTape more flexible across different JavaScript environments while simplifying configuration for common use cases.
What's New
Synchronous Configuration API: Added new synchronous configuration functions for environments where async operations aren't needed or desired
Improved Runtime Compatibility: Moved file-system dependent components to a separate package for better cross-runtime support
New Features
Synchronous Configuration API: Simplifying Your Setup
Added synchronous versions of the configuration functions:
These functions offer a simpler API for scenarios where async operations aren't needed, allowing for more straightforward code without awaiting promises. Note that these functions cannot use sinks or filters that require asynchronous disposal (such as stream sinks), but they work perfectly for most common logging configurations.
This architectural change ensures the core @logtape/logtape package is fully compatible with all JavaScript runtimes, including browsers and edge functions, without introducing file system dependencies. You'll now enjoy better compatibility with bundlers like Webpack, Rollup, and Vite that previously had issues with the file system imports.
Migration Guide
If you were using file sinks, update your imports:
// Beforeimport { getFileSink, getRotatingFileSink } from "@logtape/logtape";// Afterimport { getFileSink, getRotatingFileSink } from "@logtape/file";
Don't forget to install the new package:
# For npm, pnpm, Yarn, Bunnpm add @logtape/file# For Denodeno add jsr:@logtape/file
Looking Forward
This release represents our ongoing commitment to making LogTape the most flexible and developer-friendly logging solution for JavaScript and TypeScript applications. We're continuing to improve performance and extend compatibility across the JavaScript ecosystem.
Contributors
Special thanks to Murph Murphy for their valuable contribution to this release.
As always, we welcome your feedback and contributions! Feel free to open issues or pull requests on our GitHub repository.
This version brings two major improvements to our zero-dependency #logging library for #TypeScript:
New Synchronous Configuration API: You can now configure LogTape synchronously with configureSync(), disposeSync(), and resetSync(). Perfect for simpler setups where you don't need async operations!
Better Runtime Compatibility: We've moved all file-related components to a separate @logtape/file package. This means the core package now works flawlessly across all JavaScript environments—browsers, edge functions, and various bundlers without any file system dependencies.
Plus, we've added level mapping options for console sinks, giving you more control over how your logs appear.
Check out the full release notes for migration details:
We're excited to announce the release of LogTape 0.9.0! This version brings important improvements to make LogTape more flexible across different JavaScript environments while simplifying configuration for common use cases.
What's New
Synchronous Configuration API: Added new synchronous configuration functions for environments where async operations aren't needed or desired
Improved Runtime Compatibility: Moved file-system dependent components to a separate package for better cross-runtime support
New Features
Synchronous Configuration API: Simplifying Your Setup
Added synchronous versions of the configuration functions:
These functions offer a simpler API for scenarios where async operations aren't needed, allowing for more straightforward code without awaiting promises. Note that these functions cannot use sinks or filters that require asynchronous disposal (such as stream sinks), but they work perfectly for most common logging configurations.
This architectural change ensures the core @logtape/logtape package is fully compatible with all JavaScript runtimes, including browsers and edge functions, without introducing file system dependencies. You'll now enjoy better compatibility with bundlers like Webpack, Rollup, and Vite that previously had issues with the file system imports.
Migration Guide
If you were using file sinks, update your imports:
// Beforeimport { getFileSink, getRotatingFileSink } from "@logtape/logtape";// Afterimport { getFileSink, getRotatingFileSink } from "@logtape/file";
Don't forget to install the new package:
# For npm, pnpm, Yarn, Bunnpm add @logtape/file# For Denodeno add jsr:@logtape/file
Looking Forward
This release represents our ongoing commitment to making LogTape the most flexible and developer-friendly logging solution for JavaScript and TypeScript applications. We're continuing to improve performance and extend compatibility across the JavaScript ecosystem.
Contributors
Special thanks to Murph Murphy for their valuable contribution to this release.
As always, we welcome your feedback and contributions! Feel free to open issues or pull requests on our GitHub repository.
Haven't talked about LogTape in a while—it's a #logging library I made for #JavaScript and #TypeScript. You know how logging can be a pain point in JavaScript/TypeScript development? Well, I tried to address some common frustrations.
What makes it special
Zero dependencies
We've all been there with dependency hell, right? #LogTape has absolutely no external dependencies. Install it without worrying about bloating your node_modules.
You can organize your logs in a tree structure. Want to save only database-related logs to a file? Easy to do. Child categories can inherit settings from their parents too, which keeps things clean and manageable.
Writing a library and want to include logs without stepping on your users' toes? LogTape lets you add logging to your library while giving end users complete control over how those logs are handled.
Haven't talked about LogTape in a while—it's a #logging library I made for #JavaScript and #TypeScript. You know how logging can be a pain point in JavaScript/TypeScript development? Well, I tried to address some common frustrations.
What makes it special
Zero dependencies
We've all been there with dependency hell, right? #LogTape has absolutely no external dependencies. Install it without worrying about bloating your node_modules.
You can organize your logs in a tree structure. Want to save only database-related logs to a file? Easy to do. Child categories can inherit settings from their parents too, which keeps things clean and manageable.
Writing a library and want to include logs without stepping on your users' toes? LogTape lets you add logging to your library while giving end users complete control over how those logs are handled.
Haven't talked about LogTape in a while—it's a #logging library I made for #JavaScript and #TypeScript. You know how logging can be a pain point in JavaScript/TypeScript development? Well, I tried to address some common frustrations.
What makes it special
Zero dependencies
We've all been there with dependency hell, right? #LogTape has absolutely no external dependencies. Install it without worrying about bloating your node_modules.
You can organize your logs in a tree structure. Want to save only database-related logs to a file? Easy to do. Child categories can inherit settings from their parents too, which keeps things clean and manageable.
Writing a library and want to include logs without stepping on your users' toes? LogTape lets you add logging to your library while giving end users complete control over how those logs are handled.
Haven't talked about LogTape in a while—it's a #logging library I made for #JavaScript and #TypeScript. You know how logging can be a pain point in JavaScript/TypeScript development? Well, I tried to address some common frustrations.
What makes it special
Zero dependencies
We've all been there with dependency hell, right? #LogTape has absolutely no external dependencies. Install it without worrying about bloating your node_modules.
You can organize your logs in a tree structure. Want to save only database-related logs to a file? Easy to do. Child categories can inherit settings from their parents too, which keeps things clean and manageable.
Writing a library and want to include logs without stepping on your users' toes? LogTape lets you add logging to your library while giving end users complete control over how those logs are handled.
Haven't talked about LogTape in a while—it's a #logging library I made for #JavaScript and #TypeScript. You know how logging can be a pain point in JavaScript/TypeScript development? Well, I tried to address some common frustrations.
What makes it special
Zero dependencies
We've all been there with dependency hell, right? #LogTape has absolutely no external dependencies. Install it without worrying about bloating your node_modules.
You can organize your logs in a tree structure. Want to save only database-related logs to a file? Easy to do. Child categories can inherit settings from their parents too, which keeps things clean and manageable.
Writing a library and want to include logs without stepping on your users' toes? LogTape lets you add logging to your library while giving end users complete control over how those logs are handled.
Dearest Fediverse (and apologies for the absence),
my book "Hard Work: Producing places, relations and value on a Papua New Guinea resource frontier" was just recently published in full digital open access. (Print-on-demand and oa epub out soon!)
"Hard Work explores the complexities of natural resource extraction, looking at both large-scale processes and personal human-environment interactions. It combines a political ecology focus on the connection between environmental issues and power relations with a focus on how value is produced, represented, and materialized."
ALT text detailsCover of the book "Hard Work". The cover is a photograph showing a foggy tropical river valley. On the foreground on the left stands a Papua New Guinean woman with her back to the camera holding a food basket on her head and walking staff in her right hand looking at the valley. The woman stands in tropical garden and in front of her are banana plants. On right, a bit further from the camera is a tall tree standing in the garden. In the background are forested mountain slopes.
On the top of the cover is the name of the author, "Tuomas Tammisto". Below it is the title of the book: "Hard Work: Producing places, relations and value on a Papua New Guinea resource frontier". On the bottom left is the name of the publisher: "HUP Helsinki University Press".
Just released #LogTape 0.7.0 with implicit contexts! Now you can automatically include request IDs, user IDs, and more in your logs without passing context manually. Read more about this powerful feature:
Activists shared a win with the #Kananaskis logging slowdown last year, temporarily saving 2000 football fields worth of climate-preserving forest & trails but for how long & what comes next? We asked the Hub's Jenny Yeremiy & Joshua Killeen of #CPAWS.
ALT text details2 images, 1 image is podcast episode cover reads "Kananaskis Clearcuts: Outdated Laws, New Threats," includes 'the Climate Lens' and Calgary Climate Hub logos and a banner reads "NEW EPISODE OUT NOW!"
Image 2 is a map of Kanaaskis Country with a red circle around the Highwood area
ALT text details2 images, 1 image is podcast episode cover reads "Kananaskis Clearcuts: Outdated Laws, New Threats," includes 'the Climate Lens' and Calgary Climate Hub logos and a banner reads "NEW EPISODE OUT NOW!"
Image 2 is a map of Kanaaskis Country with a red circle around the Highwood area
ALT text details2 images, 1 image is podcast episode cover reads "Kananaskis Clearcuts: Outdated Laws, New Threats," includes 'the Climate Lens' and Calgary Climate Hub logos and a banner reads "NEW EPISODE OUT NOW!"
Image 2 is a map of Kanaaskis Country with a red circle around the Highwood area
Version 0.5.0 of #LogTape, the zero-dependency #logging library for #Deno, #Node.js, #Bun, edge functions, and browsers, has been released! The main additions include:
• Contexts • ANSI color formatter • Comprehensive docs • A few API conveniences
In the next version of #LogTape, a zero-dependency #logging library for #JavaScript & #TypeScript, contexts will be introduced that allow the same set of properties to be shared across multiple log messages. Thanks to @okikio for collaborating on the design! This feature is available for preview in 0.5.0-dev.60+f819929c.
ALT text detailsconst logger = getLogger(["my-app", "my-module"]);
const ctx = logger.with({ userId: 1234, requestId: "abc" });
ctx.info `This log message will have the context (userId & requestId).`;
ctx.warn("Context can be used inside message template: {userId}, {requestId}.");
Fedify uses hierarchical categories for fine-grained control over log output. Key categories include ["fedify", "federation", "http"] for HTTP requests/responses and ["fedify", "federation", "inbox"]/["fedify", "federation", "outbox"] for incoming/outgoing activities. (There are more categories.)
With #LogTape integration, you gain valuable insights into your Fedify app's behavior, making troubleshooting and optimization much more straightforward!
I've been enjoying infosec.exchange for the last month or so but have been putting off an #Introduction because I'm awkward and anxious (#privacy am I right?). I feel more comfortable talking about my cat than myself or my work on social media, so you'll probably mostly see him amongst my boosts and replies. He's a little hacker who tricks me into FaceID unlocking my iPad for him or hides my pouch of physical security keys to remind me not to be careless with them.
See how I just went on about the cat? Yeah... I feel imposter syndrome about belonging in #InfoSec. I'm an IT #security and #operations focused #SysAdmin (#BlueTeam) whose been fascinated/working with computers since I was 3, and have been doing it professionally for over 10 years now. Does that make me #SecOps? I honestly don't know. I love this community though and want to make an effort to share what I do know more often besides the cat pics or conversations or boosting #ThreatIntel and news I think to share.