#logging

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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 itself
import { 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 runtimes
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-service", "user-management"]);

// Works in Node.js, Deno, Bun, browsers, or edge functions
logger.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:

// Parent category
const appLogger = getLogger(["my-app"]);

// Child category inherits settings from parent
const dbLogger = getLogger(["my-app", "database"]);

// Grandchild category
const queryLogger = getLogger(["my-app", "database", "queries"]);

// Alternative approach using getChild()
const userLogger = appLogger.getChild("users");
const authLogger = userLogger.getChild("auth");

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:

logger.info("User logged in", {
  userId: 123456,
  username: "johndoe",
  loginTime: new Date(),
  ipAddress: "192.168.1.1"
});

LogTape also supports placeholders in messages, connecting structured data with human-readable text:

logger.info("User {username} (ID: {userId}) logged in from {ipAddress}", {
  userId: 123456,
  username: "johndoe",
  ipAddress: "192.168.1.1"
});

Structured logging offers substantial benefits:

Improved searchability
Search for specific field values instead of parsing text
Better analysis
Perform data analysis on structured fields
Consistent format
Enforce standardized log formats
Machine-readable
Easier processing by log management systems

For performance-conscious applications, LogTape offers lazy evaluation of structured data:

logger.debug("Performance metrics", () => ({
  memoryUsage: process.memoryUsage(),
  cpuUsage: process.cpuUsage(),
  timestamp: performance.now()
}));

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 function
const 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 configuration
await 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 logs
const 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:

  1. Libraries provide logging output points
  2. Applications configure how those logs are handled

Here's how a library might implement LogTape:

// my-awesome-lib/database.js
import { getLogger } from "@logtape/logtape";

export class Database {
  private logger = getLogger(["my-awesome-lib", "database"]);

  constructor(host, port, user) {
    this.host = host;
    this.port = port;
    this.user = user;
  }

  connect() {
    this.logger.info("Connecting to database", {
      host: this.host,
      port: this.port,
      user: this.user
    });
    
    // Connection logic...
    
    this.logger.debug("Connection established");
  }
  
  query(sql) {
    this.logger.debug("Executing query", { sql });
    // Query logic...
  }
}

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 code
import { configure, getConsoleSink } from "@logtape/logtape";
import { Database } from "my-awesome-lib";

// Configure how logs should be handled
await 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 library
const db = new Database("localhost", 5432, "user");
db.connect();

This separation of concerns offers several benefits:

  1. Library users have complete control over log handling
  2. Libraries can provide rich logging without imposing implementation details
  3. No risk of conflict with application logging configurations
  4. 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 context
const requestLogger = logger.with({
  requestId: "abc-123",
  userId: 42,
  endpoint: "/users"
});

// All logs from this logger include the context properties
requestLogger.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.

For more information and detailed documentation, visit LogTape's official website.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


Building a JavaScript library is a delicate balance. You want to provide useful functionality while being respectful of your users' choices and constraints. When it comes to logging—something many libraries need for debugging, monitoring, and user support—this balance becomes particularly challenging.

The JavaScript ecosystem has evolved various approaches to this challenge, each with its own trade-offs. LogTape offers a different path, one that's specifically designed with library authors in mind.

The current state of library logging

If you've built libraries before, you've probably encountered the logging dilemma. Your library would benefit from logging—perhaps to help users debug integration issues, trace internal state changes, or provide insights into performance bottlenecks. But how do you add this capability responsibly?

Currently, popular libraries handle this challenge in several ways:

The debug approach
Libraries like Express and Socket.IO use the lightweight debug package, which allows users to enable logging through environment variables (DEBUG=express:*). This works well but creates a separate logging system that doesn't integrate with users' existing logging infrastructure.
Custom logging systems
Libraries like Mongoose and Prisma have built their own logging mechanisms. Mongoose offers mongoose.set('debug', true) while Prisma uses its own logging configuration. These approaches work, but each library creates its own logging API that users must learn separately.
Application-focused libraries
winston, Pino, and Bunyan are powerful logging solutions, but they're primarily designed for applications rather than libraries. Using them in a library means imposing significant dependencies and potentially conflicting with users' existing logging choices.
No logging at all
Many library authors avoid the complexity entirely, leaving their libraries silent and making debugging more challenging for everyone involved.
Dependency injection
Some libraries adopt a more sophisticated approach by accepting a logger instance from the application through their configuration or constructor parameters. This maintains clean separation of concerns and allows libraries to use whatever logging system the application has chosen. However, this pattern requires more complex APIs and places additional burden on library users to understand and configure logging dependencies.

Each approach represents a reasonable solution to a genuine problem, but none fully addresses the core tension: how do you provide valuable diagnostic capabilities without imposing choices on your users?

The fragmentation problem

There's another challenge that emerges when libraries each solve logging in their own way: fragmentation. Consider a typical Node.js application that might use Express for the web framework, Socket.IO for real-time communication, Axios for HTTP requests, Mongoose for database access, and several other specialized libraries.

Each library potentially has its own logging approach:

  • Express uses DEBUG=express:*
  • Socket.IO uses DEBUG=socket.io:*
  • Mongoose uses mongoose.set('debug', true)
  • Axios might use axios-logger or similar packages
  • Redis clients have their own debug configurations
  • Authentication libraries often include their own logging mechanisms

From an application developer's perspective, this creates a management challenge. They must learn and configure multiple different logging systems, each with its own syntax, capabilities, and quirks. Logs are scattered across different outputs with inconsistent formats, making it difficult to get a unified view of what's happening in their application.

The lack of integration also means that powerful features like structured logging, log correlation, and centralized log management become much harder to implement consistently across all the libraries in use.

LogTape's approach

LogTape attempts to address these challenges with what might be called a “library-first design.” The core principle is simple but potentially powerful: if logging isn't configured, nothing happens. No output, no errors, no side effects—just complete transparency.

This approach allows you to add comprehensive logging to your library without any impact on users who don't want it. When a user imports your library and runs their code, LogTape's logging calls are essentially no-ops until someone explicitly configures logging. Users who want insights into your library's behavior can opt in; those who don't are completely unaffected.

More importantly, when users do choose to configure logging, all LogTape-enabled libraries can be managed through a single, unified configuration system. This means one consistent API, one log format, and one destination for all library logs while still allowing fine-grained control over what gets logged from which libraries.

Note

This approach isn't entirely novel—it draws inspiration from Python's standard logging library, which has successfully created a unified logging ecosystem. In Python, libraries like Requests, SQLAlchemy, and Django components all use the standard logging framework, allowing developers to configure all library logging through a single, consistent system. This has proven to be both practical and powerful, enabling rich diagnostic capabilities across the entire Python ecosystem while maintaining simplicity for application developers.

// In your library code - completely safe to include
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-awesome-lib", "database"]);

export function connectToDatabase(config) {
  logger.debug("Attempting database connection", { config });
  // ... your logic
  logger.info("Database connection established");
}

The dependency consideration

Modern JavaScript development involves careful consideration of dependencies. While popular logging libraries like winston and Pino are well-maintained and widely trusted, they do come with their own dependency trees. winston, for example, includes 17 dependencies, while Pino includes 1.

For library authors, this creates a consideration: every dependency you add becomes a dependency for your users, whether they want it or not. This isn't necessarily problematic (many excellent libraries have dependencies), but it does represent a choice you're making on behalf of your users.

LogTape takes a different approach with zero dependencies. This isn't just a philosophical choice—it has practical implications for your library's users. They won't see additional packages in their node_modules, won't need to worry about supply chain considerations for logging-related dependencies, and won't face potential version conflicts between your logging choice and theirs.

At just 5.3KB minified and gzipped, LogTape adds minimal weight to their bundles. The installation process becomes faster, the dependency tree stays cleaner, and security audits remain focused on the dependencies that directly serve your library's core functionality.

Breaking the compatibility chain

Here's a challenge that might be familiar: you want your library to support both ESM and CommonJS environments. Perhaps some of your users are working with legacy Node.js projects that rely on CommonJS, while others are using modern ESM setups or building for browsers.

The challenge becomes apparent when you have dependencies. While ESM modules can import CommonJS modules without issues, the reverse isn't true—CommonJS modules cannot require ESM-only packages (at least not until the experimental features in Node.js 22+ become stable). This creates an asymmetric compatibility constraint.

If your library depends on any ESM-only packages, your library effectively becomes ESM-only as well, since CommonJS environments won't be able to use it. This means that even one ESM-only dependency in your chain can prevent you from supporting CommonJS users.

LogTape supports both ESM and CommonJS completely, meaning it won't be the weak link that forces this limitation. Whether your users are working with legacy Node.js projects, cutting-edge ESM applications, or hybrid environments, LogTape adapts seamlessly to their setup.

More importantly, when LogTape provides native ESM support (rather than just being importable as CommonJS), it enables tree shaking in modern bundlers. Tree shaking allows bundlers to eliminate unused code during the build process, but it requires the static import/export structure that only ESM provides. While CommonJS modules can be imported into ESM projects, they're often treated as opaque blocks that can't be optimized, potentially including unused code in the final bundle.

For a logging library that aims to have minimal impact, this optimization capability can be meaningful, especially for applications where bundle size matters.

Universal runtime support

The JavaScript ecosystem spans an impressive range of runtime environments today. Your library might run in Node.js servers, Deno scripts, Bun applications, web browsers, or edge functions. LogTape works identically across all of these environments without requiring polyfills, compatibility layers, or runtime-specific code.

This universality means you can focus on your library's core functionality rather than worrying about whether your logging choice will work in every environment your users might encounter. Whether someone imports your library into a Cloudflare Worker, a Next.js application, or a Deno CLI tool, the logging behavior remains consistent and reliable.

Performance without compromise

One concern library authors often have about logging is performance impact. What if your users import your library into a high-performance application? What if they're running in a memory-constrained environment?

LogTape addresses this with remarkable efficiency when logging is disabled. The overhead of an unconfigured LogTape call is virtually zero—among the lowest of any logging solution available. This means you can add detailed logging throughout your library for development and debugging purposes without worrying about performance impact on users who don't enable it.

When logging is enabled, LogTape consistently outperforms other libraries, particularly for console output—often the most common logging destination during development.

Avoiding namespace collisions

Libraries sharing the same application can create logging chaos when they all output to the same namespace. LogTape's hierarchical category system elegantly solves this by encouraging libraries to use their own namespaces.

Your library might use categories like ["my-awesome-lib", "database"] or ["my-awesome-lib", "validation"], ensuring that your logs are clearly separated from other libraries and the main application. Users who configure LogTape can then control logging levels independently for different libraries and different components within those libraries.

Developer experience that just works

LogTape is built with TypeScript from the ground up, meaning your TypeScript-based library gets full type safety without additional dependencies or type packages. The API feels natural and modern, supporting both template literals and structured logging patterns that integrate well with contemporary JavaScript development practices.

// Template literal style - feels natural
logger.info`User ${userId} performed action ${action}`;

// Structured logging - great for monitoring
logger.info("User action completed", { userId, action, duration });

Practical integration

Actually using LogTape in your library is refreshingly straightforward. You simply import the logger, create appropriately namespaced categories, and log where it makes sense. No configuration, no setup, no complex initialization sequences.

import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-lib", "api"]);

export async function fetchUserData(userId) {
  logger.debug("Fetching user data", { userId });
  
  try {
    const response = await api.get(`/users/${userId}`);
    logger.info("User data retrieved successfully", { 
      userId, 
      status: response.status 
    });
    return response.data;
  } catch (error) {
    logger.error("Failed to fetch user data", { userId, error });
    throw error;
  }
}

For users who want to see these logs, configuration is equally simple:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: ["my-lib"], lowestLevel: "info", sinks: ["console"] }
  ]
});

Bridging the transition

If your potential users are already invested in other logging systems, LogTape provides adapters for popular libraries like winston and Pino. This allows LogTape-enabled libraries to integrate with existing logging infrastructure, routing their logs through whatever system applications are already using.

The existence of these adapters reveals an honest truth: LogTape isn't yet a widely-adopted standard in the JavaScript ecosystem. Most applications are still built around established logging libraries, and asking users to completely restructure their logging approach would be unrealistic. The adapters represent a practical compromise—they allow library authors to take advantage of LogTape's library-friendly design while respecting users' existing investments and preferences.

This approach reduces friction for adoption while still providing library authors with a modern, zero-dependency logging API. Perhaps over time, as more libraries adopt this pattern and more developers experience its benefits, the need for such adapters might diminish. But for now, they serve as a pragmatic bridge between LogTape's vision and the current reality of the ecosystem.

A choice worth considering

Ultimately, choosing LogTape for your library represents a particular philosophy about the relationship between libraries and applications. It's about providing capabilities while preserving choice, offering insights while avoiding imposition.

The traditional approaches—whether using debug packages, application-focused loggers, or custom solutions—each have their merits and have served the community well. LogTape simply offers another option: one designed specifically for the unique position libraries occupy in the JavaScript ecosystem.

For library authors, this approach might offer several practical benefits. Your library gets detailed logging for development, debugging, and user support, while your users retain complete autonomy over whether and how to use those capabilities.

The broader benefit might be a more cohesive logging experience across the JavaScript ecosystem—one where libraries can provide rich diagnostic information that integrates seamlessly with whatever logging strategy applications choose to employ.

In a world where every dependency decision has implications, LogTape offers an approach worth considering: a way to enhance your library's capabilities while respecting your users' preferences and existing choices.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


Building a JavaScript library is a delicate balance. You want to provide useful functionality while being respectful of your users' choices and constraints. When it comes to logging—something many libraries need for debugging, monitoring, and user support—this balance becomes particularly challenging.

The JavaScript ecosystem has evolved various approaches to this challenge, each with its own trade-offs. LogTape offers a different path, one that's specifically designed with library authors in mind.

The current state of library logging

If you've built libraries before, you've probably encountered the logging dilemma. Your library would benefit from logging—perhaps to help users debug integration issues, trace internal state changes, or provide insights into performance bottlenecks. But how do you add this capability responsibly?

Currently, popular libraries handle this challenge in several ways:

The debug approach
Libraries like Express and Socket.IO use the lightweight debug package, which allows users to enable logging through environment variables (DEBUG=express:*). This works well but creates a separate logging system that doesn't integrate with users' existing logging infrastructure.
Custom logging systems
Libraries like Mongoose and Prisma have built their own logging mechanisms. Mongoose offers mongoose.set('debug', true) while Prisma uses its own logging configuration. These approaches work, but each library creates its own logging API that users must learn separately.
Application-focused libraries
winston, Pino, and Bunyan are powerful logging solutions, but they're primarily designed for applications rather than libraries. Using them in a library means imposing significant dependencies and potentially conflicting with users' existing logging choices.
No logging at all
Many library authors avoid the complexity entirely, leaving their libraries silent and making debugging more challenging for everyone involved.
Dependency injection
Some libraries adopt a more sophisticated approach by accepting a logger instance from the application through their configuration or constructor parameters. This maintains clean separation of concerns and allows libraries to use whatever logging system the application has chosen. However, this pattern requires more complex APIs and places additional burden on library users to understand and configure logging dependencies.

Each approach represents a reasonable solution to a genuine problem, but none fully addresses the core tension: how do you provide valuable diagnostic capabilities without imposing choices on your users?

The fragmentation problem

There's another challenge that emerges when libraries each solve logging in their own way: fragmentation. Consider a typical Node.js application that might use Express for the web framework, Socket.IO for real-time communication, Axios for HTTP requests, Mongoose for database access, and several other specialized libraries.

Each library potentially has its own logging approach:

  • Express uses DEBUG=express:*
  • Socket.IO uses DEBUG=socket.io:*
  • Mongoose uses mongoose.set('debug', true)
  • Axios might use axios-logger or similar packages
  • Redis clients have their own debug configurations
  • Authentication libraries often include their own logging mechanisms

From an application developer's perspective, this creates a management challenge. They must learn and configure multiple different logging systems, each with its own syntax, capabilities, and quirks. Logs are scattered across different outputs with inconsistent formats, making it difficult to get a unified view of what's happening in their application.

The lack of integration also means that powerful features like structured logging, log correlation, and centralized log management become much harder to implement consistently across all the libraries in use.

LogTape's approach

LogTape attempts to address these challenges with what might be called a “library-first design.” The core principle is simple but potentially powerful: if logging isn't configured, nothing happens. No output, no errors, no side effects—just complete transparency.

This approach allows you to add comprehensive logging to your library without any impact on users who don't want it. When a user imports your library and runs their code, LogTape's logging calls are essentially no-ops until someone explicitly configures logging. Users who want insights into your library's behavior can opt in; those who don't are completely unaffected.

More importantly, when users do choose to configure logging, all LogTape-enabled libraries can be managed through a single, unified configuration system. This means one consistent API, one log format, and one destination for all library logs while still allowing fine-grained control over what gets logged from which libraries.

Note

This approach isn't entirely novel—it draws inspiration from Python's standard logging library, which has successfully created a unified logging ecosystem. In Python, libraries like Requests, SQLAlchemy, and Django components all use the standard logging framework, allowing developers to configure all library logging through a single, consistent system. This has proven to be both practical and powerful, enabling rich diagnostic capabilities across the entire Python ecosystem while maintaining simplicity for application developers.

// In your library code - completely safe to include
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-awesome-lib", "database"]);

export function connectToDatabase(config) {
  logger.debug("Attempting database connection", { config });
  // ... your logic
  logger.info("Database connection established");
}

The dependency consideration

Modern JavaScript development involves careful consideration of dependencies. While popular logging libraries like winston and Pino are well-maintained and widely trusted, they do come with their own dependency trees. winston, for example, includes 17 dependencies, while Pino includes 1.

For library authors, this creates a consideration: every dependency you add becomes a dependency for your users, whether they want it or not. This isn't necessarily problematic (many excellent libraries have dependencies), but it does represent a choice you're making on behalf of your users.

LogTape takes a different approach with zero dependencies. This isn't just a philosophical choice—it has practical implications for your library's users. They won't see additional packages in their node_modules, won't need to worry about supply chain considerations for logging-related dependencies, and won't face potential version conflicts between your logging choice and theirs.

At just 5.3KB minified and gzipped, LogTape adds minimal weight to their bundles. The installation process becomes faster, the dependency tree stays cleaner, and security audits remain focused on the dependencies that directly serve your library's core functionality.

Breaking the compatibility chain

Here's a challenge that might be familiar: you want your library to support both ESM and CommonJS environments. Perhaps some of your users are working with legacy Node.js projects that rely on CommonJS, while others are using modern ESM setups or building for browsers.

The challenge becomes apparent when you have dependencies. While ESM modules can import CommonJS modules without issues, the reverse isn't true—CommonJS modules cannot require ESM-only packages (at least not until the experimental features in Node.js 22+ become stable). This creates an asymmetric compatibility constraint.

If your library depends on any ESM-only packages, your library effectively becomes ESM-only as well, since CommonJS environments won't be able to use it. This means that even one ESM-only dependency in your chain can prevent you from supporting CommonJS users.

LogTape supports both ESM and CommonJS completely, meaning it won't be the weak link that forces this limitation. Whether your users are working with legacy Node.js projects, cutting-edge ESM applications, or hybrid environments, LogTape adapts seamlessly to their setup.

More importantly, when LogTape provides native ESM support (rather than just being importable as CommonJS), it enables tree shaking in modern bundlers. Tree shaking allows bundlers to eliminate unused code during the build process, but it requires the static import/export structure that only ESM provides. While CommonJS modules can be imported into ESM projects, they're often treated as opaque blocks that can't be optimized, potentially including unused code in the final bundle.

For a logging library that aims to have minimal impact, this optimization capability can be meaningful, especially for applications where bundle size matters.

Universal runtime support

The JavaScript ecosystem spans an impressive range of runtime environments today. Your library might run in Node.js servers, Deno scripts, Bun applications, web browsers, or edge functions. LogTape works identically across all of these environments without requiring polyfills, compatibility layers, or runtime-specific code.

This universality means you can focus on your library's core functionality rather than worrying about whether your logging choice will work in every environment your users might encounter. Whether someone imports your library into a Cloudflare Worker, a Next.js application, or a Deno CLI tool, the logging behavior remains consistent and reliable.

Performance without compromise

One concern library authors often have about logging is performance impact. What if your users import your library into a high-performance application? What if they're running in a memory-constrained environment?

LogTape addresses this with remarkable efficiency when logging is disabled. The overhead of an unconfigured LogTape call is virtually zero—among the lowest of any logging solution available. This means you can add detailed logging throughout your library for development and debugging purposes without worrying about performance impact on users who don't enable it.

When logging is enabled, LogTape consistently outperforms other libraries, particularly for console output—often the most common logging destination during development.

Avoiding namespace collisions

Libraries sharing the same application can create logging chaos when they all output to the same namespace. LogTape's hierarchical category system elegantly solves this by encouraging libraries to use their own namespaces.

Your library might use categories like ["my-awesome-lib", "database"] or ["my-awesome-lib", "validation"], ensuring that your logs are clearly separated from other libraries and the main application. Users who configure LogTape can then control logging levels independently for different libraries and different components within those libraries.

Developer experience that just works

LogTape is built with TypeScript from the ground up, meaning your TypeScript-based library gets full type safety without additional dependencies or type packages. The API feels natural and modern, supporting both template literals and structured logging patterns that integrate well with contemporary JavaScript development practices.

// Template literal style - feels natural
logger.info`User ${userId} performed action ${action}`;

// Structured logging - great for monitoring
logger.info("User action completed", { userId, action, duration });

Practical integration

Actually using LogTape in your library is refreshingly straightforward. You simply import the logger, create appropriately namespaced categories, and log where it makes sense. No configuration, no setup, no complex initialization sequences.

import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-lib", "api"]);

export async function fetchUserData(userId) {
  logger.debug("Fetching user data", { userId });
  
  try {
    const response = await api.get(`/users/${userId}`);
    logger.info("User data retrieved successfully", { 
      userId, 
      status: response.status 
    });
    return response.data;
  } catch (error) {
    logger.error("Failed to fetch user data", { userId, error });
    throw error;
  }
}

For users who want to see these logs, configuration is equally simple:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: ["my-lib"], lowestLevel: "info", sinks: ["console"] }
  ]
});

Bridging the transition

If your potential users are already invested in other logging systems, LogTape provides adapters for popular libraries like winston and Pino. This allows LogTape-enabled libraries to integrate with existing logging infrastructure, routing their logs through whatever system applications are already using.

The existence of these adapters reveals an honest truth: LogTape isn't yet a widely-adopted standard in the JavaScript ecosystem. Most applications are still built around established logging libraries, and asking users to completely restructure their logging approach would be unrealistic. The adapters represent a practical compromise—they allow library authors to take advantage of LogTape's library-friendly design while respecting users' existing investments and preferences.

This approach reduces friction for adoption while still providing library authors with a modern, zero-dependency logging API. Perhaps over time, as more libraries adopt this pattern and more developers experience its benefits, the need for such adapters might diminish. But for now, they serve as a pragmatic bridge between LogTape's vision and the current reality of the ecosystem.

A choice worth considering

Ultimately, choosing LogTape for your library represents a particular philosophy about the relationship between libraries and applications. It's about providing capabilities while preserving choice, offering insights while avoiding imposition.

The traditional approaches—whether using debug packages, application-focused loggers, or custom solutions—each have their merits and have served the community well. LogTape simply offers another option: one designed specifically for the unique position libraries occupy in the JavaScript ecosystem.

For library authors, this approach might offer several practical benefits. Your library gets detailed logging for development, debugging, and user support, while your users retain complete autonomy over whether and how to use those capabilities.

The broader benefit might be a more cohesive logging experience across the JavaScript ecosystem—one where libraries can provide rich diagnostic information that integrates seamlessly with whatever logging strategy applications choose to employ.

In a world where every dependency decision has implications, LogTape offers an approach worth considering: a way to enhance your library's capabilities while respecting your users' preferences and existing choices.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Just shared my thoughts on library on Hacker News. Explores the fragmentation problem and dependency dilemmas from a library author's perspective. Would love to hear feedback from the / users.

https://news.ycombinator.com/item?id=44351881

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


Building a JavaScript library is a delicate balance. You want to provide useful functionality while being respectful of your users' choices and constraints. When it comes to logging—something many libraries need for debugging, monitoring, and user support—this balance becomes particularly challenging.

The JavaScript ecosystem has evolved various approaches to this challenge, each with its own trade-offs. LogTape offers a different path, one that's specifically designed with library authors in mind.

The current state of library logging

If you've built libraries before, you've probably encountered the logging dilemma. Your library would benefit from logging—perhaps to help users debug integration issues, trace internal state changes, or provide insights into performance bottlenecks. But how do you add this capability responsibly?

Currently, popular libraries handle this challenge in several ways:

The debug approach
Libraries like Express and Socket.IO use the lightweight debug package, which allows users to enable logging through environment variables (DEBUG=express:*). This works well but creates a separate logging system that doesn't integrate with users' existing logging infrastructure.
Custom logging systems
Libraries like Mongoose and Prisma have built their own logging mechanisms. Mongoose offers mongoose.set('debug', true) while Prisma uses its own logging configuration. These approaches work, but each library creates its own logging API that users must learn separately.
Application-focused libraries
winston, Pino, and Bunyan are powerful logging solutions, but they're primarily designed for applications rather than libraries. Using them in a library means imposing significant dependencies and potentially conflicting with users' existing logging choices.
No logging at all
Many library authors avoid the complexity entirely, leaving their libraries silent and making debugging more challenging for everyone involved.
Dependency injection
Some libraries adopt a more sophisticated approach by accepting a logger instance from the application through their configuration or constructor parameters. This maintains clean separation of concerns and allows libraries to use whatever logging system the application has chosen. However, this pattern requires more complex APIs and places additional burden on library users to understand and configure logging dependencies.

Each approach represents a reasonable solution to a genuine problem, but none fully addresses the core tension: how do you provide valuable diagnostic capabilities without imposing choices on your users?

The fragmentation problem

There's another challenge that emerges when libraries each solve logging in their own way: fragmentation. Consider a typical Node.js application that might use Express for the web framework, Socket.IO for real-time communication, Axios for HTTP requests, Mongoose for database access, and several other specialized libraries.

Each library potentially has its own logging approach:

  • Express uses DEBUG=express:*
  • Socket.IO uses DEBUG=socket.io:*
  • Mongoose uses mongoose.set('debug', true)
  • Axios might use axios-logger or similar packages
  • Redis clients have their own debug configurations
  • Authentication libraries often include their own logging mechanisms

From an application developer's perspective, this creates a management challenge. They must learn and configure multiple different logging systems, each with its own syntax, capabilities, and quirks. Logs are scattered across different outputs with inconsistent formats, making it difficult to get a unified view of what's happening in their application.

The lack of integration also means that powerful features like structured logging, log correlation, and centralized log management become much harder to implement consistently across all the libraries in use.

LogTape's approach

LogTape attempts to address these challenges with what might be called a “library-first design.” The core principle is simple but potentially powerful: if logging isn't configured, nothing happens. No output, no errors, no side effects—just complete transparency.

This approach allows you to add comprehensive logging to your library without any impact on users who don't want it. When a user imports your library and runs their code, LogTape's logging calls are essentially no-ops until someone explicitly configures logging. Users who want insights into your library's behavior can opt in; those who don't are completely unaffected.

More importantly, when users do choose to configure logging, all LogTape-enabled libraries can be managed through a single, unified configuration system. This means one consistent API, one log format, and one destination for all library logs while still allowing fine-grained control over what gets logged from which libraries.

Note

This approach isn't entirely novel—it draws inspiration from Python's standard logging library, which has successfully created a unified logging ecosystem. In Python, libraries like Requests, SQLAlchemy, and Django components all use the standard logging framework, allowing developers to configure all library logging through a single, consistent system. This has proven to be both practical and powerful, enabling rich diagnostic capabilities across the entire Python ecosystem while maintaining simplicity for application developers.

// In your library code - completely safe to include
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-awesome-lib", "database"]);

export function connectToDatabase(config) {
  logger.debug("Attempting database connection", { config });
  // ... your logic
  logger.info("Database connection established");
}

The dependency consideration

Modern JavaScript development involves careful consideration of dependencies. While popular logging libraries like winston and Pino are well-maintained and widely trusted, they do come with their own dependency trees. winston, for example, includes 17 dependencies, while Pino includes 1.

For library authors, this creates a consideration: every dependency you add becomes a dependency for your users, whether they want it or not. This isn't necessarily problematic (many excellent libraries have dependencies), but it does represent a choice you're making on behalf of your users.

LogTape takes a different approach with zero dependencies. This isn't just a philosophical choice—it has practical implications for your library's users. They won't see additional packages in their node_modules, won't need to worry about supply chain considerations for logging-related dependencies, and won't face potential version conflicts between your logging choice and theirs.

At just 5.3KB minified and gzipped, LogTape adds minimal weight to their bundles. The installation process becomes faster, the dependency tree stays cleaner, and security audits remain focused on the dependencies that directly serve your library's core functionality.

Breaking the compatibility chain

Here's a challenge that might be familiar: you want your library to support both ESM and CommonJS environments. Perhaps some of your users are working with legacy Node.js projects that rely on CommonJS, while others are using modern ESM setups or building for browsers.

The challenge becomes apparent when you have dependencies. While ESM modules can import CommonJS modules without issues, the reverse isn't true—CommonJS modules cannot require ESM-only packages (at least not until the experimental features in Node.js 22+ become stable). This creates an asymmetric compatibility constraint.

If your library depends on any ESM-only packages, your library effectively becomes ESM-only as well, since CommonJS environments won't be able to use it. This means that even one ESM-only dependency in your chain can prevent you from supporting CommonJS users.

LogTape supports both ESM and CommonJS completely, meaning it won't be the weak link that forces this limitation. Whether your users are working with legacy Node.js projects, cutting-edge ESM applications, or hybrid environments, LogTape adapts seamlessly to their setup.

More importantly, when LogTape provides native ESM support (rather than just being importable as CommonJS), it enables tree shaking in modern bundlers. Tree shaking allows bundlers to eliminate unused code during the build process, but it requires the static import/export structure that only ESM provides. While CommonJS modules can be imported into ESM projects, they're often treated as opaque blocks that can't be optimized, potentially including unused code in the final bundle.

For a logging library that aims to have minimal impact, this optimization capability can be meaningful, especially for applications where bundle size matters.

Universal runtime support

The JavaScript ecosystem spans an impressive range of runtime environments today. Your library might run in Node.js servers, Deno scripts, Bun applications, web browsers, or edge functions. LogTape works identically across all of these environments without requiring polyfills, compatibility layers, or runtime-specific code.

This universality means you can focus on your library's core functionality rather than worrying about whether your logging choice will work in every environment your users might encounter. Whether someone imports your library into a Cloudflare Worker, a Next.js application, or a Deno CLI tool, the logging behavior remains consistent and reliable.

Performance without compromise

One concern library authors often have about logging is performance impact. What if your users import your library into a high-performance application? What if they're running in a memory-constrained environment?

LogTape addresses this with remarkable efficiency when logging is disabled. The overhead of an unconfigured LogTape call is virtually zero—among the lowest of any logging solution available. This means you can add detailed logging throughout your library for development and debugging purposes without worrying about performance impact on users who don't enable it.

When logging is enabled, LogTape consistently outperforms other libraries, particularly for console output—often the most common logging destination during development.

Avoiding namespace collisions

Libraries sharing the same application can create logging chaos when they all output to the same namespace. LogTape's hierarchical category system elegantly solves this by encouraging libraries to use their own namespaces.

Your library might use categories like ["my-awesome-lib", "database"] or ["my-awesome-lib", "validation"], ensuring that your logs are clearly separated from other libraries and the main application. Users who configure LogTape can then control logging levels independently for different libraries and different components within those libraries.

Developer experience that just works

LogTape is built with TypeScript from the ground up, meaning your TypeScript-based library gets full type safety without additional dependencies or type packages. The API feels natural and modern, supporting both template literals and structured logging patterns that integrate well with contemporary JavaScript development practices.

// Template literal style - feels natural
logger.info`User ${userId} performed action ${action}`;

// Structured logging - great for monitoring
logger.info("User action completed", { userId, action, duration });

Practical integration

Actually using LogTape in your library is refreshingly straightforward. You simply import the logger, create appropriately namespaced categories, and log where it makes sense. No configuration, no setup, no complex initialization sequences.

import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-lib", "api"]);

export async function fetchUserData(userId) {
  logger.debug("Fetching user data", { userId });
  
  try {
    const response = await api.get(`/users/${userId}`);
    logger.info("User data retrieved successfully", { 
      userId, 
      status: response.status 
    });
    return response.data;
  } catch (error) {
    logger.error("Failed to fetch user data", { userId, error });
    throw error;
  }
}

For users who want to see these logs, configuration is equally simple:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: ["my-lib"], lowestLevel: "info", sinks: ["console"] }
  ]
});

Bridging the transition

If your potential users are already invested in other logging systems, LogTape provides adapters for popular libraries like winston and Pino. This allows LogTape-enabled libraries to integrate with existing logging infrastructure, routing their logs through whatever system applications are already using.

The existence of these adapters reveals an honest truth: LogTape isn't yet a widely-adopted standard in the JavaScript ecosystem. Most applications are still built around established logging libraries, and asking users to completely restructure their logging approach would be unrealistic. The adapters represent a practical compromise—they allow library authors to take advantage of LogTape's library-friendly design while respecting users' existing investments and preferences.

This approach reduces friction for adoption while still providing library authors with a modern, zero-dependency logging API. Perhaps over time, as more libraries adopt this pattern and more developers experience its benefits, the need for such adapters might diminish. But for now, they serve as a pragmatic bridge between LogTape's vision and the current reality of the ecosystem.

A choice worth considering

Ultimately, choosing LogTape for your library represents a particular philosophy about the relationship between libraries and applications. It's about providing capabilities while preserving choice, offering insights while avoiding imposition.

The traditional approaches—whether using debug packages, application-focused loggers, or custom solutions—each have their merits and have served the community well. LogTape simply offers another option: one designed specifically for the unique position libraries occupy in the JavaScript ecosystem.

For library authors, this approach might offer several practical benefits. Your library gets detailed logging for development, debugging, and user support, while your users retain complete autonomy over whether and how to use those capabilities.

The broader benefit might be a more cohesive logging experience across the JavaScript ecosystem—one where libraries can provide rich diagnostic information that integrates seamlessly with whatever logging strategy applications choose to employ.

In a world where every dependency decision has implications, LogTape offers an approach worth considering: a way to enhance your library's capabilities while respecting your users' preferences and existing choices.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Just shared my thoughts on library on Hacker News. Explores the fragmentation problem and dependency dilemmas from a library author's perspective. Would love to hear feedback from the / users.

https://news.ycombinator.com/item?id=44351881

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


Building a JavaScript library is a delicate balance. You want to provide useful functionality while being respectful of your users' choices and constraints. When it comes to logging—something many libraries need for debugging, monitoring, and user support—this balance becomes particularly challenging.

The JavaScript ecosystem has evolved various approaches to this challenge, each with its own trade-offs. LogTape offers a different path, one that's specifically designed with library authors in mind.

The current state of library logging

If you've built libraries before, you've probably encountered the logging dilemma. Your library would benefit from logging—perhaps to help users debug integration issues, trace internal state changes, or provide insights into performance bottlenecks. But how do you add this capability responsibly?

Currently, popular libraries handle this challenge in several ways:

The debug approach
Libraries like Express and Socket.IO use the lightweight debug package, which allows users to enable logging through environment variables (DEBUG=express:*). This works well but creates a separate logging system that doesn't integrate with users' existing logging infrastructure.
Custom logging systems
Libraries like Mongoose and Prisma have built their own logging mechanisms. Mongoose offers mongoose.set('debug', true) while Prisma uses its own logging configuration. These approaches work, but each library creates its own logging API that users must learn separately.
Application-focused libraries
winston, Pino, and Bunyan are powerful logging solutions, but they're primarily designed for applications rather than libraries. Using them in a library means imposing significant dependencies and potentially conflicting with users' existing logging choices.
No logging at all
Many library authors avoid the complexity entirely, leaving their libraries silent and making debugging more challenging for everyone involved.
Dependency injection
Some libraries adopt a more sophisticated approach by accepting a logger instance from the application through their configuration or constructor parameters. This maintains clean separation of concerns and allows libraries to use whatever logging system the application has chosen. However, this pattern requires more complex APIs and places additional burden on library users to understand and configure logging dependencies.

Each approach represents a reasonable solution to a genuine problem, but none fully addresses the core tension: how do you provide valuable diagnostic capabilities without imposing choices on your users?

The fragmentation problem

There's another challenge that emerges when libraries each solve logging in their own way: fragmentation. Consider a typical Node.js application that might use Express for the web framework, Socket.IO for real-time communication, Axios for HTTP requests, Mongoose for database access, and several other specialized libraries.

Each library potentially has its own logging approach:

  • Express uses DEBUG=express:*
  • Socket.IO uses DEBUG=socket.io:*
  • Mongoose uses mongoose.set('debug', true)
  • Axios might use axios-logger or similar packages
  • Redis clients have their own debug configurations
  • Authentication libraries often include their own logging mechanisms

From an application developer's perspective, this creates a management challenge. They must learn and configure multiple different logging systems, each with its own syntax, capabilities, and quirks. Logs are scattered across different outputs with inconsistent formats, making it difficult to get a unified view of what's happening in their application.

The lack of integration also means that powerful features like structured logging, log correlation, and centralized log management become much harder to implement consistently across all the libraries in use.

LogTape's approach

LogTape attempts to address these challenges with what might be called a “library-first design.” The core principle is simple but potentially powerful: if logging isn't configured, nothing happens. No output, no errors, no side effects—just complete transparency.

This approach allows you to add comprehensive logging to your library without any impact on users who don't want it. When a user imports your library and runs their code, LogTape's logging calls are essentially no-ops until someone explicitly configures logging. Users who want insights into your library's behavior can opt in; those who don't are completely unaffected.

More importantly, when users do choose to configure logging, all LogTape-enabled libraries can be managed through a single, unified configuration system. This means one consistent API, one log format, and one destination for all library logs while still allowing fine-grained control over what gets logged from which libraries.

Note

This approach isn't entirely novel—it draws inspiration from Python's standard logging library, which has successfully created a unified logging ecosystem. In Python, libraries like Requests, SQLAlchemy, and Django components all use the standard logging framework, allowing developers to configure all library logging through a single, consistent system. This has proven to be both practical and powerful, enabling rich diagnostic capabilities across the entire Python ecosystem while maintaining simplicity for application developers.

// In your library code - completely safe to include
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-awesome-lib", "database"]);

export function connectToDatabase(config) {
  logger.debug("Attempting database connection", { config });
  // ... your logic
  logger.info("Database connection established");
}

The dependency consideration

Modern JavaScript development involves careful consideration of dependencies. While popular logging libraries like winston and Pino are well-maintained and widely trusted, they do come with their own dependency trees. winston, for example, includes 17 dependencies, while Pino includes 1.

For library authors, this creates a consideration: every dependency you add becomes a dependency for your users, whether they want it or not. This isn't necessarily problematic (many excellent libraries have dependencies), but it does represent a choice you're making on behalf of your users.

LogTape takes a different approach with zero dependencies. This isn't just a philosophical choice—it has practical implications for your library's users. They won't see additional packages in their node_modules, won't need to worry about supply chain considerations for logging-related dependencies, and won't face potential version conflicts between your logging choice and theirs.

At just 5.3KB minified and gzipped, LogTape adds minimal weight to their bundles. The installation process becomes faster, the dependency tree stays cleaner, and security audits remain focused on the dependencies that directly serve your library's core functionality.

Breaking the compatibility chain

Here's a challenge that might be familiar: you want your library to support both ESM and CommonJS environments. Perhaps some of your users are working with legacy Node.js projects that rely on CommonJS, while others are using modern ESM setups or building for browsers.

The challenge becomes apparent when you have dependencies. While ESM modules can import CommonJS modules without issues, the reverse isn't true—CommonJS modules cannot require ESM-only packages (at least not until the experimental features in Node.js 22+ become stable). This creates an asymmetric compatibility constraint.

If your library depends on any ESM-only packages, your library effectively becomes ESM-only as well, since CommonJS environments won't be able to use it. This means that even one ESM-only dependency in your chain can prevent you from supporting CommonJS users.

LogTape supports both ESM and CommonJS completely, meaning it won't be the weak link that forces this limitation. Whether your users are working with legacy Node.js projects, cutting-edge ESM applications, or hybrid environments, LogTape adapts seamlessly to their setup.

More importantly, when LogTape provides native ESM support (rather than just being importable as CommonJS), it enables tree shaking in modern bundlers. Tree shaking allows bundlers to eliminate unused code during the build process, but it requires the static import/export structure that only ESM provides. While CommonJS modules can be imported into ESM projects, they're often treated as opaque blocks that can't be optimized, potentially including unused code in the final bundle.

For a logging library that aims to have minimal impact, this optimization capability can be meaningful, especially for applications where bundle size matters.

Universal runtime support

The JavaScript ecosystem spans an impressive range of runtime environments today. Your library might run in Node.js servers, Deno scripts, Bun applications, web browsers, or edge functions. LogTape works identically across all of these environments without requiring polyfills, compatibility layers, or runtime-specific code.

This universality means you can focus on your library's core functionality rather than worrying about whether your logging choice will work in every environment your users might encounter. Whether someone imports your library into a Cloudflare Worker, a Next.js application, or a Deno CLI tool, the logging behavior remains consistent and reliable.

Performance without compromise

One concern library authors often have about logging is performance impact. What if your users import your library into a high-performance application? What if they're running in a memory-constrained environment?

LogTape addresses this with remarkable efficiency when logging is disabled. The overhead of an unconfigured LogTape call is virtually zero—among the lowest of any logging solution available. This means you can add detailed logging throughout your library for development and debugging purposes without worrying about performance impact on users who don't enable it.

When logging is enabled, LogTape consistently outperforms other libraries, particularly for console output—often the most common logging destination during development.

Avoiding namespace collisions

Libraries sharing the same application can create logging chaos when they all output to the same namespace. LogTape's hierarchical category system elegantly solves this by encouraging libraries to use their own namespaces.

Your library might use categories like ["my-awesome-lib", "database"] or ["my-awesome-lib", "validation"], ensuring that your logs are clearly separated from other libraries and the main application. Users who configure LogTape can then control logging levels independently for different libraries and different components within those libraries.

Developer experience that just works

LogTape is built with TypeScript from the ground up, meaning your TypeScript-based library gets full type safety without additional dependencies or type packages. The API feels natural and modern, supporting both template literals and structured logging patterns that integrate well with contemporary JavaScript development practices.

// Template literal style - feels natural
logger.info`User ${userId} performed action ${action}`;

// Structured logging - great for monitoring
logger.info("User action completed", { userId, action, duration });

Practical integration

Actually using LogTape in your library is refreshingly straightforward. You simply import the logger, create appropriately namespaced categories, and log where it makes sense. No configuration, no setup, no complex initialization sequences.

import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-lib", "api"]);

export async function fetchUserData(userId) {
  logger.debug("Fetching user data", { userId });
  
  try {
    const response = await api.get(`/users/${userId}`);
    logger.info("User data retrieved successfully", { 
      userId, 
      status: response.status 
    });
    return response.data;
  } catch (error) {
    logger.error("Failed to fetch user data", { userId, error });
    throw error;
  }
}

For users who want to see these logs, configuration is equally simple:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: ["my-lib"], lowestLevel: "info", sinks: ["console"] }
  ]
});

Bridging the transition

If your potential users are already invested in other logging systems, LogTape provides adapters for popular libraries like winston and Pino. This allows LogTape-enabled libraries to integrate with existing logging infrastructure, routing their logs through whatever system applications are already using.

The existence of these adapters reveals an honest truth: LogTape isn't yet a widely-adopted standard in the JavaScript ecosystem. Most applications are still built around established logging libraries, and asking users to completely restructure their logging approach would be unrealistic. The adapters represent a practical compromise—they allow library authors to take advantage of LogTape's library-friendly design while respecting users' existing investments and preferences.

This approach reduces friction for adoption while still providing library authors with a modern, zero-dependency logging API. Perhaps over time, as more libraries adopt this pattern and more developers experience its benefits, the need for such adapters might diminish. But for now, they serve as a pragmatic bridge between LogTape's vision and the current reality of the ecosystem.

A choice worth considering

Ultimately, choosing LogTape for your library represents a particular philosophy about the relationship between libraries and applications. It's about providing capabilities while preserving choice, offering insights while avoiding imposition.

The traditional approaches—whether using debug packages, application-focused loggers, or custom solutions—each have their merits and have served the community well. LogTape simply offers another option: one designed specifically for the unique position libraries occupy in the JavaScript ecosystem.

For library authors, this approach might offer several practical benefits. Your library gets detailed logging for development, debugging, and user support, while your users retain complete autonomy over whether and how to use those capabilities.

The broader benefit might be a more cohesive logging experience across the JavaScript ecosystem—one where libraries can provide rich diagnostic information that integrates seamlessly with whatever logging strategy applications choose to employ.

In a world where every dependency decision has implications, LogTape offers an approach worth considering: a way to enhance your library's capabilities while respecting your users' preferences and existing choices.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


Building a JavaScript library is a delicate balance. You want to provide useful functionality while being respectful of your users' choices and constraints. When it comes to logging—something many libraries need for debugging, monitoring, and user support—this balance becomes particularly challenging.

The JavaScript ecosystem has evolved various approaches to this challenge, each with its own trade-offs. LogTape offers a different path, one that's specifically designed with library authors in mind.

The current state of library logging

If you've built libraries before, you've probably encountered the logging dilemma. Your library would benefit from logging—perhaps to help users debug integration issues, trace internal state changes, or provide insights into performance bottlenecks. But how do you add this capability responsibly?

Currently, popular libraries handle this challenge in several ways:

The debug approach
Libraries like Express and Socket.IO use the lightweight debug package, which allows users to enable logging through environment variables (DEBUG=express:*). This works well but creates a separate logging system that doesn't integrate with users' existing logging infrastructure.
Custom logging systems
Libraries like Mongoose and Prisma have built their own logging mechanisms. Mongoose offers mongoose.set('debug', true) while Prisma uses its own logging configuration. These approaches work, but each library creates its own logging API that users must learn separately.
Application-focused libraries
winston, Pino, and Bunyan are powerful logging solutions, but they're primarily designed for applications rather than libraries. Using them in a library means imposing significant dependencies and potentially conflicting with users' existing logging choices.
No logging at all
Many library authors avoid the complexity entirely, leaving their libraries silent and making debugging more challenging for everyone involved.
Dependency injection
Some libraries adopt a more sophisticated approach by accepting a logger instance from the application through their configuration or constructor parameters. This maintains clean separation of concerns and allows libraries to use whatever logging system the application has chosen. However, this pattern requires more complex APIs and places additional burden on library users to understand and configure logging dependencies.

Each approach represents a reasonable solution to a genuine problem, but none fully addresses the core tension: how do you provide valuable diagnostic capabilities without imposing choices on your users?

The fragmentation problem

There's another challenge that emerges when libraries each solve logging in their own way: fragmentation. Consider a typical Node.js application that might use Express for the web framework, Socket.IO for real-time communication, Axios for HTTP requests, Mongoose for database access, and several other specialized libraries.

Each library potentially has its own logging approach:

  • Express uses DEBUG=express:*
  • Socket.IO uses DEBUG=socket.io:*
  • Mongoose uses mongoose.set('debug', true)
  • Axios might use axios-logger or similar packages
  • Redis clients have their own debug configurations
  • Authentication libraries often include their own logging mechanisms

From an application developer's perspective, this creates a management challenge. They must learn and configure multiple different logging systems, each with its own syntax, capabilities, and quirks. Logs are scattered across different outputs with inconsistent formats, making it difficult to get a unified view of what's happening in their application.

The lack of integration also means that powerful features like structured logging, log correlation, and centralized log management become much harder to implement consistently across all the libraries in use.

LogTape's approach

LogTape attempts to address these challenges with what might be called a “library-first design.” The core principle is simple but potentially powerful: if logging isn't configured, nothing happens. No output, no errors, no side effects—just complete transparency.

This approach allows you to add comprehensive logging to your library without any impact on users who don't want it. When a user imports your library and runs their code, LogTape's logging calls are essentially no-ops until someone explicitly configures logging. Users who want insights into your library's behavior can opt in; those who don't are completely unaffected.

More importantly, when users do choose to configure logging, all LogTape-enabled libraries can be managed through a single, unified configuration system. This means one consistent API, one log format, and one destination for all library logs while still allowing fine-grained control over what gets logged from which libraries.

Note

This approach isn't entirely novel—it draws inspiration from Python's standard logging library, which has successfully created a unified logging ecosystem. In Python, libraries like Requests, SQLAlchemy, and Django components all use the standard logging framework, allowing developers to configure all library logging through a single, consistent system. This has proven to be both practical and powerful, enabling rich diagnostic capabilities across the entire Python ecosystem while maintaining simplicity for application developers.

// In your library code - completely safe to include
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-awesome-lib", "database"]);

export function connectToDatabase(config) {
  logger.debug("Attempting database connection", { config });
  // ... your logic
  logger.info("Database connection established");
}

The dependency consideration

Modern JavaScript development involves careful consideration of dependencies. While popular logging libraries like winston and Pino are well-maintained and widely trusted, they do come with their own dependency trees. winston, for example, includes 17 dependencies, while Pino includes 1.

For library authors, this creates a consideration: every dependency you add becomes a dependency for your users, whether they want it or not. This isn't necessarily problematic (many excellent libraries have dependencies), but it does represent a choice you're making on behalf of your users.

LogTape takes a different approach with zero dependencies. This isn't just a philosophical choice—it has practical implications for your library's users. They won't see additional packages in their node_modules, won't need to worry about supply chain considerations for logging-related dependencies, and won't face potential version conflicts between your logging choice and theirs.

At just 5.3KB minified and gzipped, LogTape adds minimal weight to their bundles. The installation process becomes faster, the dependency tree stays cleaner, and security audits remain focused on the dependencies that directly serve your library's core functionality.

Breaking the compatibility chain

Here's a challenge that might be familiar: you want your library to support both ESM and CommonJS environments. Perhaps some of your users are working with legacy Node.js projects that rely on CommonJS, while others are using modern ESM setups or building for browsers.

The challenge becomes apparent when you have dependencies. While ESM modules can import CommonJS modules without issues, the reverse isn't true—CommonJS modules cannot require ESM-only packages (at least not until the experimental features in Node.js 22+ become stable). This creates an asymmetric compatibility constraint.

If your library depends on any ESM-only packages, your library effectively becomes ESM-only as well, since CommonJS environments won't be able to use it. This means that even one ESM-only dependency in your chain can prevent you from supporting CommonJS users.

LogTape supports both ESM and CommonJS completely, meaning it won't be the weak link that forces this limitation. Whether your users are working with legacy Node.js projects, cutting-edge ESM applications, or hybrid environments, LogTape adapts seamlessly to their setup.

More importantly, when LogTape provides native ESM support (rather than just being importable as CommonJS), it enables tree shaking in modern bundlers. Tree shaking allows bundlers to eliminate unused code during the build process, but it requires the static import/export structure that only ESM provides. While CommonJS modules can be imported into ESM projects, they're often treated as opaque blocks that can't be optimized, potentially including unused code in the final bundle.

For a logging library that aims to have minimal impact, this optimization capability can be meaningful, especially for applications where bundle size matters.

Universal runtime support

The JavaScript ecosystem spans an impressive range of runtime environments today. Your library might run in Node.js servers, Deno scripts, Bun applications, web browsers, or edge functions. LogTape works identically across all of these environments without requiring polyfills, compatibility layers, or runtime-specific code.

This universality means you can focus on your library's core functionality rather than worrying about whether your logging choice will work in every environment your users might encounter. Whether someone imports your library into a Cloudflare Worker, a Next.js application, or a Deno CLI tool, the logging behavior remains consistent and reliable.

Performance without compromise

One concern library authors often have about logging is performance impact. What if your users import your library into a high-performance application? What if they're running in a memory-constrained environment?

LogTape addresses this with remarkable efficiency when logging is disabled. The overhead of an unconfigured LogTape call is virtually zero—among the lowest of any logging solution available. This means you can add detailed logging throughout your library for development and debugging purposes without worrying about performance impact on users who don't enable it.

When logging is enabled, LogTape consistently outperforms other libraries, particularly for console output—often the most common logging destination during development.

Avoiding namespace collisions

Libraries sharing the same application can create logging chaos when they all output to the same namespace. LogTape's hierarchical category system elegantly solves this by encouraging libraries to use their own namespaces.

Your library might use categories like ["my-awesome-lib", "database"] or ["my-awesome-lib", "validation"], ensuring that your logs are clearly separated from other libraries and the main application. Users who configure LogTape can then control logging levels independently for different libraries and different components within those libraries.

Developer experience that just works

LogTape is built with TypeScript from the ground up, meaning your TypeScript-based library gets full type safety without additional dependencies or type packages. The API feels natural and modern, supporting both template literals and structured logging patterns that integrate well with contemporary JavaScript development practices.

// Template literal style - feels natural
logger.info`User ${userId} performed action ${action}`;

// Structured logging - great for monitoring
logger.info("User action completed", { userId, action, duration });

Practical integration

Actually using LogTape in your library is refreshingly straightforward. You simply import the logger, create appropriately namespaced categories, and log where it makes sense. No configuration, no setup, no complex initialization sequences.

import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-lib", "api"]);

export async function fetchUserData(userId) {
  logger.debug("Fetching user data", { userId });
  
  try {
    const response = await api.get(`/users/${userId}`);
    logger.info("User data retrieved successfully", { 
      userId, 
      status: response.status 
    });
    return response.data;
  } catch (error) {
    logger.error("Failed to fetch user data", { userId, error });
    throw error;
  }
}

For users who want to see these logs, configuration is equally simple:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: ["my-lib"], lowestLevel: "info", sinks: ["console"] }
  ]
});

Bridging the transition

If your potential users are already invested in other logging systems, LogTape provides adapters for popular libraries like winston and Pino. This allows LogTape-enabled libraries to integrate with existing logging infrastructure, routing their logs through whatever system applications are already using.

The existence of these adapters reveals an honest truth: LogTape isn't yet a widely-adopted standard in the JavaScript ecosystem. Most applications are still built around established logging libraries, and asking users to completely restructure their logging approach would be unrealistic. The adapters represent a practical compromise—they allow library authors to take advantage of LogTape's library-friendly design while respecting users' existing investments and preferences.

This approach reduces friction for adoption while still providing library authors with a modern, zero-dependency logging API. Perhaps over time, as more libraries adopt this pattern and more developers experience its benefits, the need for such adapters might diminish. But for now, they serve as a pragmatic bridge between LogTape's vision and the current reality of the ecosystem.

A choice worth considering

Ultimately, choosing LogTape for your library represents a particular philosophy about the relationship between libraries and applications. It's about providing capabilities while preserving choice, offering insights while avoiding imposition.

The traditional approaches—whether using debug packages, application-focused loggers, or custom solutions—each have their merits and have served the community well. LogTape simply offers another option: one designed specifically for the unique position libraries occupy in the JavaScript ecosystem.

For library authors, this approach might offer several practical benefits. Your library gets detailed logging for development, debugging, and user support, while your users retain complete autonomy over whether and how to use those capabilities.

The broader benefit might be a more cohesive logging experience across the JavaScript ecosystem—one where libraries can provide rich diagnostic information that integrates seamlessly with whatever logging strategy applications choose to employ.

In a world where every dependency decision has implications, LogTape offers an approach worth considering: a way to enhance your library's capabilities while respecting your users' preferences and existing choices.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


Building a JavaScript library is a delicate balance. You want to provide useful functionality while being respectful of your users' choices and constraints. When it comes to logging—something many libraries need for debugging, monitoring, and user support—this balance becomes particularly challenging.

The JavaScript ecosystem has evolved various approaches to this challenge, each with its own trade-offs. LogTape offers a different path, one that's specifically designed with library authors in mind.

The current state of library logging

If you've built libraries before, you've probably encountered the logging dilemma. Your library would benefit from logging—perhaps to help users debug integration issues, trace internal state changes, or provide insights into performance bottlenecks. But how do you add this capability responsibly?

Currently, popular libraries handle this challenge in several ways:

The debug approach
Libraries like Express and Socket.IO use the lightweight debug package, which allows users to enable logging through environment variables (DEBUG=express:*). This works well but creates a separate logging system that doesn't integrate with users' existing logging infrastructure.
Custom logging systems
Libraries like Mongoose and Prisma have built their own logging mechanisms. Mongoose offers mongoose.set('debug', true) while Prisma uses its own logging configuration. These approaches work, but each library creates its own logging API that users must learn separately.
Application-focused libraries
winston, Pino, and Bunyan are powerful logging solutions, but they're primarily designed for applications rather than libraries. Using them in a library means imposing significant dependencies and potentially conflicting with users' existing logging choices.
No logging at all
Many library authors avoid the complexity entirely, leaving their libraries silent and making debugging more challenging for everyone involved.
Dependency injection
Some libraries adopt a more sophisticated approach by accepting a logger instance from the application through their configuration or constructor parameters. This maintains clean separation of concerns and allows libraries to use whatever logging system the application has chosen. However, this pattern requires more complex APIs and places additional burden on library users to understand and configure logging dependencies.

Each approach represents a reasonable solution to a genuine problem, but none fully addresses the core tension: how do you provide valuable diagnostic capabilities without imposing choices on your users?

The fragmentation problem

There's another challenge that emerges when libraries each solve logging in their own way: fragmentation. Consider a typical Node.js application that might use Express for the web framework, Socket.IO for real-time communication, Axios for HTTP requests, Mongoose for database access, and several other specialized libraries.

Each library potentially has its own logging approach:

  • Express uses DEBUG=express:*
  • Socket.IO uses DEBUG=socket.io:*
  • Mongoose uses mongoose.set('debug', true)
  • Axios might use axios-logger or similar packages
  • Redis clients have their own debug configurations
  • Authentication libraries often include their own logging mechanisms

From an application developer's perspective, this creates a management challenge. They must learn and configure multiple different logging systems, each with its own syntax, capabilities, and quirks. Logs are scattered across different outputs with inconsistent formats, making it difficult to get a unified view of what's happening in their application.

The lack of integration also means that powerful features like structured logging, log correlation, and centralized log management become much harder to implement consistently across all the libraries in use.

LogTape's approach

LogTape attempts to address these challenges with what might be called a “library-first design.” The core principle is simple but potentially powerful: if logging isn't configured, nothing happens. No output, no errors, no side effects—just complete transparency.

This approach allows you to add comprehensive logging to your library without any impact on users who don't want it. When a user imports your library and runs their code, LogTape's logging calls are essentially no-ops until someone explicitly configures logging. Users who want insights into your library's behavior can opt in; those who don't are completely unaffected.

More importantly, when users do choose to configure logging, all LogTape-enabled libraries can be managed through a single, unified configuration system. This means one consistent API, one log format, and one destination for all library logs while still allowing fine-grained control over what gets logged from which libraries.

Note

This approach isn't entirely novel—it draws inspiration from Python's standard logging library, which has successfully created a unified logging ecosystem. In Python, libraries like Requests, SQLAlchemy, and Django components all use the standard logging framework, allowing developers to configure all library logging through a single, consistent system. This has proven to be both practical and powerful, enabling rich diagnostic capabilities across the entire Python ecosystem while maintaining simplicity for application developers.

// In your library code - completely safe to include
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-awesome-lib", "database"]);

export function connectToDatabase(config) {
  logger.debug("Attempting database connection", { config });
  // ... your logic
  logger.info("Database connection established");
}

The dependency consideration

Modern JavaScript development involves careful consideration of dependencies. While popular logging libraries like winston and Pino are well-maintained and widely trusted, they do come with their own dependency trees. winston, for example, includes 17 dependencies, while Pino includes 1.

For library authors, this creates a consideration: every dependency you add becomes a dependency for your users, whether they want it or not. This isn't necessarily problematic (many excellent libraries have dependencies), but it does represent a choice you're making on behalf of your users.

LogTape takes a different approach with zero dependencies. This isn't just a philosophical choice—it has practical implications for your library's users. They won't see additional packages in their node_modules, won't need to worry about supply chain considerations for logging-related dependencies, and won't face potential version conflicts between your logging choice and theirs.

At just 5.3KB minified and gzipped, LogTape adds minimal weight to their bundles. The installation process becomes faster, the dependency tree stays cleaner, and security audits remain focused on the dependencies that directly serve your library's core functionality.

Breaking the compatibility chain

Here's a challenge that might be familiar: you want your library to support both ESM and CommonJS environments. Perhaps some of your users are working with legacy Node.js projects that rely on CommonJS, while others are using modern ESM setups or building for browsers.

The challenge becomes apparent when you have dependencies. While ESM modules can import CommonJS modules without issues, the reverse isn't true—CommonJS modules cannot require ESM-only packages (at least not until the experimental features in Node.js 22+ become stable). This creates an asymmetric compatibility constraint.

If your library depends on any ESM-only packages, your library effectively becomes ESM-only as well, since CommonJS environments won't be able to use it. This means that even one ESM-only dependency in your chain can prevent you from supporting CommonJS users.

LogTape supports both ESM and CommonJS completely, meaning it won't be the weak link that forces this limitation. Whether your users are working with legacy Node.js projects, cutting-edge ESM applications, or hybrid environments, LogTape adapts seamlessly to their setup.

More importantly, when LogTape provides native ESM support (rather than just being importable as CommonJS), it enables tree shaking in modern bundlers. Tree shaking allows bundlers to eliminate unused code during the build process, but it requires the static import/export structure that only ESM provides. While CommonJS modules can be imported into ESM projects, they're often treated as opaque blocks that can't be optimized, potentially including unused code in the final bundle.

For a logging library that aims to have minimal impact, this optimization capability can be meaningful, especially for applications where bundle size matters.

Universal runtime support

The JavaScript ecosystem spans an impressive range of runtime environments today. Your library might run in Node.js servers, Deno scripts, Bun applications, web browsers, or edge functions. LogTape works identically across all of these environments without requiring polyfills, compatibility layers, or runtime-specific code.

This universality means you can focus on your library's core functionality rather than worrying about whether your logging choice will work in every environment your users might encounter. Whether someone imports your library into a Cloudflare Worker, a Next.js application, or a Deno CLI tool, the logging behavior remains consistent and reliable.

Performance without compromise

One concern library authors often have about logging is performance impact. What if your users import your library into a high-performance application? What if they're running in a memory-constrained environment?

LogTape addresses this with remarkable efficiency when logging is disabled. The overhead of an unconfigured LogTape call is virtually zero—among the lowest of any logging solution available. This means you can add detailed logging throughout your library for development and debugging purposes without worrying about performance impact on users who don't enable it.

When logging is enabled, LogTape consistently outperforms other libraries, particularly for console output—often the most common logging destination during development.

Avoiding namespace collisions

Libraries sharing the same application can create logging chaos when they all output to the same namespace. LogTape's hierarchical category system elegantly solves this by encouraging libraries to use their own namespaces.

Your library might use categories like ["my-awesome-lib", "database"] or ["my-awesome-lib", "validation"], ensuring that your logs are clearly separated from other libraries and the main application. Users who configure LogTape can then control logging levels independently for different libraries and different components within those libraries.

Developer experience that just works

LogTape is built with TypeScript from the ground up, meaning your TypeScript-based library gets full type safety without additional dependencies or type packages. The API feels natural and modern, supporting both template literals and structured logging patterns that integrate well with contemporary JavaScript development practices.

// Template literal style - feels natural
logger.info`User ${userId} performed action ${action}`;

// Structured logging - great for monitoring
logger.info("User action completed", { userId, action, duration });

Practical integration

Actually using LogTape in your library is refreshingly straightforward. You simply import the logger, create appropriately namespaced categories, and log where it makes sense. No configuration, no setup, no complex initialization sequences.

import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-lib", "api"]);

export async function fetchUserData(userId) {
  logger.debug("Fetching user data", { userId });
  
  try {
    const response = await api.get(`/users/${userId}`);
    logger.info("User data retrieved successfully", { 
      userId, 
      status: response.status 
    });
    return response.data;
  } catch (error) {
    logger.error("Failed to fetch user data", { userId, error });
    throw error;
  }
}

For users who want to see these logs, configuration is equally simple:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: ["my-lib"], lowestLevel: "info", sinks: ["console"] }
  ]
});

Bridging the transition

If your potential users are already invested in other logging systems, LogTape provides adapters for popular libraries like winston and Pino. This allows LogTape-enabled libraries to integrate with existing logging infrastructure, routing their logs through whatever system applications are already using.

The existence of these adapters reveals an honest truth: LogTape isn't yet a widely-adopted standard in the JavaScript ecosystem. Most applications are still built around established logging libraries, and asking users to completely restructure their logging approach would be unrealistic. The adapters represent a practical compromise—they allow library authors to take advantage of LogTape's library-friendly design while respecting users' existing investments and preferences.

This approach reduces friction for adoption while still providing library authors with a modern, zero-dependency logging API. Perhaps over time, as more libraries adopt this pattern and more developers experience its benefits, the need for such adapters might diminish. But for now, they serve as a pragmatic bridge between LogTape's vision and the current reality of the ecosystem.

A choice worth considering

Ultimately, choosing LogTape for your library represents a particular philosophy about the relationship between libraries and applications. It's about providing capabilities while preserving choice, offering insights while avoiding imposition.

The traditional approaches—whether using debug packages, application-focused loggers, or custom solutions—each have their merits and have served the community well. LogTape simply offers another option: one designed specifically for the unique position libraries occupy in the JavaScript ecosystem.

For library authors, this approach might offer several practical benefits. Your library gets detailed logging for development, debugging, and user support, while your users retain complete autonomy over whether and how to use those capabilities.

The broader benefit might be a more cohesive logging experience across the JavaScript ecosystem—one where libraries can provide rich diagnostic information that integrates seamlessly with whatever logging strategy applications choose to employ.

In a world where every dependency decision has implications, LogTape offers an approach worth considering: a way to enhance your library's capabilities while respecting your users' preferences and existing choices.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


What is LogTape?

LogTape is a logging library designed specifically for the modern JavaScript ecosystem. It stands out with its zero-dependency architecture, universal runtime support across Node.js, Deno, Bun, browsers, and edge functions, and a library-first design philosophy that allows library authors to add logging without imposing any burden on their users. When LogTape isn't configured, logging calls have virtually no performance impact, making it the only truly unobtrusive logging solution available.

For a comprehensive overview of LogTape's capabilities and philosophy, see our introduction guide.

Milestone achievement

We're excited to announce LogTape 1.0.0, marking a significant milestone in the library's development. This release represents our commitment to API stability and long-term support. The 1.0.0 designation signals that LogTape's core APIs are now stable and ready for production use, with any future breaking changes following semantic versioning principles.

This milestone builds upon months of refinement, community feedback, and real-world usage, establishing LogTape as a mature and reliable logging solution for JavaScript applications and libraries.

Major new features

High-performance logging infrastructure

LogTape 1.0.0 introduces several performance-oriented features designed for high-throughput production environments. The new non-blocking sink option allows console, stream, and file sinks to buffer log records and flush them asynchronously, preventing logging operations from blocking your application's main thread.

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: {
    console: getConsoleSink({ 
      nonBlocking: {
        bufferSize: 1000,
        flushInterval: 50
      }
    })
  },
  // ...
});

The new fromAsyncSink() function provides a clean way to integrate asynchronous logging operations while maintaining LogTape's synchronous sink interface. This enables scenarios like sending logs to remote servers or databases without blocking your application.

import { fromAsyncSink } from "@logtape/logtape";

const webhookSink = fromAsyncSink(async (record) => {
  await fetch("https://logs.example.com", {
    method: "POST",
    body: JSON.stringify(record)
  });
});

For file operations specifically, the new getStreamFileSink() function in the @logtape/file package leverages Node.js PassThrough streams to deliver optimal I/O performance with automatic backpressure management.

New sink integrations

This release significantly expands LogTape's integration capabilities with two major new sink packages. The @logtape/cloudwatch-logs package enables direct integration with AWS CloudWatch Logs, featuring intelligent batching, exponential backoff retry strategies, and support for structured logging through JSON Lines formatting.

import { getCloudWatchLogsSink } from "@logtape/cloudwatch-logs";

const sink = getCloudWatchLogsSink({
  logGroupName: "/aws/lambda/my-function",
  logStreamName: "my-stream",
  region: "us-east-1"
});

The @logtape/windows-eventlog package brings native Windows Event Log support with cross-runtime compatibility across Deno, Node.js, and Bun. This integration uses runtime-optimized FFI implementations for maximum performance while maintaining proper error handling and resource cleanup.

Beautiful development experience

The new @logtape/pretty package transforms console logging into a visually appealing experience designed specifically for local development. Inspired by Signale, it features colorful emojis for each log level, smart category truncation that preserves important context, and perfect column alignment that makes logs easy to scan.

LogTape pretty formatter output showing colorful console logs with emojis and perfect alignment

import { configure, getConsoleSink } from "@logtape/logtape";
import { prettyFormatter } from "@logtape/pretty";

await configure({
  sinks: {
    console: getConsoleSink({ formatter: prettyFormatter })
  },
  // ...
});

As shown above, the pretty formatter supports true color terminals with rich color schemes, configurable icons, and intelligent word wrapping that maintains visual consistency even for long messages.

Ecosystem integration

Perhaps most significantly, LogTape 1.0.0 introduces adapter packages that bridge the gap between LogTape's library-friendly design and existing logging infrastructure. The @logtape/adaptor-winston and @logtape/adaptor-pino packages allow applications using these established logging libraries to seamlessly integrate LogTape-enabled libraries without changing their existing setup.

// Quick setup with winston
import "@logtape/adaptor-winston/install";
// Or with custom configuration
import { install } from "@logtape/adaptor-winston";
import winston from "winston";

const logger = winston.createLogger({/* your config */});
install(logger);

These adapters preserve LogTape's structured logging capabilities while routing everything through your preferred logging system, making adoption of LogTape-enabled libraries frictionless for existing applications.

Developer experience enhancements

This release includes several quality-of-life improvements for developers working with LogTape. The new getLogLevels() function provides programmatic access to all available log levels, while the LogMethod type offers better type inference for logging methods.

Browser compatibility has been improved, particularly for the @logtape/otel package, which previously had issues in browser environments due to Node.js-specific imports. The package now works seamlessly across all JavaScript runtimes without throwing module resolution errors.

Breaking changes and migration guide

LogTape 1.0.0 includes one notable breaking change: the removal of the deprecated LoggerConfig.level property. This property was deprecated in version 0.8.0 in favor of the more descriptive LoggerConfig.lowestLevel property.

If your configuration still uses the old property, simply rename it:

// Before (deprecated)
{ category: ["app"], level: "info", sinks: ["console"] }
// After
{ category: ["app"], lowestLevel: "info", sinks: ["console"] }

For more complex filtering requirements, consider using the LoggerConfig.filters option instead, which provides more flexibility and supports inheritance from parent loggers.

Complete package ecosystem

LogTape 1.0.0 represents the culmination of a comprehensive package ecosystem, now consisting of 11 specialized packages that address different aspects of logging infrastructure. This modular approach allows you to install only the packages you need, keeping your dependency footprint minimal while accessing powerful logging capabilities when required.

Package JSR npm Description
@logtape/logtape JSR npm Core logging functionality
@logtape/adaptor-pino JSR npm Pino adapter
@logtape/adaptor-winston JSR npm winston adapter
@logtape/cloudwatch-logs JSR npm AWS CloudWatch Logs sink
@logtape/file JSR npm File sinks
@logtape/otel JSR npm OpenTelemetry sink
@logtape/pretty JSR npm Beautiful text formatter
@logtape/redaction JSR npm Data redaction
@logtape/sentry JSR npm Sentry sink
@logtape/syslog JSR npm Syslog sink
@logtape/windows-eventlog JSR npm Windows Event Log sink

Getting started

Whether you're new to LogTape or upgrading from a previous version, getting started with 1.0.0 is straightforward. For new projects, begin with a simple configuration and gradually add the packages and features you need:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: "my-app", lowestLevel: "info", sinks: ["console"] }
  ]
});

Existing applications using winston or Pino can immediately benefit from LogTape-enabled libraries by installing the appropriate adapter. For comprehensive migration guidance and detailed feature documentation, visit our documentation site.

The 1.0.0 release represents not just a version number, but a commitment to the stability and maturity that production applications require. We're excited to see what you'll build with LogTape.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


What is LogTape?

LogTape is a logging library designed specifically for the modern JavaScript ecosystem. It stands out with its zero-dependency architecture, universal runtime support across Node.js, Deno, Bun, browsers, and edge functions, and a library-first design philosophy that allows library authors to add logging without imposing any burden on their users. When LogTape isn't configured, logging calls have virtually no performance impact, making it the only truly unobtrusive logging solution available.

For a comprehensive overview of LogTape's capabilities and philosophy, see our introduction guide.

Milestone achievement

We're excited to announce LogTape 1.0.0, marking a significant milestone in the library's development. This release represents our commitment to API stability and long-term support. The 1.0.0 designation signals that LogTape's core APIs are now stable and ready for production use, with any future breaking changes following semantic versioning principles.

This milestone builds upon months of refinement, community feedback, and real-world usage, establishing LogTape as a mature and reliable logging solution for JavaScript applications and libraries.

Major new features

High-performance logging infrastructure

LogTape 1.0.0 introduces several performance-oriented features designed for high-throughput production environments. The new non-blocking sink option allows console, stream, and file sinks to buffer log records and flush them asynchronously, preventing logging operations from blocking your application's main thread.

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: {
    console: getConsoleSink({ 
      nonBlocking: {
        bufferSize: 1000,
        flushInterval: 50
      }
    })
  },
  // ...
});

The new fromAsyncSink() function provides a clean way to integrate asynchronous logging operations while maintaining LogTape's synchronous sink interface. This enables scenarios like sending logs to remote servers or databases without blocking your application.

import { fromAsyncSink } from "@logtape/logtape";

const webhookSink = fromAsyncSink(async (record) => {
  await fetch("https://logs.example.com", {
    method: "POST",
    body: JSON.stringify(record)
  });
});

For file operations specifically, the new getStreamFileSink() function in the @logtape/file package leverages Node.js PassThrough streams to deliver optimal I/O performance with automatic backpressure management.

New sink integrations

This release significantly expands LogTape's integration capabilities with two major new sink packages. The @logtape/cloudwatch-logs package enables direct integration with AWS CloudWatch Logs, featuring intelligent batching, exponential backoff retry strategies, and support for structured logging through JSON Lines formatting.

import { getCloudWatchLogsSink } from "@logtape/cloudwatch-logs";

const sink = getCloudWatchLogsSink({
  logGroupName: "/aws/lambda/my-function",
  logStreamName: "my-stream",
  region: "us-east-1"
});

The @logtape/windows-eventlog package brings native Windows Event Log support with cross-runtime compatibility across Deno, Node.js, and Bun. This integration uses runtime-optimized FFI implementations for maximum performance while maintaining proper error handling and resource cleanup.

Beautiful development experience

The new @logtape/pretty package transforms console logging into a visually appealing experience designed specifically for local development. Inspired by Signale, it features colorful emojis for each log level, smart category truncation that preserves important context, and perfect column alignment that makes logs easy to scan.

LogTape pretty formatter output showing colorful console logs with emojis and perfect alignment

import { configure, getConsoleSink } from "@logtape/logtape";
import { prettyFormatter } from "@logtape/pretty";

await configure({
  sinks: {
    console: getConsoleSink({ formatter: prettyFormatter })
  },
  // ...
});

As shown above, the pretty formatter supports true color terminals with rich color schemes, configurable icons, and intelligent word wrapping that maintains visual consistency even for long messages.

Ecosystem integration

Perhaps most significantly, LogTape 1.0.0 introduces adapter packages that bridge the gap between LogTape's library-friendly design and existing logging infrastructure. The @logtape/adaptor-winston and @logtape/adaptor-pino packages allow applications using these established logging libraries to seamlessly integrate LogTape-enabled libraries without changing their existing setup.

// Quick setup with winston
import "@logtape/adaptor-winston/install";
// Or with custom configuration
import { install } from "@logtape/adaptor-winston";
import winston from "winston";

const logger = winston.createLogger({/* your config */});
install(logger);

These adapters preserve LogTape's structured logging capabilities while routing everything through your preferred logging system, making adoption of LogTape-enabled libraries frictionless for existing applications.

Developer experience enhancements

This release includes several quality-of-life improvements for developers working with LogTape. The new getLogLevels() function provides programmatic access to all available log levels, while the LogMethod type offers better type inference for logging methods.

Browser compatibility has been improved, particularly for the @logtape/otel package, which previously had issues in browser environments due to Node.js-specific imports. The package now works seamlessly across all JavaScript runtimes without throwing module resolution errors.

Breaking changes and migration guide

LogTape 1.0.0 includes one notable breaking change: the removal of the deprecated LoggerConfig.level property. This property was deprecated in version 0.8.0 in favor of the more descriptive LoggerConfig.lowestLevel property.

If your configuration still uses the old property, simply rename it:

// Before (deprecated)
{ category: ["app"], level: "info", sinks: ["console"] }
// After
{ category: ["app"], lowestLevel: "info", sinks: ["console"] }

For more complex filtering requirements, consider using the LoggerConfig.filters option instead, which provides more flexibility and supports inheritance from parent loggers.

Complete package ecosystem

LogTape 1.0.0 represents the culmination of a comprehensive package ecosystem, now consisting of 11 specialized packages that address different aspects of logging infrastructure. This modular approach allows you to install only the packages you need, keeping your dependency footprint minimal while accessing powerful logging capabilities when required.

Package JSR npm Description
@logtape/logtape JSR npm Core logging functionality
@logtape/adaptor-pino JSR npm Pino adapter
@logtape/adaptor-winston JSR npm winston adapter
@logtape/cloudwatch-logs JSR npm AWS CloudWatch Logs sink
@logtape/file JSR npm File sinks
@logtape/otel JSR npm OpenTelemetry sink
@logtape/pretty JSR npm Beautiful text formatter
@logtape/redaction JSR npm Data redaction
@logtape/sentry JSR npm Sentry sink
@logtape/syslog JSR npm Syslog sink
@logtape/windows-eventlog JSR npm Windows Event Log sink

Getting started

Whether you're new to LogTape or upgrading from a previous version, getting started with 1.0.0 is straightforward. For new projects, begin with a simple configuration and gradually add the packages and features you need:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: "my-app", lowestLevel: "info", sinks: ["console"] }
  ]
});

Existing applications using winston or Pino can immediately benefit from LogTape-enabled libraries by installing the appropriate adapter. For comprehensive migration guidance and detailed feature documentation, visit our documentation site.

The 1.0.0 release represents not just a version number, but a commitment to the stability and maturity that production applications require. We're excited to see what you'll build with LogTape.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


What is LogTape?

LogTape is a logging library designed specifically for the modern JavaScript ecosystem. It stands out with its zero-dependency architecture, universal runtime support across Node.js, Deno, Bun, browsers, and edge functions, and a library-first design philosophy that allows library authors to add logging without imposing any burden on their users. When LogTape isn't configured, logging calls have virtually no performance impact, making it the only truly unobtrusive logging solution available.

For a comprehensive overview of LogTape's capabilities and philosophy, see our introduction guide.

Milestone achievement

We're excited to announce LogTape 1.0.0, marking a significant milestone in the library's development. This release represents our commitment to API stability and long-term support. The 1.0.0 designation signals that LogTape's core APIs are now stable and ready for production use, with any future breaking changes following semantic versioning principles.

This milestone builds upon months of refinement, community feedback, and real-world usage, establishing LogTape as a mature and reliable logging solution for JavaScript applications and libraries.

Major new features

High-performance logging infrastructure

LogTape 1.0.0 introduces several performance-oriented features designed for high-throughput production environments. The new non-blocking sink option allows console, stream, and file sinks to buffer log records and flush them asynchronously, preventing logging operations from blocking your application's main thread.

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: {
    console: getConsoleSink({ 
      nonBlocking: {
        bufferSize: 1000,
        flushInterval: 50
      }
    })
  },
  // ...
});

The new fromAsyncSink() function provides a clean way to integrate asynchronous logging operations while maintaining LogTape's synchronous sink interface. This enables scenarios like sending logs to remote servers or databases without blocking your application.

import { fromAsyncSink } from "@logtape/logtape";

const webhookSink = fromAsyncSink(async (record) => {
  await fetch("https://logs.example.com", {
    method: "POST",
    body: JSON.stringify(record)
  });
});

For file operations specifically, the new getStreamFileSink() function in the @logtape/file package leverages Node.js PassThrough streams to deliver optimal I/O performance with automatic backpressure management.

New sink integrations

This release significantly expands LogTape's integration capabilities with two major new sink packages. The @logtape/cloudwatch-logs package enables direct integration with AWS CloudWatch Logs, featuring intelligent batching, exponential backoff retry strategies, and support for structured logging through JSON Lines formatting.

import { getCloudWatchLogsSink } from "@logtape/cloudwatch-logs";

const sink = getCloudWatchLogsSink({
  logGroupName: "/aws/lambda/my-function",
  logStreamName: "my-stream",
  region: "us-east-1"
});

The @logtape/windows-eventlog package brings native Windows Event Log support with cross-runtime compatibility across Deno, Node.js, and Bun. This integration uses runtime-optimized FFI implementations for maximum performance while maintaining proper error handling and resource cleanup.

Beautiful development experience

The new @logtape/pretty package transforms console logging into a visually appealing experience designed specifically for local development. Inspired by Signale, it features colorful emojis for each log level, smart category truncation that preserves important context, and perfect column alignment that makes logs easy to scan.

LogTape pretty formatter output showing colorful console logs with emojis and perfect alignment

import { configure, getConsoleSink } from "@logtape/logtape";
import { prettyFormatter } from "@logtape/pretty";

await configure({
  sinks: {
    console: getConsoleSink({ formatter: prettyFormatter })
  },
  // ...
});

As shown above, the pretty formatter supports true color terminals with rich color schemes, configurable icons, and intelligent word wrapping that maintains visual consistency even for long messages.

Ecosystem integration

Perhaps most significantly, LogTape 1.0.0 introduces adapter packages that bridge the gap between LogTape's library-friendly design and existing logging infrastructure. The @logtape/adaptor-winston and @logtape/adaptor-pino packages allow applications using these established logging libraries to seamlessly integrate LogTape-enabled libraries without changing their existing setup.

// Quick setup with winston
import "@logtape/adaptor-winston/install";
// Or with custom configuration
import { install } from "@logtape/adaptor-winston";
import winston from "winston";

const logger = winston.createLogger({/* your config */});
install(logger);

These adapters preserve LogTape's structured logging capabilities while routing everything through your preferred logging system, making adoption of LogTape-enabled libraries frictionless for existing applications.

Developer experience enhancements

This release includes several quality-of-life improvements for developers working with LogTape. The new getLogLevels() function provides programmatic access to all available log levels, while the LogMethod type offers better type inference for logging methods.

Browser compatibility has been improved, particularly for the @logtape/otel package, which previously had issues in browser environments due to Node.js-specific imports. The package now works seamlessly across all JavaScript runtimes without throwing module resolution errors.

Breaking changes and migration guide

LogTape 1.0.0 includes one notable breaking change: the removal of the deprecated LoggerConfig.level property. This property was deprecated in version 0.8.0 in favor of the more descriptive LoggerConfig.lowestLevel property.

If your configuration still uses the old property, simply rename it:

// Before (deprecated)
{ category: ["app"], level: "info", sinks: ["console"] }
// After
{ category: ["app"], lowestLevel: "info", sinks: ["console"] }

For more complex filtering requirements, consider using the LoggerConfig.filters option instead, which provides more flexibility and supports inheritance from parent loggers.

Complete package ecosystem

LogTape 1.0.0 represents the culmination of a comprehensive package ecosystem, now consisting of 11 specialized packages that address different aspects of logging infrastructure. This modular approach allows you to install only the packages you need, keeping your dependency footprint minimal while accessing powerful logging capabilities when required.

Package JSR npm Description
@logtape/logtape JSR npm Core logging functionality
@logtape/adaptor-pino JSR npm Pino adapter
@logtape/adaptor-winston JSR npm winston adapter
@logtape/cloudwatch-logs JSR npm AWS CloudWatch Logs sink
@logtape/file JSR npm File sinks
@logtape/otel JSR npm OpenTelemetry sink
@logtape/pretty JSR npm Beautiful text formatter
@logtape/redaction JSR npm Data redaction
@logtape/sentry JSR npm Sentry sink
@logtape/syslog JSR npm Syslog sink
@logtape/windows-eventlog JSR npm Windows Event Log sink

Getting started

Whether you're new to LogTape or upgrading from a previous version, getting started with 1.0.0 is straightforward. For new projects, begin with a simple configuration and gradually add the packages and features you need:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: "my-app", lowestLevel: "info", sinks: ["console"] }
  ]
});

Existing applications using winston or Pino can immediately benefit from LogTape-enabled libraries by installing the appropriate adapter. For comprehensive migration guidance and detailed feature documentation, visit our documentation site.

The 1.0.0 release represents not just a version number, but a commitment to the stability and maturity that production applications require. We're excited to see what you'll build with LogTape.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


What is LogTape?

LogTape is a logging library designed specifically for the modern JavaScript ecosystem. It stands out with its zero-dependency architecture, universal runtime support across Node.js, Deno, Bun, browsers, and edge functions, and a library-first design philosophy that allows library authors to add logging without imposing any burden on their users. When LogTape isn't configured, logging calls have virtually no performance impact, making it the only truly unobtrusive logging solution available.

For a comprehensive overview of LogTape's capabilities and philosophy, see our introduction guide.

Milestone achievement

We're excited to announce LogTape 1.0.0, marking a significant milestone in the library's development. This release represents our commitment to API stability and long-term support. The 1.0.0 designation signals that LogTape's core APIs are now stable and ready for production use, with any future breaking changes following semantic versioning principles.

This milestone builds upon months of refinement, community feedback, and real-world usage, establishing LogTape as a mature and reliable logging solution for JavaScript applications and libraries.

Major new features

High-performance logging infrastructure

LogTape 1.0.0 introduces several performance-oriented features designed for high-throughput production environments. The new non-blocking sink option allows console, stream, and file sinks to buffer log records and flush them asynchronously, preventing logging operations from blocking your application's main thread.

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: {
    console: getConsoleSink({ 
      nonBlocking: {
        bufferSize: 1000,
        flushInterval: 50
      }
    })
  },
  // ...
});

The new fromAsyncSink() function provides a clean way to integrate asynchronous logging operations while maintaining LogTape's synchronous sink interface. This enables scenarios like sending logs to remote servers or databases without blocking your application.

import { fromAsyncSink } from "@logtape/logtape";

const webhookSink = fromAsyncSink(async (record) => {
  await fetch("https://logs.example.com", {
    method: "POST",
    body: JSON.stringify(record)
  });
});

For file operations specifically, the new getStreamFileSink() function in the @logtape/file package leverages Node.js PassThrough streams to deliver optimal I/O performance with automatic backpressure management.

New sink integrations

This release significantly expands LogTape's integration capabilities with two major new sink packages. The @logtape/cloudwatch-logs package enables direct integration with AWS CloudWatch Logs, featuring intelligent batching, exponential backoff retry strategies, and support for structured logging through JSON Lines formatting.

import { getCloudWatchLogsSink } from "@logtape/cloudwatch-logs";

const sink = getCloudWatchLogsSink({
  logGroupName: "/aws/lambda/my-function",
  logStreamName: "my-stream",
  region: "us-east-1"
});

The @logtape/windows-eventlog package brings native Windows Event Log support with cross-runtime compatibility across Deno, Node.js, and Bun. This integration uses runtime-optimized FFI implementations for maximum performance while maintaining proper error handling and resource cleanup.

Beautiful development experience

The new @logtape/pretty package transforms console logging into a visually appealing experience designed specifically for local development. Inspired by Signale, it features colorful emojis for each log level, smart category truncation that preserves important context, and perfect column alignment that makes logs easy to scan.

LogTape pretty formatter output showing colorful console logs with emojis and perfect alignment

import { configure, getConsoleSink } from "@logtape/logtape";
import { prettyFormatter } from "@logtape/pretty";

await configure({
  sinks: {
    console: getConsoleSink({ formatter: prettyFormatter })
  },
  // ...
});

As shown above, the pretty formatter supports true color terminals with rich color schemes, configurable icons, and intelligent word wrapping that maintains visual consistency even for long messages.

Ecosystem integration

Perhaps most significantly, LogTape 1.0.0 introduces adapter packages that bridge the gap between LogTape's library-friendly design and existing logging infrastructure. The @logtape/adaptor-winston and @logtape/adaptor-pino packages allow applications using these established logging libraries to seamlessly integrate LogTape-enabled libraries without changing their existing setup.

// Quick setup with winston
import "@logtape/adaptor-winston/install";
// Or with custom configuration
import { install } from "@logtape/adaptor-winston";
import winston from "winston";

const logger = winston.createLogger({/* your config */});
install(logger);

These adapters preserve LogTape's structured logging capabilities while routing everything through your preferred logging system, making adoption of LogTape-enabled libraries frictionless for existing applications.

Developer experience enhancements

This release includes several quality-of-life improvements for developers working with LogTape. The new getLogLevels() function provides programmatic access to all available log levels, while the LogMethod type offers better type inference for logging methods.

Browser compatibility has been improved, particularly for the @logtape/otel package, which previously had issues in browser environments due to Node.js-specific imports. The package now works seamlessly across all JavaScript runtimes without throwing module resolution errors.

Breaking changes and migration guide

LogTape 1.0.0 includes one notable breaking change: the removal of the deprecated LoggerConfig.level property. This property was deprecated in version 0.8.0 in favor of the more descriptive LoggerConfig.lowestLevel property.

If your configuration still uses the old property, simply rename it:

// Before (deprecated)
{ category: ["app"], level: "info", sinks: ["console"] }
// After
{ category: ["app"], lowestLevel: "info", sinks: ["console"] }

For more complex filtering requirements, consider using the LoggerConfig.filters option instead, which provides more flexibility and supports inheritance from parent loggers.

Complete package ecosystem

LogTape 1.0.0 represents the culmination of a comprehensive package ecosystem, now consisting of 11 specialized packages that address different aspects of logging infrastructure. This modular approach allows you to install only the packages you need, keeping your dependency footprint minimal while accessing powerful logging capabilities when required.

Package JSR npm Description
@logtape/logtape JSR npm Core logging functionality
@logtape/adaptor-pino JSR npm Pino adapter
@logtape/adaptor-winston JSR npm winston adapter
@logtape/cloudwatch-logs JSR npm AWS CloudWatch Logs sink
@logtape/file JSR npm File sinks
@logtape/otel JSR npm OpenTelemetry sink
@logtape/pretty JSR npm Beautiful text formatter
@logtape/redaction JSR npm Data redaction
@logtape/sentry JSR npm Sentry sink
@logtape/syslog JSR npm Syslog sink
@logtape/windows-eventlog JSR npm Windows Event Log sink

Getting started

Whether you're new to LogTape or upgrading from a previous version, getting started with 1.0.0 is straightforward. For new projects, begin with a simple configuration and gradually add the packages and features you need:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: "my-app", lowestLevel: "info", sinks: ["console"] }
  ]
});

Existing applications using winston or Pino can immediately benefit from LogTape-enabled libraries by installing the appropriate adapter. For comprehensive migration guidance and detailed feature documentation, visit our documentation site.

The 1.0.0 release represents not just a version number, but a commitment to the stability and maturity that production applications require. We're excited to see what you'll build with LogTape.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


What is LogTape?

LogTape is a logging library designed specifically for the modern JavaScript ecosystem. It stands out with its zero-dependency architecture, universal runtime support across Node.js, Deno, Bun, browsers, and edge functions, and a library-first design philosophy that allows library authors to add logging without imposing any burden on their users. When LogTape isn't configured, logging calls have virtually no performance impact, making it the only truly unobtrusive logging solution available.

For a comprehensive overview of LogTape's capabilities and philosophy, see our introduction guide.

Milestone achievement

We're excited to announce LogTape 1.0.0, marking a significant milestone in the library's development. This release represents our commitment to API stability and long-term support. The 1.0.0 designation signals that LogTape's core APIs are now stable and ready for production use, with any future breaking changes following semantic versioning principles.

This milestone builds upon months of refinement, community feedback, and real-world usage, establishing LogTape as a mature and reliable logging solution for JavaScript applications and libraries.

Major new features

High-performance logging infrastructure

LogTape 1.0.0 introduces several performance-oriented features designed for high-throughput production environments. The new non-blocking sink option allows console, stream, and file sinks to buffer log records and flush them asynchronously, preventing logging operations from blocking your application's main thread.

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: {
    console: getConsoleSink({ 
      nonBlocking: {
        bufferSize: 1000,
        flushInterval: 50
      }
    })
  },
  // ...
});

The new fromAsyncSink() function provides a clean way to integrate asynchronous logging operations while maintaining LogTape's synchronous sink interface. This enables scenarios like sending logs to remote servers or databases without blocking your application.

import { fromAsyncSink } from "@logtape/logtape";

const webhookSink = fromAsyncSink(async (record) => {
  await fetch("https://logs.example.com", {
    method: "POST",
    body: JSON.stringify(record)
  });
});

For file operations specifically, the new getStreamFileSink() function in the @logtape/file package leverages Node.js PassThrough streams to deliver optimal I/O performance with automatic backpressure management.

New sink integrations

This release significantly expands LogTape's integration capabilities with two major new sink packages. The @logtape/cloudwatch-logs package enables direct integration with AWS CloudWatch Logs, featuring intelligent batching, exponential backoff retry strategies, and support for structured logging through JSON Lines formatting.

import { getCloudWatchLogsSink } from "@logtape/cloudwatch-logs";

const sink = getCloudWatchLogsSink({
  logGroupName: "/aws/lambda/my-function",
  logStreamName: "my-stream",
  region: "us-east-1"
});

The @logtape/windows-eventlog package brings native Windows Event Log support with cross-runtime compatibility across Deno, Node.js, and Bun. This integration uses runtime-optimized FFI implementations for maximum performance while maintaining proper error handling and resource cleanup.

Beautiful development experience

The new @logtape/pretty package transforms console logging into a visually appealing experience designed specifically for local development. Inspired by Signale, it features colorful emojis for each log level, smart category truncation that preserves important context, and perfect column alignment that makes logs easy to scan.

LogTape pretty formatter output showing colorful console logs with emojis and perfect alignment

import { configure, getConsoleSink } from "@logtape/logtape";
import { prettyFormatter } from "@logtape/pretty";

await configure({
  sinks: {
    console: getConsoleSink({ formatter: prettyFormatter })
  },
  // ...
});

As shown above, the pretty formatter supports true color terminals with rich color schemes, configurable icons, and intelligent word wrapping that maintains visual consistency even for long messages.

Ecosystem integration

Perhaps most significantly, LogTape 1.0.0 introduces adapter packages that bridge the gap between LogTape's library-friendly design and existing logging infrastructure. The @logtape/adaptor-winston and @logtape/adaptor-pino packages allow applications using these established logging libraries to seamlessly integrate LogTape-enabled libraries without changing their existing setup.

// Quick setup with winston
import "@logtape/adaptor-winston/install";
// Or with custom configuration
import { install } from "@logtape/adaptor-winston";
import winston from "winston";

const logger = winston.createLogger({/* your config */});
install(logger);

These adapters preserve LogTape's structured logging capabilities while routing everything through your preferred logging system, making adoption of LogTape-enabled libraries frictionless for existing applications.

Developer experience enhancements

This release includes several quality-of-life improvements for developers working with LogTape. The new getLogLevels() function provides programmatic access to all available log levels, while the LogMethod type offers better type inference for logging methods.

Browser compatibility has been improved, particularly for the @logtape/otel package, which previously had issues in browser environments due to Node.js-specific imports. The package now works seamlessly across all JavaScript runtimes without throwing module resolution errors.

Breaking changes and migration guide

LogTape 1.0.0 includes one notable breaking change: the removal of the deprecated LoggerConfig.level property. This property was deprecated in version 0.8.0 in favor of the more descriptive LoggerConfig.lowestLevel property.

If your configuration still uses the old property, simply rename it:

// Before (deprecated)
{ category: ["app"], level: "info", sinks: ["console"] }
// After
{ category: ["app"], lowestLevel: "info", sinks: ["console"] }

For more complex filtering requirements, consider using the LoggerConfig.filters option instead, which provides more flexibility and supports inheritance from parent loggers.

Complete package ecosystem

LogTape 1.0.0 represents the culmination of a comprehensive package ecosystem, now consisting of 11 specialized packages that address different aspects of logging infrastructure. This modular approach allows you to install only the packages you need, keeping your dependency footprint minimal while accessing powerful logging capabilities when required.

Package JSR npm Description
@logtape/logtape JSR npm Core logging functionality
@logtape/adaptor-pino JSR npm Pino adapter
@logtape/adaptor-winston JSR npm winston adapter
@logtape/cloudwatch-logs JSR npm AWS CloudWatch Logs sink
@logtape/file JSR npm File sinks
@logtape/otel JSR npm OpenTelemetry sink
@logtape/pretty JSR npm Beautiful text formatter
@logtape/redaction JSR npm Data redaction
@logtape/sentry JSR npm Sentry sink
@logtape/syslog JSR npm Syslog sink
@logtape/windows-eventlog JSR npm Windows Event Log sink

Getting started

Whether you're new to LogTape or upgrading from a previous version, getting started with 1.0.0 is straightforward. For new projects, begin with a simple configuration and gradually add the packages and features you need:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: "my-app", lowestLevel: "info", sinks: ["console"] }
  ]
});

Existing applications using winston or Pino can immediately benefit from LogTape-enabled libraries by installing the appropriate adapter. For comprehensive migration guidance and detailed feature documentation, visit our documentation site.

The 1.0.0 release represents not just a version number, but a commitment to the stability and maturity that production applications require. We're excited to see what you'll build with LogTape.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


What is LogTape?

LogTape is a logging library designed specifically for the modern JavaScript ecosystem. It stands out with its zero-dependency architecture, universal runtime support across Node.js, Deno, Bun, browsers, and edge functions, and a library-first design philosophy that allows library authors to add logging without imposing any burden on their users. When LogTape isn't configured, logging calls have virtually no performance impact, making it the only truly unobtrusive logging solution available.

For a comprehensive overview of LogTape's capabilities and philosophy, see our introduction guide.

Milestone achievement

We're excited to announce LogTape 1.0.0, marking a significant milestone in the library's development. This release represents our commitment to API stability and long-term support. The 1.0.0 designation signals that LogTape's core APIs are now stable and ready for production use, with any future breaking changes following semantic versioning principles.

This milestone builds upon months of refinement, community feedback, and real-world usage, establishing LogTape as a mature and reliable logging solution for JavaScript applications and libraries.

Major new features

High-performance logging infrastructure

LogTape 1.0.0 introduces several performance-oriented features designed for high-throughput production environments. The new non-blocking sink option allows console, stream, and file sinks to buffer log records and flush them asynchronously, preventing logging operations from blocking your application's main thread.

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: {
    console: getConsoleSink({ 
      nonBlocking: {
        bufferSize: 1000,
        flushInterval: 50
      }
    })
  },
  // ...
});

The new fromAsyncSink() function provides a clean way to integrate asynchronous logging operations while maintaining LogTape's synchronous sink interface. This enables scenarios like sending logs to remote servers or databases without blocking your application.

import { fromAsyncSink } from "@logtape/logtape";

const webhookSink = fromAsyncSink(async (record) => {
  await fetch("https://logs.example.com", {
    method: "POST",
    body: JSON.stringify(record)
  });
});

For file operations specifically, the new getStreamFileSink() function in the @logtape/file package leverages Node.js PassThrough streams to deliver optimal I/O performance with automatic backpressure management.

New sink integrations

This release significantly expands LogTape's integration capabilities with two major new sink packages. The @logtape/cloudwatch-logs package enables direct integration with AWS CloudWatch Logs, featuring intelligent batching, exponential backoff retry strategies, and support for structured logging through JSON Lines formatting.

import { getCloudWatchLogsSink } from "@logtape/cloudwatch-logs";

const sink = getCloudWatchLogsSink({
  logGroupName: "/aws/lambda/my-function",
  logStreamName: "my-stream",
  region: "us-east-1"
});

The @logtape/windows-eventlog package brings native Windows Event Log support with cross-runtime compatibility across Deno, Node.js, and Bun. This integration uses runtime-optimized FFI implementations for maximum performance while maintaining proper error handling and resource cleanup.

Beautiful development experience

The new @logtape/pretty package transforms console logging into a visually appealing experience designed specifically for local development. Inspired by Signale, it features colorful emojis for each log level, smart category truncation that preserves important context, and perfect column alignment that makes logs easy to scan.

LogTape pretty formatter output showing colorful console logs with emojis and perfect alignment

import { configure, getConsoleSink } from "@logtape/logtape";
import { prettyFormatter } from "@logtape/pretty";

await configure({
  sinks: {
    console: getConsoleSink({ formatter: prettyFormatter })
  },
  // ...
});

As shown above, the pretty formatter supports true color terminals with rich color schemes, configurable icons, and intelligent word wrapping that maintains visual consistency even for long messages.

Ecosystem integration

Perhaps most significantly, LogTape 1.0.0 introduces adapter packages that bridge the gap between LogTape's library-friendly design and existing logging infrastructure. The @logtape/adaptor-winston and @logtape/adaptor-pino packages allow applications using these established logging libraries to seamlessly integrate LogTape-enabled libraries without changing their existing setup.

// Quick setup with winston
import "@logtape/adaptor-winston/install";
// Or with custom configuration
import { install } from "@logtape/adaptor-winston";
import winston from "winston";

const logger = winston.createLogger({/* your config */});
install(logger);

These adapters preserve LogTape's structured logging capabilities while routing everything through your preferred logging system, making adoption of LogTape-enabled libraries frictionless for existing applications.

Developer experience enhancements

This release includes several quality-of-life improvements for developers working with LogTape. The new getLogLevels() function provides programmatic access to all available log levels, while the LogMethod type offers better type inference for logging methods.

Browser compatibility has been improved, particularly for the @logtape/otel package, which previously had issues in browser environments due to Node.js-specific imports. The package now works seamlessly across all JavaScript runtimes without throwing module resolution errors.

Breaking changes and migration guide

LogTape 1.0.0 includes one notable breaking change: the removal of the deprecated LoggerConfig.level property. This property was deprecated in version 0.8.0 in favor of the more descriptive LoggerConfig.lowestLevel property.

If your configuration still uses the old property, simply rename it:

// Before (deprecated)
{ category: ["app"], level: "info", sinks: ["console"] }
// After
{ category: ["app"], lowestLevel: "info", sinks: ["console"] }

For more complex filtering requirements, consider using the LoggerConfig.filters option instead, which provides more flexibility and supports inheritance from parent loggers.

Complete package ecosystem

LogTape 1.0.0 represents the culmination of a comprehensive package ecosystem, now consisting of 11 specialized packages that address different aspects of logging infrastructure. This modular approach allows you to install only the packages you need, keeping your dependency footprint minimal while accessing powerful logging capabilities when required.

Package JSR npm Description
@logtape/logtape JSR npm Core logging functionality
@logtape/adaptor-pino JSR npm Pino adapter
@logtape/adaptor-winston JSR npm winston adapter
@logtape/cloudwatch-logs JSR npm AWS CloudWatch Logs sink
@logtape/file JSR npm File sinks
@logtape/otel JSR npm OpenTelemetry sink
@logtape/pretty JSR npm Beautiful text formatter
@logtape/redaction JSR npm Data redaction
@logtape/sentry JSR npm Sentry sink
@logtape/syslog JSR npm Syslog sink
@logtape/windows-eventlog JSR npm Windows Event Log sink

Getting started

Whether you're new to LogTape or upgrading from a previous version, getting started with 1.0.0 is straightforward. For new projects, begin with a simple configuration and gradually add the packages and features you need:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  loggers: [
    { category: "my-app", lowestLevel: "info", sinks: ["console"] }
  ]
});

Existing applications using winston or Pino can immediately benefit from LogTape-enabled libraries by installing the appropriate adapter. For comprehensive migration guidance and detailed feature documentation, visit our documentation site.

The 1.0.0 release represents not just a version number, but a commitment to the stability and maturity that production applications require. We're excited to see what you'll build with LogTape.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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.

  • Added "trace" to the LogLevel union type
  • Added Logger.trace() method for logging trace-level messages
  • The complete severity hierarchy is now: trace < debug < info < warning < error < fatal

Enhanced File Sink Performance

File sinks now support configurable buffering, significantly improving write performance for high-volume logging scenarios.

  • Added bufferSize option (default: 8192 characters) to control write buffering behavior
  • Added flushInterval option (default: 5000ms) for automatic time-based buffer flushing
  • Set bufferSize to 0 for immediate writes without buffering
  • Set flushInterval to 0 to disable time-based flushing
  • Buffer contents are automatically flushed when the sink is disposed

These options are available for both getFileSink() and getRotatingFileSink() functions.

Syslog Support

The new @logtape/syslog package enables sending log messages to syslog servers using the RFC 5424 format.

  • Support for both UDP and TCP protocols
  • All standard RFC 5424 facilities (kern, user, mail, daemon, local07, 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
})

Installation

LogTape 0.12.0 is available on JSR and npm:

deno add jsr:@logtape/logtape  # Deno
npm  add     @logtape/logtape  # npm
pnpm add     @logtape/logtape  # pnpm
yarn add     @logtape/logtape  # yarn
bun  add     @logtape/logtape  # Bun

For the syslog sink:

deno add jsr:@logtape/syslog  # Deno
npm  add     @logtape/syslog  # npm
pnpm add     @logtape/syslog  # pnpm
yarn add     @logtape/syslog  # yarn
bun  add     @logtape/syslog  # Bun

Acknowledgments

We thank all contributors who helped make this release possible, including those who reported issues, submitted pull requests, and provided feedback.

For the complete list of changes, please refer to the changelog.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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.

  • Added "trace" to the LogLevel union type
  • Added Logger.trace() method for logging trace-level messages
  • The complete severity hierarchy is now: trace < debug < info < warning < error < fatal

Enhanced File Sink Performance

File sinks now support configurable buffering, significantly improving write performance for high-volume logging scenarios.

  • Added bufferSize option (default: 8192 characters) to control write buffering behavior
  • Added flushInterval option (default: 5000ms) for automatic time-based buffer flushing
  • Set bufferSize to 0 for immediate writes without buffering
  • Set flushInterval to 0 to disable time-based flushing
  • Buffer contents are automatically flushed when the sink is disposed

These options are available for both getFileSink() and getRotatingFileSink() functions.

Syslog Support

The new @logtape/syslog package enables sending log messages to syslog servers using the RFC 5424 format.

  • Support for both UDP and TCP protocols
  • All standard RFC 5424 facilities (kern, user, mail, daemon, local07, 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
})

Installation

LogTape 0.12.0 is available on JSR and npm:

deno add jsr:@logtape/logtape  # Deno
npm  add     @logtape/logtape  # npm
pnpm add     @logtape/logtape  # pnpm
yarn add     @logtape/logtape  # yarn
bun  add     @logtape/logtape  # Bun

For the syslog sink:

deno add jsr:@logtape/syslog  # Deno
npm  add     @logtape/syslog  # npm
pnpm add     @logtape/syslog  # pnpm
yarn add     @logtape/syslog  # yarn
bun  add     @logtape/syslog  # Bun

Acknowledgments

We thank all contributors who helped make this release possible, including those who reported issues, submitted pull requests, and provided feedback.

For the complete list of changes, please refer to the changelog.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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.

  • Added "trace" to the LogLevel union type
  • Added Logger.trace() method for logging trace-level messages
  • The complete severity hierarchy is now: trace < debug < info < warning < error < fatal

Enhanced File Sink Performance

File sinks now support configurable buffering, significantly improving write performance for high-volume logging scenarios.

  • Added bufferSize option (default: 8192 characters) to control write buffering behavior
  • Added flushInterval option (default: 5000ms) for automatic time-based buffer flushing
  • Set bufferSize to 0 for immediate writes without buffering
  • Set flushInterval to 0 to disable time-based flushing
  • Buffer contents are automatically flushed when the sink is disposed

These options are available for both getFileSink() and getRotatingFileSink() functions.

Syslog Support

The new @logtape/syslog package enables sending log messages to syslog servers using the RFC 5424 format.

  • Support for both UDP and TCP protocols
  • All standard RFC 5424 facilities (kern, user, mail, daemon, local07, 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
})

Installation

LogTape 0.12.0 is available on JSR and npm:

deno add jsr:@logtape/logtape  # Deno
npm  add     @logtape/logtape  # npm
pnpm add     @logtape/logtape  # pnpm
yarn add     @logtape/logtape  # yarn
bun  add     @logtape/logtape  # Bun

For the syslog sink:

deno add jsr:@logtape/syslog  # Deno
npm  add     @logtape/syslog  # npm
pnpm add     @logtape/syslog  # pnpm
yarn add     @logtape/syslog  # yarn
bun  add     @logtape/syslog  # Bun

Acknowledgments

We thank all contributors who helped make this release possible, including those who reported issues, submitted pull requests, and provided feedback.

For the complete list of changes, please refer to the changelog.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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.

  • Added "trace" to the LogLevel union type
  • Added Logger.trace() method for logging trace-level messages
  • The complete severity hierarchy is now: trace < debug < info < warning < error < fatal

Enhanced File Sink Performance

File sinks now support configurable buffering, significantly improving write performance for high-volume logging scenarios.

  • Added bufferSize option (default: 8192 characters) to control write buffering behavior
  • Added flushInterval option (default: 5000ms) for automatic time-based buffer flushing
  • Set bufferSize to 0 for immediate writes without buffering
  • Set flushInterval to 0 to disable time-based flushing
  • Buffer contents are automatically flushed when the sink is disposed

These options are available for both getFileSink() and getRotatingFileSink() functions.

Syslog Support

The new @logtape/syslog package enables sending log messages to syslog servers using the RFC 5424 format.

  • Support for both UDP and TCP protocols
  • All standard RFC 5424 facilities (kern, user, mail, daemon, local07, 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
})

Installation

LogTape 0.12.0 is available on JSR and npm:

deno add jsr:@logtape/logtape  # Deno
npm  add     @logtape/logtape  # npm
pnpm add     @logtape/logtape  # pnpm
yarn add     @logtape/logtape  # yarn
bun  add     @logtape/logtape  # Bun

For the syslog sink:

deno add jsr:@logtape/syslog  # Deno
npm  add     @logtape/syslog  # npm
pnpm add     @logtape/syslog  # pnpm
yarn add     @logtape/syslog  # yarn
bun  add     @logtape/syslog  # Bun

Acknowledgments

We thank all contributors who helped make this release possible, including those who reported issues, submitted pull requests, and provided feedback.

For the complete list of changes, please refer to the changelog.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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.

  • Added "trace" to the LogLevel union type
  • Added Logger.trace() method for logging trace-level messages
  • The complete severity hierarchy is now: trace < debug < info < warning < error < fatal

Enhanced File Sink Performance

File sinks now support configurable buffering, significantly improving write performance for high-volume logging scenarios.

  • Added bufferSize option (default: 8192 characters) to control write buffering behavior
  • Added flushInterval option (default: 5000ms) for automatic time-based buffer flushing
  • Set bufferSize to 0 for immediate writes without buffering
  • Set flushInterval to 0 to disable time-based flushing
  • Buffer contents are automatically flushed when the sink is disposed

These options are available for both getFileSink() and getRotatingFileSink() functions.

Syslog Support

The new @logtape/syslog package enables sending log messages to syslog servers using the RFC 5424 format.

  • Support for both UDP and TCP protocols
  • All standard RFC 5424 facilities (kern, user, mail, daemon, local07, 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
})

Installation

LogTape 0.12.0 is available on JSR and npm:

deno add jsr:@logtape/logtape  # Deno
npm  add     @logtape/logtape  # npm
pnpm add     @logtape/logtape  # pnpm
yarn add     @logtape/logtape  # yarn
bun  add     @logtape/logtape  # Bun

For the syslog sink:

deno add jsr:@logtape/syslog  # Deno
npm  add     @logtape/syslog  # npm
pnpm add     @logtape/syslog  # pnpm
yarn add     @logtape/syslog  # yarn
bun  add     @logtape/syslog  # Bun

Acknowledgments

We thank all contributors who helped make this release possible, including those who reported issues, submitted pull requests, and provided feedback.

For the complete list of changes, please refer to the changelog.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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.

  • Added "trace" to the LogLevel union type
  • Added Logger.trace() method for logging trace-level messages
  • The complete severity hierarchy is now: trace < debug < info < warning < error < fatal

Enhanced File Sink Performance

File sinks now support configurable buffering, significantly improving write performance for high-volume logging scenarios.

  • Added bufferSize option (default: 8192 characters) to control write buffering behavior
  • Added flushInterval option (default: 5000ms) for automatic time-based buffer flushing
  • Set bufferSize to 0 for immediate writes without buffering
  • Set flushInterval to 0 to disable time-based flushing
  • Buffer contents are automatically flushed when the sink is disposed

These options are available for both getFileSink() and getRotatingFileSink() functions.

Syslog Support

The new @logtape/syslog package enables sending log messages to syslog servers using the RFC 5424 format.

  • Support for both UDP and TCP protocols
  • All standard RFC 5424 facilities (kern, user, mail, daemon, local07, 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
})

Installation

LogTape 0.12.0 is available on JSR and npm:

deno add jsr:@logtape/logtape  # Deno
npm  add     @logtape/logtape  # npm
pnpm add     @logtape/logtape  # pnpm
yarn add     @logtape/logtape  # yarn
bun  add     @logtape/logtape  # Bun

For the syslog sink:

deno add jsr:@logtape/syslog  # Deno
npm  add     @logtape/syslog  # npm
pnpm add     @logtape/syslog  # pnpm
yarn add     @logtape/syslog  # yarn
bun  add     @logtape/syslog  # Bun

Acknowledgments

We thank all contributors who helped make this release possible, including those who reported issues, submitted pull requests, and provided feedback.

For the complete list of changes, please refer to the changelog.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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.

Customizable JSON Lines options

The new getJsonLinesFormatter() function provides several customization options:

  • Category separator: Control how hierarchical categories are joined
  • Message format: Choose between raw templates or rendered messages
  • Properties handling: Flatten properties, nest them, or prepend with custom prefixes

Backward compatibility

All existing logging patterns continue to work exactly as before. The new features are additive and don't break any existing code.

Why this matters

These enhancements make LogTape even more powerful for modern application logging:

  • Better observability: Structured data makes logs more searchable and analyzable
  • Improved developer experience: Less boilerplate when logging complex objects
  • Industry standard formats: JSON Lines support for better integration with log management systems
  • Flexible formatting: Customize output to match your infrastructure needs

Installation

LogTape 0.11.0 is available on both JSR and npm:

deno add jsr:@logtape/logtape@0.11.0
npm  add     @logtape/logtape@0.11.0
pnpm add     @logtape/logtape@0.11.0
yarn add     @logtape/logtape@0.11.0
bun  add     @logtape/logtape@0.11.0

Learn more

We hope these new features enhance your logging experience. As always, LogTape remains zero-dependency and works across all JavaScript runtimes.

Happy logging!

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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.

Customizable JSON Lines options

The new getJsonLinesFormatter() function provides several customization options:

  • Category separator: Control how hierarchical categories are joined
  • Message format: Choose between raw templates or rendered messages
  • Properties handling: Flatten properties, nest them, or prepend with custom prefixes

Backward compatibility

All existing logging patterns continue to work exactly as before. The new features are additive and don't break any existing code.

Why this matters

These enhancements make LogTape even more powerful for modern application logging:

  • Better observability: Structured data makes logs more searchable and analyzable
  • Improved developer experience: Less boilerplate when logging complex objects
  • Industry standard formats: JSON Lines support for better integration with log management systems
  • Flexible formatting: Customize output to match your infrastructure needs

Installation

LogTape 0.11.0 is available on both JSR and npm:

deno add jsr:@logtape/logtape@0.11.0
npm  add     @logtape/logtape@0.11.0
pnpm add     @logtape/logtape@0.11.0
yarn add     @logtape/logtape@0.11.0
bun  add     @logtape/logtape@0.11.0

Learn more

We hope these new features enhance your logging experience. As always, LogTape remains zero-dependency and works across all JavaScript runtimes.

Happy logging!

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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 itself
import { 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 runtimes
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-service", "user-management"]);

// Works in Node.js, Deno, Bun, browsers, or edge functions
logger.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:

// Parent category
const appLogger = getLogger(["my-app"]);

// Child category inherits settings from parent
const dbLogger = getLogger(["my-app", "database"]);

// Grandchild category
const queryLogger = getLogger(["my-app", "database", "queries"]);

// Alternative approach using getChild()
const userLogger = appLogger.getChild("users");
const authLogger = userLogger.getChild("auth");

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:

logger.info("User logged in", {
  userId: 123456,
  username: "johndoe",
  loginTime: new Date(),
  ipAddress: "192.168.1.1"
});

LogTape also supports placeholders in messages, connecting structured data with human-readable text:

logger.info("User {username} (ID: {userId}) logged in from {ipAddress}", {
  userId: 123456,
  username: "johndoe",
  ipAddress: "192.168.1.1"
});

Structured logging offers substantial benefits:

Improved searchability
Search for specific field values instead of parsing text
Better analysis
Perform data analysis on structured fields
Consistent format
Enforce standardized log formats
Machine-readable
Easier processing by log management systems

For performance-conscious applications, LogTape offers lazy evaluation of structured data:

logger.debug("Performance metrics", () => ({
  memoryUsage: process.memoryUsage(),
  cpuUsage: process.cpuUsage(),
  timestamp: performance.now()
}));

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 function
const 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 configuration
await 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 logs
const 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:

  1. Libraries provide logging output points
  2. Applications configure how those logs are handled

Here's how a library might implement LogTape:

// my-awesome-lib/database.js
import { getLogger } from "@logtape/logtape";

export class Database {
  private logger = getLogger(["my-awesome-lib", "database"]);

  constructor(host, port, user) {
    this.host = host;
    this.port = port;
    this.user = user;
  }

  connect() {
    this.logger.info("Connecting to database", {
      host: this.host,
      port: this.port,
      user: this.user
    });
    
    // Connection logic...
    
    this.logger.debug("Connection established");
  }
  
  query(sql) {
    this.logger.debug("Executing query", { sql });
    // Query logic...
  }
}

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 code
import { configure, getConsoleSink } from "@logtape/logtape";
import { Database } from "my-awesome-lib";

// Configure how logs should be handled
await 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 library
const db = new Database("localhost", 5432, "user");
db.connect();

This separation of concerns offers several benefits:

  1. Library users have complete control over log handling
  2. Libraries can provide rich logging without imposing implementation details
  3. No risk of conflict with application logging configurations
  4. 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 context
const requestLogger = logger.with({
  requestId: "abc-123",
  userId: 42,
  endpoint: "/users"
});

// All logs from this logger include the context properties
requestLogger.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.

For more information and detailed documentation, visit LogTape's official website.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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)
  • Rickey Ward for adding the lazy file sink option (#38, #39)

We encourage all users to upgrade to this version to take advantage of these improvements. As always, we welcome your feedback and contributions.


For more details on these changes, see the full changelog.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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)
  • Rickey Ward for adding the lazy file sink option (#38, #39)

We encourage all users to upgrade to this version to take advantage of these improvements. As always, we welcome your feedback and contributions.


For more details on these changes, see the full changelog.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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)
  • Rickey Ward for adding the lazy file sink option (#38, #39)

We encourage all users to upgrade to this version to take advantage of these improvements. As always, we welcome your feedback and contributions.


For more details on these changes, see the full changelog.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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)
  • Rickey Ward for adding the lazy file sink option (#38, #39)

We encourage all users to upgrade to this version to take advantage of these improvements. As always, we welcome your feedback and contributions.


For more details on these changes, see the full changelog.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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)
  • Rickey Ward for adding the lazy file sink option (#38, #39)

We encourage all users to upgrade to this version to take advantage of these improvements. As always, we welcome your feedback and contributions.


For more details on these changes, see the full changelog.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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 itself
import { 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 runtimes
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-service", "user-management"]);

// Works in Node.js, Deno, Bun, browsers, or edge functions
logger.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:

// Parent category
const appLogger = getLogger(["my-app"]);

// Child category inherits settings from parent
const dbLogger = getLogger(["my-app", "database"]);

// Grandchild category
const queryLogger = getLogger(["my-app", "database", "queries"]);

// Alternative approach using getChild()
const userLogger = appLogger.getChild("users");
const authLogger = userLogger.getChild("auth");

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:

logger.info("User logged in", {
  userId: 123456,
  username: "johndoe",
  loginTime: new Date(),
  ipAddress: "192.168.1.1"
});

LogTape also supports placeholders in messages, connecting structured data with human-readable text:

logger.info("User {username} (ID: {userId}) logged in from {ipAddress}", {
  userId: 123456,
  username: "johndoe",
  ipAddress: "192.168.1.1"
});

Structured logging offers substantial benefits:

Improved searchability
Search for specific field values instead of parsing text
Better analysis
Perform data analysis on structured fields
Consistent format
Enforce standardized log formats
Machine-readable
Easier processing by log management systems

For performance-conscious applications, LogTape offers lazy evaluation of structured data:

logger.debug("Performance metrics", () => ({
  memoryUsage: process.memoryUsage(),
  cpuUsage: process.cpuUsage(),
  timestamp: performance.now()
}));

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 function
const 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 configuration
await 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 logs
const 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:

  1. Libraries provide logging output points
  2. Applications configure how those logs are handled

Here's how a library might implement LogTape:

// my-awesome-lib/database.js
import { getLogger } from "@logtape/logtape";

export class Database {
  private logger = getLogger(["my-awesome-lib", "database"]);

  constructor(host, port, user) {
    this.host = host;
    this.port = port;
    this.user = user;
  }

  connect() {
    this.logger.info("Connecting to database", {
      host: this.host,
      port: this.port,
      user: this.user
    });
    
    // Connection logic...
    
    this.logger.debug("Connection established");
  }
  
  query(sql) {
    this.logger.debug("Executing query", { sql });
    // Query logic...
  }
}

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 code
import { configure, getConsoleSink } from "@logtape/logtape";
import { Database } from "my-awesome-lib";

// Configure how logs should be handled
await 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 library
const db = new Database("localhost", 5432, "user");
db.connect();

This separation of concerns offers several benefits:

  1. Library users have complete control over log handling
  2. Libraries can provide rich logging without imposing implementation details
  3. No risk of conflict with application logging configurations
  4. 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 context
const requestLogger = logger.with({
  requestId: "abc-123",
  userId: 42,
  endpoint: "/users"
});

// All logs from this logger include the context properties
requestLogger.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.

For more information and detailed documentation, visit LogTape's official website.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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 itself
import { 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 runtimes
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-service", "user-management"]);

// Works in Node.js, Deno, Bun, browsers, or edge functions
logger.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:

// Parent category
const appLogger = getLogger(["my-app"]);

// Child category inherits settings from parent
const dbLogger = getLogger(["my-app", "database"]);

// Grandchild category
const queryLogger = getLogger(["my-app", "database", "queries"]);

// Alternative approach using getChild()
const userLogger = appLogger.getChild("users");
const authLogger = userLogger.getChild("auth");

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:

logger.info("User logged in", {
  userId: 123456,
  username: "johndoe",
  loginTime: new Date(),
  ipAddress: "192.168.1.1"
});

LogTape also supports placeholders in messages, connecting structured data with human-readable text:

logger.info("User {username} (ID: {userId}) logged in from {ipAddress}", {
  userId: 123456,
  username: "johndoe",
  ipAddress: "192.168.1.1"
});

Structured logging offers substantial benefits:

Improved searchability
Search for specific field values instead of parsing text
Better analysis
Perform data analysis on structured fields
Consistent format
Enforce standardized log formats
Machine-readable
Easier processing by log management systems

For performance-conscious applications, LogTape offers lazy evaluation of structured data:

logger.debug("Performance metrics", () => ({
  memoryUsage: process.memoryUsage(),
  cpuUsage: process.cpuUsage(),
  timestamp: performance.now()
}));

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 function
const 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 configuration
await 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 logs
const 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:

  1. Libraries provide logging output points
  2. Applications configure how those logs are handled

Here's how a library might implement LogTape:

// my-awesome-lib/database.js
import { getLogger } from "@logtape/logtape";

export class Database {
  private logger = getLogger(["my-awesome-lib", "database"]);

  constructor(host, port, user) {
    this.host = host;
    this.port = port;
    this.user = user;
  }

  connect() {
    this.logger.info("Connecting to database", {
      host: this.host,
      port: this.port,
      user: this.user
    });
    
    // Connection logic...
    
    this.logger.debug("Connection established");
  }
  
  query(sql) {
    this.logger.debug("Executing query", { sql });
    // Query logic...
  }
}

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 code
import { configure, getConsoleSink } from "@logtape/logtape";
import { Database } from "my-awesome-lib";

// Configure how logs should be handled
await 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 library
const db = new Database("localhost", 5432, "user");
db.connect();

This separation of concerns offers several benefits:

  1. Library users have complete control over log handling
  2. Libraries can provide rich logging without imposing implementation details
  3. No risk of conflict with application logging configurations
  4. 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 context
const requestLogger = logger.with({
  requestId: "abc-123",
  userId: 42,
  endpoint: "/users"
});

// All logs from this logger include the context properties
requestLogger.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.

For more information and detailed documentation, visit LogTape's official website.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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 itself
import { 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 runtimes
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-service", "user-management"]);

// Works in Node.js, Deno, Bun, browsers, or edge functions
logger.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:

// Parent category
const appLogger = getLogger(["my-app"]);

// Child category inherits settings from parent
const dbLogger = getLogger(["my-app", "database"]);

// Grandchild category
const queryLogger = getLogger(["my-app", "database", "queries"]);

// Alternative approach using getChild()
const userLogger = appLogger.getChild("users");
const authLogger = userLogger.getChild("auth");

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:

logger.info("User logged in", {
  userId: 123456,
  username: "johndoe",
  loginTime: new Date(),
  ipAddress: "192.168.1.1"
});

LogTape also supports placeholders in messages, connecting structured data with human-readable text:

logger.info("User {username} (ID: {userId}) logged in from {ipAddress}", {
  userId: 123456,
  username: "johndoe",
  ipAddress: "192.168.1.1"
});

Structured logging offers substantial benefits:

Improved searchability
Search for specific field values instead of parsing text
Better analysis
Perform data analysis on structured fields
Consistent format
Enforce standardized log formats
Machine-readable
Easier processing by log management systems

For performance-conscious applications, LogTape offers lazy evaluation of structured data:

logger.debug("Performance metrics", () => ({
  memoryUsage: process.memoryUsage(),
  cpuUsage: process.cpuUsage(),
  timestamp: performance.now()
}));

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 function
const 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 configuration
await 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 logs
const 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:

  1. Libraries provide logging output points
  2. Applications configure how those logs are handled

Here's how a library might implement LogTape:

// my-awesome-lib/database.js
import { getLogger } from "@logtape/logtape";

export class Database {
  private logger = getLogger(["my-awesome-lib", "database"]);

  constructor(host, port, user) {
    this.host = host;
    this.port = port;
    this.user = user;
  }

  connect() {
    this.logger.info("Connecting to database", {
      host: this.host,
      port: this.port,
      user: this.user
    });
    
    // Connection logic...
    
    this.logger.debug("Connection established");
  }
  
  query(sql) {
    this.logger.debug("Executing query", { sql });
    // Query logic...
  }
}

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 code
import { configure, getConsoleSink } from "@logtape/logtape";
import { Database } from "my-awesome-lib";

// Configure how logs should be handled
await 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 library
const db = new Database("localhost", 5432, "user");
db.connect();

This separation of concerns offers several benefits:

  1. Library users have complete control over log handling
  2. Libraries can provide rich logging without imposing implementation details
  3. No risk of conflict with application logging configurations
  4. 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 context
const requestLogger = logger.with({
  requestId: "abc-123",
  userId: 42,
  endpoint: "/users"
});

// All logs from this logger include the context properties
requestLogger.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.

For more information and detailed documentation, visit LogTape's official website.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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 itself
import { 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 runtimes
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-service", "user-management"]);

// Works in Node.js, Deno, Bun, browsers, or edge functions
logger.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:

// Parent category
const appLogger = getLogger(["my-app"]);

// Child category inherits settings from parent
const dbLogger = getLogger(["my-app", "database"]);

// Grandchild category
const queryLogger = getLogger(["my-app", "database", "queries"]);

// Alternative approach using getChild()
const userLogger = appLogger.getChild("users");
const authLogger = userLogger.getChild("auth");

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:

logger.info("User logged in", {
  userId: 123456,
  username: "johndoe",
  loginTime: new Date(),
  ipAddress: "192.168.1.1"
});

LogTape also supports placeholders in messages, connecting structured data with human-readable text:

logger.info("User {username} (ID: {userId}) logged in from {ipAddress}", {
  userId: 123456,
  username: "johndoe",
  ipAddress: "192.168.1.1"
});

Structured logging offers substantial benefits:

Improved searchability
Search for specific field values instead of parsing text
Better analysis
Perform data analysis on structured fields
Consistent format
Enforce standardized log formats
Machine-readable
Easier processing by log management systems

For performance-conscious applications, LogTape offers lazy evaluation of structured data:

logger.debug("Performance metrics", () => ({
  memoryUsage: process.memoryUsage(),
  cpuUsage: process.cpuUsage(),
  timestamp: performance.now()
}));

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 function
const 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 configuration
await 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 logs
const 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:

  1. Libraries provide logging output points
  2. Applications configure how those logs are handled

Here's how a library might implement LogTape:

// my-awesome-lib/database.js
import { getLogger } from "@logtape/logtape";

export class Database {
  private logger = getLogger(["my-awesome-lib", "database"]);

  constructor(host, port, user) {
    this.host = host;
    this.port = port;
    this.user = user;
  }

  connect() {
    this.logger.info("Connecting to database", {
      host: this.host,
      port: this.port,
      user: this.user
    });
    
    // Connection logic...
    
    this.logger.debug("Connection established");
  }
  
  query(sql) {
    this.logger.debug("Executing query", { sql });
    // Query logic...
  }
}

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 code
import { configure, getConsoleSink } from "@logtape/logtape";
import { Database } from "my-awesome-lib";

// Configure how logs should be handled
await 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 library
const db = new Database("localhost", 5432, "user");
db.connect();

This separation of concerns offers several benefits:

  1. Library users have complete control over log handling
  2. Libraries can provide rich logging without imposing implementation details
  3. No risk of conflict with application logging configurations
  4. 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 context
const requestLogger = logger.with({
  requestId: "abc-123",
  userId: 42,
  endpoint: "/users"
});

// All logs from this logger include the context properties
requestLogger.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.

For more information and detailed documentation, visit LogTape's official website.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub


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 itself
import { 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 runtimes
import { getLogger } from "@logtape/logtape";

const logger = getLogger(["my-service", "user-management"]);

// Works in Node.js, Deno, Bun, browsers, or edge functions
logger.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:

// Parent category
const appLogger = getLogger(["my-app"]);

// Child category inherits settings from parent
const dbLogger = getLogger(["my-app", "database"]);

// Grandchild category
const queryLogger = getLogger(["my-app", "database", "queries"]);

// Alternative approach using getChild()
const userLogger = appLogger.getChild("users");
const authLogger = userLogger.getChild("auth");

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:

logger.info("User logged in", {
  userId: 123456,
  username: "johndoe",
  loginTime: new Date(),
  ipAddress: "192.168.1.1"
});

LogTape also supports placeholders in messages, connecting structured data with human-readable text:

logger.info("User {username} (ID: {userId}) logged in from {ipAddress}", {
  userId: 123456,
  username: "johndoe",
  ipAddress: "192.168.1.1"
});

Structured logging offers substantial benefits:

Improved searchability
Search for specific field values instead of parsing text
Better analysis
Perform data analysis on structured fields
Consistent format
Enforce standardized log formats
Machine-readable
Easier processing by log management systems

For performance-conscious applications, LogTape offers lazy evaluation of structured data:

logger.debug("Performance metrics", () => ({
  memoryUsage: process.memoryUsage(),
  cpuUsage: process.cpuUsage(),
  timestamp: performance.now()
}));

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 function
const 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 configuration
await 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 logs
const 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:

  1. Libraries provide logging output points
  2. Applications configure how those logs are handled

Here's how a library might implement LogTape:

// my-awesome-lib/database.js
import { getLogger } from "@logtape/logtape";

export class Database {
  private logger = getLogger(["my-awesome-lib", "database"]);

  constructor(host, port, user) {
    this.host = host;
    this.port = port;
    this.user = user;
  }

  connect() {
    this.logger.info("Connecting to database", {
      host: this.host,
      port: this.port,
      user: this.user
    });
    
    // Connection logic...
    
    this.logger.debug("Connection established");
  }
  
  query(sql) {
    this.logger.debug("Executing query", { sql });
    // Query logic...
  }
}

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 code
import { configure, getConsoleSink } from "@logtape/logtape";
import { Database } from "my-awesome-lib";

// Configure how logs should be handled
await 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 library
const db = new Database("localhost", 5432, "user");
db.connect();

This separation of concerns offers several benefits:

  1. Library users have complete control over log handling
  2. Libraries can provide rich logging without imposing implementation details
  3. No risk of conflict with application logging configurations
  4. 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 context
const requestLogger = logger.with({
  requestId: "abc-123",
  userId: 42,
  endpoint: "/users"
});

// All logs from this logger include the context properties
requestLogger.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.

For more information and detailed documentation, visit LogTape's official website.

scy's avatar
scy

@scy@chaos.social · Reply to scy's post

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:

The 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)
        )
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) )
Roni Laukkarinen's avatar
Roni Laukkarinen

@rolle@mementomori.social

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.

Life 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 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
scy's avatar
scy

@scy@chaos.social

Python: *has very flexible logging system*

also Python: *has no format string placeholder for fractional seconds*

me, wanting a ISO 8601 compatible timestamp with milliseconds in his logs:

[Edit: fixed and improved version in the replies]

A 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")
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")
anna_lillith 🇺🇦🌱🐖's avatar
anna_lillith 🇺🇦🌱🐖

@anna_lillith@mas.to

This news is a devastating blow to conservation efforts.

Trump has ordered over 100 million hectares of forest to be chopped down. 

That's nearly three times the size of California.
ALT text detailsTrump has ordered over 100 million hectares of forest to be chopped down. That's nearly three times the size of California.
洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Just released 0.9.0! 🎉

This version brings two major improvements to our zero-dependency library for :

  1. 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!

  2. 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:

https://hackers.pub/@hongminhee/2025/logtape-090

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub

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.

import { configureSync, getConsoleSink } from "@logtape/logtape";

configureSync({ 
  sinks: {
    console: getConsoleSink(),
  },
  loggers: [
    {
      category: "my-app",
      lowestLevel: "info",
      sinks: ["console"],
    },
  ],
});

Console Sink Enhancements

Breaking Changes

File Sinks Moved to Separate Package: Better Cross-Platform Support

To improve runtime compatibility, file-related sinks have been moved to the @logtape/file package:

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:

// Before
import { getFileSink, getRotatingFileSink } from "@logtape/logtape";

// After
import { getFileSink, getRotatingFileSink } from "@logtape/file";

Don't forget to install the new package:

# For npm, pnpm, Yarn, Bun
npm add @logtape/file

# For Deno
deno 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.

Happy logging!

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Just released 0.9.0! 🎉

This version brings two major improvements to our zero-dependency library for :

  1. 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!

  2. 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:

https://hackers.pub/@hongminhee/2025/logtape-090

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub

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.

import { configureSync, getConsoleSink } from "@logtape/logtape";

configureSync({ 
  sinks: {
    console: getConsoleSink(),
  },
  loggers: [
    {
      category: "my-app",
      lowestLevel: "info",
      sinks: ["console"],
    },
  ],
});

Console Sink Enhancements

Breaking Changes

File Sinks Moved to Separate Package: Better Cross-Platform Support

To improve runtime compatibility, file-related sinks have been moved to the @logtape/file package:

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:

// Before
import { getFileSink, getRotatingFileSink } from "@logtape/logtape";

// After
import { getFileSink, getRotatingFileSink } from "@logtape/file";

Don't forget to install the new package:

# For npm, pnpm, Yarn, Bun
npm add @logtape/file

# For Deno
deno 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.

Happy logging!

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Just released 0.9.0! 🎉

This version brings two major improvements to our zero-dependency library for :

  1. 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!

  2. 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:

https://hackers.pub/@hongminhee/2025/logtape-090

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub

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.

import { configureSync, getConsoleSink } from "@logtape/logtape";

configureSync({ 
  sinks: {
    console: getConsoleSink(),
  },
  loggers: [
    {
      category: "my-app",
      lowestLevel: "info",
      sinks: ["console"],
    },
  ],
});

Console Sink Enhancements

Breaking Changes

File Sinks Moved to Separate Package: Better Cross-Platform Support

To improve runtime compatibility, file-related sinks have been moved to the @logtape/file package:

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:

// Before
import { getFileSink, getRotatingFileSink } from "@logtape/logtape";

// After
import { getFileSink, getRotatingFileSink } from "@logtape/file";

Don't forget to install the new package:

# For npm, pnpm, Yarn, Bun
npm add @logtape/file

# For Deno
deno 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.

Happy logging!

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Just released 0.9.0! 🎉

This version brings two major improvements to our zero-dependency library for :

  1. 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!

  2. 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:

https://hackers.pub/@hongminhee/2025/logtape-090

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub

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.

import { configureSync, getConsoleSink } from "@logtape/logtape";

configureSync({ 
  sinks: {
    console: getConsoleSink(),
  },
  loggers: [
    {
      category: "my-app",
      lowestLevel: "info",
      sinks: ["console"],
    },
  ],
});

Console Sink Enhancements

Breaking Changes

File Sinks Moved to Separate Package: Better Cross-Platform Support

To improve runtime compatibility, file-related sinks have been moved to the @logtape/file package:

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:

// Before
import { getFileSink, getRotatingFileSink } from "@logtape/logtape";

// After
import { getFileSink, getRotatingFileSink } from "@logtape/file";

Don't forget to install the new package:

# For npm, pnpm, Yarn, Bun
npm add @logtape/file

# For Deno
deno 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.

Happy logging!

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Haven't talked about LogTape in a while—it's a library I made for and . 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? has absolutely no external dependencies. Install it without worrying about bloating your node_modules.

Hierarchical categories

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.

Library-friendly

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.

Structured logging

Plain text logs not cutting it? LogTape supports structured logging. Makes log analysis way easier down the road.

Runs anywhere

Works smoothly in Node.js, Deno, Bun, browsers, and even edge functions. No special configuration needed.


Check out https://logtape.org/ if you're interested in learning more.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Haven't talked about LogTape in a while—it's a library I made for and . 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? has absolutely no external dependencies. Install it without worrying about bloating your node_modules.

Hierarchical categories

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.

Library-friendly

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.

Structured logging

Plain text logs not cutting it? LogTape supports structured logging. Makes log analysis way easier down the road.

Runs anywhere

Works smoothly in Node.js, Deno, Bun, browsers, and even edge functions. No special configuration needed.


Check out https://logtape.org/ if you're interested in learning more.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Haven't talked about LogTape in a while—it's a library I made for and . 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? has absolutely no external dependencies. Install it without worrying about bloating your node_modules.

Hierarchical categories

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.

Library-friendly

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.

Structured logging

Plain text logs not cutting it? LogTape supports structured logging. Makes log analysis way easier down the road.

Runs anywhere

Works smoothly in Node.js, Deno, Bun, browsers, and even edge functions. No special configuration needed.


Check out https://logtape.org/ if you're interested in learning more.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Haven't talked about LogTape in a while—it's a library I made for and . 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? has absolutely no external dependencies. Install it without worrying about bloating your node_modules.

Hierarchical categories

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.

Library-friendly

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.

Structured logging

Plain text logs not cutting it? LogTape supports structured logging. Makes log analysis way easier down the road.

Runs anywhere

Works smoothly in Node.js, Deno, Bun, browsers, and even edge functions. No special configuration needed.


Check out https://logtape.org/ if you're interested in learning more.

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Haven't talked about LogTape in a while—it's a library I made for and . 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? has absolutely no external dependencies. Install it without worrying about bloating your node_modules.

Hierarchical categories

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.

Library-friendly

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.

Structured logging

Plain text logs not cutting it? LogTape supports structured logging. Makes log analysis way easier down the road.

Runs anywhere

Works smoothly in Node.js, Deno, Bun, browsers, and even edge functions. No special configuration needed.


Check out https://logtape.org/ if you're interested in learning more.

Tuomas Tammisto's avatar
Tuomas Tammisto

@tutam@fediscience.org

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!)

hup.fi/site/books/m/10.33134/H

"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."

@anthropology

Cover 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".
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".
洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@fosstodon.org · Reply to 洪 民憙 (Hong Minhee)'s post

0.7.0をリリースしました!

新機能の暗黙的コンテキストにより、リクエストID、ユーザーIDなどの文脈情報を、手動で渡すことなくログに自動的に含められるようになりました。

詳しくは記事をご覧ください:

zenn.dev/hongminhee/articles/3

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@fosstodon.org

Just released 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:

dev.to/hongminhee/introducing-

Bentley's avatar
Bentley

@Bentley@canada.masto.host

Activists shared a win with the 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 .

PODCAST: podcastics.com/episode/309464/

2 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
2 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
2 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
洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@fosstodon.org

Version 0.5.0 of , the zero-dependency library for , .js, , edge functions, and browsers, has been released! The main additions include:

• Contexts
• ANSI color formatter
• Comprehensive docs
• A few API conveniences

LogTape v0.5.0 is available from JSR and npm:

• JSR: jsr.io/@logtape/logtape@0.5.0
• npm: npmjs.com/package/@logtape/log

In addition, a new website with documentation has been launched, so please check it out!

logtape.org/

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@fosstodon.org

In the next version of , a zero-dependency library for & , 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.

• JSR: jsr.io/@logtape/logtape@0.5.0-
• npm: npmjs.com/package/@logtape/log

github.com/dahlia/logtape#cont

const 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}.");
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}.");
lynn~>'s avatar
lynn~>

@lynn_sh@tech.lgbt

does anyone know where userspace programs are supposed to store logs? /var/log/ is not acceptable because i don't wish to ever interact with root

João Pinheiro's avatar
João Pinheiro

@joaopinheiro@kolektiva.social

New Images Show Largest Uncontacted Indigenous Tribe on Earth Dangerously Close to Loggers in Peruvian Amazon
ecowatch.com/indigenous-tribe-

洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@fosstodon.org

Released v0.4.2 of , which is a zero-dependency library for & ! Since this version, it work well on Workers out of box!

• JSR: jsr.io/@logtape/logtape@0.4.2
• npm: npmjs.com/package/@logtape/log

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

offers robust logging capabilities through integration with LogTape. This feature allows you to easily debug and monitor your Fedify app!

To enable , simply install the @logtape/logtape package and configure it in your app's entry point:

import { configure, getConsoleSink } from "@logtape/logtape";

await configure({
  sinks: { console: getConsoleSink() },
  filters: {},
  loggers: [
    { category: "your-app", sinks: ["console"], level: "debug" },
    { category: "fedify",   sinks: ["console"], level: "info" },
  ],
});

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 integration, you gain valuable insights into your Fedify app's behavior, making troubleshooting and optimization much more straightforward!

https://fedify.dev/manual/log

log4jm's avatar
log4jm

@log4jmc@infosec.exchange

belated #Introduction

I've been enjoying infosec.exchange for the last month or so but have been putting off an because I'm awkward and anxious ( 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 . I'm an IT and focused () 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 ? 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 and news I think to share.

If I had to sum up in a few hashtags and such, I know securing and best but I use/protect and if you'll forgive me for using there too. I love and , the and and we share, and stuff, and , and reading/writing reports just as much as code. I'm not super passionate about the but that's not a hill I'd die on and is pretty cool.

Did I mention I have one of the best ever?

Anyway, "it's me, hi!"