Hello, I'm an open source software engineer in my late 30s living in #Seoul, #Korea, and an avid advocate of #FLOSS and the #fediverse.
I'm the creator of @fedify, an #ActivityPub server framework in #TypeScript, @hollo, an ActivityPub-enabled microblogging software for single users, and @botkit, a simple ActivityPub bot framework.
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)
Fedify는 #ActivityPub 기반 연합형 서버 프레임워크로, 개발자들이 분산형 소셜 네트워크인 #연합우주(#fediverse)에 애플리케이션을 쉽게 통합할 수 있도록 돕습니다. 복잡한 ActivityPub 프로토콜 구현을 단순화하여 개발 시간을 크게 단축시킵니다. MIT 라이선스 하에 제공되는 오픈 소스 프로젝트입니다.
💼 Fedify를 활용하는 프로젝트들
다양한 프로젝트들이 이미 Fedify를 활용하고 있습니다:
Ghost: 수백만 사용자를 보유한 전문적인 오픈 소스(MIT 라이선스) 퍼블리싱 플랫폼으로, Fedify의 주요 후원사이자 파트너입니다.
Fedify is looking for new partnership opportunities!
What is Fedify?
#Fedify is an #ActivityPub-based federated server framework that helps developers easily integrate their applications with the #fediverse, a decentralized social network. It simplifies the complex implementation of the ActivityPub protocol, significantly reducing development time. Fedify is an open-source project available under the MIT license.
💼 Projects using Fedify
Various projects are already leveraging Fedify:
Ghost: A professional publishing platform with millions of users, open source under MIT license, and a major sponsor and partner of Fedify.
Hollo: A lightweight microblogging platform for individual users (open source, AGPL-3.0)
Hackers' Pub: A fediverse blogging platform for software engineers (open source, AGPL-3.0)
Encyclia: A bridge service that makes ORCID academic records available via ActivityPub
🚀 Value provided by Fedify
80% development time reduction: Utilize a proven framework instead of complex ActivityPub implementation
Immediate fediverse compatibility: Instant compatibility with various fediverse services including Mastodon, Misskey, Pleroma, Pixelfed, PeerTube, etc.
Expert technical support: Direct support from ActivityPub and Federation protocol experts
Custom development: Tailored feature development to meet your specific requirements
🤝 Potential collaboration models
Custom consulting and integration support: Professional assistance for integrating Fedify into your platform
Custom feature development: Development and implementation of specific features needed for your platform
Long-term technical partnership: Long-term collaboration for continuous development and maintenance
🌟 Benefits of collaborating with Fedify
Technical advantage: Save time and resources compared to in-house implementation
Brand image: Enhance corporate image through support of the open-source ecosystem
Entry to decentralized social networks: Easily participate in the fediverse ecosystem
Competitive edge: Strengthen product competitiveness through social features
📩 Interested?
If you're considering implementing ActivityPub or wish to collaborate with the Fedify project, please get in touch:
Even small contributions help sustain development and show there's interest in open source #ActivityPub tools. Thanks to our existing sponsors who've already been helping move the project forward!
The goal remains the same: make building federated applications more accessible to developers so the #fediverse can continue to grow and thrive.
Just received word that @fedify wasn't selected for @nlnet's Open Call this round. While disappointing, I understand the competition was fierce with many worthy projects seeking limited funding.
The journey continues though—#Fedify development will move forward at its own pace. Thanks to everyone who's shown interest and support for this project so far. Building tools for the #fediverse remains important work, and I'm committed to seeing it through.
If you know of other funding opportunities that might be a good fit for open source #ActivityPub tools, I'm all ears.
Even small contributions help sustain development and show there's interest in open source #ActivityPub tools. Thanks to our existing sponsors who've already been helping move the project forward!
The goal remains the same: make building federated applications more accessible to developers so the #fediverse can continue to grow and thrive.
Just received word that @fedify wasn't selected for @nlnet's Open Call this round. While disappointing, I understand the competition was fierce with many worthy projects seeking limited funding.
The journey continues though—#Fedify development will move forward at its own pace. Thanks to everyone who's shown interest and support for this project so far. Building tools for the #fediverse remains important work, and I'm committed to seeing it through.
If you know of other funding opportunities that might be a good fit for open source #ActivityPub tools, I'm all ears.
I implemented data redaction for #LogTape today. The feature will be available through a dedicated package called @logtape/redaction (JSR & npm). It provides two complementary approaches to protect sensitive information in your logs:
Pattern-based redaction: Uses regex patterns to identify and mask sensitive data in formatted log output
This proposal describes an ActivityPub extension to allow actors to publish a short status text, with optional expiration, link attachment, and history.
Some centralized communication services provide their users with the ability to set a status on their account, which is usually displayed on their profile and sometimes next to their name in other places in the UI. These are distinct from regular posts because they can not be interacted with in any way whatsoever, can't contain media attachments, and usually have a short character limit on the order of several hundred characters at most. Statuses are always visible to anyone who can see the actor itself.
In the diverse and ever-evolving JavaScript ecosystem, logging remains a critical component for development, debugging, and monitoring applications. While numerous logging libraries exist, LogTape stands out with its unique combination of simplicity, flexibility, and cross-runtime compatibility. Let's explore why LogTape deserves consideration for your next JavaScript or TypeScript project—whether you're building an application or a library.
Zero Dependencies: A Lightweight Footprint
One of LogTape's most compelling features is its complete absence of dependencies. In an era where “dependency hell” plagues many JavaScript projects, LogTape offers a refreshing alternative:
// No additional packages to install beyond LogTape itselfimport { configure, getConsoleSink, getLogger } from "@logtape/logtape";
This zero-dependency approach provides several advantages:
Reduced bundle size
No transitive dependencies means smaller packages
Enhanced stability
No risk of breaking changes from upstream dependencies
Simplified security
Fewer potential vulnerabilities from third-party code
Lower integration overhead
Particularly valuable for library authors who don't want to burden users with additional dependencies
Runtime Diversity: Write Once, Log Everywhere
While many popular logging libraries focus primarily on Node.js, LogTape provides seamless support across diverse JavaScript runtimes:
This runtime flexibility means you can use consistent logging patterns regardless of your deployment environment:
// Same API works seamlessly across all JavaScript runtimesimport { getLogger } from "@logtape/logtape";const logger = getLogger(["my-service", "user-management"]);// Works in Node.js, Deno, Bun, browsers, or edge functionslogger.info`User ${userId} logged in successfully`;
For teams working across multiple platforms or projects transitioning between runtimes, this consistency is invaluable. No need to learn different logging libraries or approaches—LogTape works the same way everywhere.
Hierarchical Categories: Fine-Grained Control
LogTape's hierarchical category system represents a standout feature that's surprisingly rare among JavaScript logging libraries. Categories allow you to organize logs in a tree-like structure:
This hierarchical approach offers powerful benefits:
Targeted filtering
Configure different log levels for different parts of your application
Inheritance
Child loggers inherit settings from parents, reducing configuration overhead
Organizational clarity
Logs naturally follow your application's module structure
Here's how you might configure logging levels for different categories:
await configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log"), }, loggers: [ // Base configuration for all app logs { category: ["my-app"], lowestLevel: "info", sinks: ["console", "file"] }, // More verbose logging just for database components { category: ["my-app", "database"], lowestLevel: "debug", sinks: ["file"] } ]});
With this configuration, all application logs at "info" level and above go to both console and file, while database-specific logs include more detailed "debug" level information, but only in the log file.
Structured Logging: Beyond Simple Text
Modern logging goes beyond simple text strings. LogTape embraces structured logging, which treats log entries as data objects rather than plain text:
The function is only evaluated if the debug level is enabled, preventing unnecessary computation for suppressed log levels.
Extremely Simple Sinks and Filters: Minimal Boilerplate
LogTape's approach to extensibility is remarkably straightforward. Creating custom sinks (output destinations) and filters requires minimal boilerplate code.
Dead Simple Sinks
A sink in LogTape is just a function that receives a log record:
// Creating a custom sink is as simple as defining a functionconst mySink = (record) => { const timestamp = new Date(record.timestamp).toISOString(); const level = record.level.toUpperCase(); const category = record.category.join('.'); // Send to your custom destination myCustomLogService.send({ time: timestamp, priority: level, component: category, message: record.message, ...record.properties });};// Use your custom sink in configurationawait configure({ sinks: { console: getConsoleSink(), custom: mySink }, loggers: [ { category: ["my-app"], sinks: ["console", "custom"] } ]});
Compare this with other libraries that require extending classes, implementing multiple methods, or following specific patterns. LogTape's approach is refreshingly straightforward.
Simple Filters
Similarly, filters in LogTape are just functions that return a Boolean:
// Filter that only passes high-priority or specific component logsconst importantLogsFilter = (record) => { // Always include errors if (record.level === "error" || record.level === "fatal") { return true; } // Always include payment-related logs if (record.category.includes("payments")) { return true; } // Filter out other logs return false;};await configure({ // ...sinks configuration filters: { important: importantLogsFilter }, loggers: [ { category: ["my-app"], sinks: ["alertSystem"], filters: ["important"] } ]});
LogTape also provides a convenient shorthand for level-based filtering:
await configure({ // ...sinks configuration filters: { // This creates a filter for "warning" level and above warningAndAbove: "warning" }, loggers: [ { category: ["my-app"], sinks: ["console"], filters: ["warningAndAbove"] } ]});
Perfect for Library Authors
LogTape is uniquely well-suited for library authors who want to incorporate logging without burdening their users. The core philosophy is simple:
The key point is that the library never calls configure(). Instead, it provides useful log output points with appropriate levels and contextual data.
Applications using the library can then decide exactly how to handle these logs:
// Application codeimport { configure, getConsoleSink } from "@logtape/logtape";import { Database } from "my-awesome-lib";// Configure how logs should be handledawait configure({ sinks: { console: getConsoleSink(), file: getFileSink("app.log") }, loggers: [ // Handle all library logs { category: ["my-awesome-lib"], lowestLevel: "info", sinks: ["file"] }, // More verbose for database component during development { category: ["my-awesome-lib", "database"], lowestLevel: "debug", sinks: ["console", "file"] } ]});// Use the libraryconst db = new Database("localhost", 5432, "user");db.connect();
This separation of concerns offers several benefits:
Library users have complete control over log handling
Libraries can provide rich logging without imposing implementation details
No risk of conflict with application logging configurations
Libraries can be "noisy" internally while allowing applications to filter as needed
Contexts for Richer Logging
LogTape provides context mechanisms for adding consistent properties across multiple log messages. This is particularly valuable for tracing requests through a system:
Explicit Contexts
const logger = getLogger(["my-app", "api"]);// Create a logger with contextconst requestLogger = logger.with({ requestId: "abc-123", userId: 42, endpoint: "/users"});// All logs from this logger include the context propertiesrequestLogger.info("Processing request");requestLogger.debug("Validating input");requestLogger.info("Request completed", { durationMs: 120 });
Implicit Contexts (v0.7.0+)
For cases where you want context to apply across function calls without explicit passing:
import { getLogger, withContext } from "@logtape/logtape";function handleRequest(req, res) { withContext({ requestId: req.id, userId: req.user?.id }, () => { // All logs within this function and any functions it calls // will automatically include the context properties processRequest(req, res); });}function processRequest(req, res) { // No need to pass context - it's automatically available getLogger(["my-app", "processor"]).info("Processing data"); // Call other functions that will also inherit the context validateInput(req.body);}function validateInput(data) { // This log also gets the requestId and userId getLogger(["my-app", "validator"]).debug("Validating input", { data });}
This implicit context capability is invaluable for tracing requests through multiple layers of code without manually threading context through every function call.
When LogTape Might Not Be Your Best Choice
While LogTape offers compelling advantages for many use cases, it's not universally the best choice:
Extreme performance requirements
If your application logs tens of thousands of entries per second and raw performance is the top priority, specialized high-performance libraries like Pino may be more suitable with their focus on optimized logging throughput.
Extensive pre-built integrations
If you need immediate integration with numerous specific systems (Elasticsearch, Graylog, etc.) without writing any custom code, Winston's rich ecosystem of transports might provide a faster starting point.
Legacy systems with specific logging requirements
If you're maintaining systems built around specific logging patterns from Java or other environments, purpose-built libraries like Log4js might offer more familiar APIs.
Web browser-only applications with minimal logging needs
For extremely simple web browser-only logging needs where you just want basic console output with levels, even simpler libraries like loglevel might be sufficient.
Conclusion
LogTape stands out in the crowded JavaScript logging landscape by offering a unique combination of features that address real-world development challenges:
Zero dependencies for a lightweight, secure foundation
Runtime diversity supporting Node.js, Deno, Bun, browsers, and edge functions
Hierarchical categories for better log organization and filtering
Structured logging for improved analysis and searchability
Simple extension mechanisms with minimal boilerplate
Library-friendly design that respects separation of concerns
Whether you're building applications or libraries, working across multiple JavaScript runtimes, or simply seeking a clean, well-designed logging solution, LogTape deserves serious consideration. Its thoughtful design balances simplicity with powerful features, avoiding common pitfalls of JavaScript logging libraries.
Deno 2.3.3 is out 🎊 ⭐ deno serve any directory ⭐ fetch over Unix sockets ⭐ new OTel events: boot_failure and uncaught_exception ⭐ dark mode on HTML coverage report
I am very happy that today we merged the pull request switching the Mastodon frontend build chain from the very old (and outdated) Webpack 4 to @vite ⚡️
It is not visible to end-users but it will greatly improve the developer experience with working on the Mastodon frontend and opens the door for many good things.
I started the initial work 2 years ago, then @chaosexanima took over and got it over the finish line 🚀