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

洪 民憙 (Hong Minhee) :nonbinary:

@hongminhee@hollo.social · 977 following · 1331 followers

An intersectionalist, feminist, and socialist living in Seoul (UTC+09:00). @tokolovesme's spouse. Who's behind @fedify, @hollo, and @botkit. Write some free software in , , , & . They/them.

서울에 사는 交叉女性主義者이자 社會主義者. 金剛兔(@tokolovesme)의 配偶者. @fedify, @hollo, @botkit 메인테이너. , , , 等으로 自由 소프트웨어 만듦.

()

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

@hongminhee@hollo.social

Hello, I'm an open source software engineer in my late 30s living in , , and an avid advocate of and the .

I'm the creator of @fedify, an server framework in , @hollo, an ActivityPub-enabled microblogging software for single users, and @botkit, a simple ActivityPub bot framework.

I'm also very interested in East Asian languages (so-called ) and . Feel free to talk to me in , (), or (), or even in Literary Chinese (, )!

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

@hongminhee@hollo.social · Reply to 洪 民憙 (Hong Minhee) :nonbinary:'s post

安寧(안녕)하세요, 저는 서울에 살고 있는 30() 後半(후반) 오픈 소스 소프트웨어 엔지니어이며, 自由(자유)·오픈 소스 소프트웨어와 聯合宇宙(연합우주)(fediverse)의 熱烈(열렬)支持者(지지자)입니다.

저는 TypeScript() ActivityPub 서버 프레임워크인 @fedify 프로젝트와 싱글 유저() ActivityPub 마이크로블로그인 @hollo 프로젝트와 ActivityPub 봇 프레임워크인 @botkit 프로젝트의 製作者(제작자)이기도 합니다.

저는 ()아시아 言語(언어)(이른바 )와 유니코드에도 關心(관심)이 많습니다. 聯合宇宙(연합우주)에서는 國漢文混用體(국한문 혼용체)를 쓰고 있어요! 제게 韓國語(한국어)英語(영어), 日本語(일본어)로 말을 걸어주세요. (아니면, 漢文(한문)으로도!)

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

@hongminhee@hollo.social · Reply to 洪 民憙 (Hong Minhee) :nonbinary:'s post

こんにちは、私はソウルに住んでいる30代後半のオープンソースソフトウェアエンジニアで、自由・オープンソースソフトウェアとフェディバースの熱烈な支持者です。名前は洪 民憙ホン・ミンヒです。

私はTypeScript用のActivityPubサーバーフレームワークである「@fedify」と、ActivityPubをサポートする1人用マイクロブログである 「@hollo」と、ActivityPubのボットを作成する為のシンプルなフレームワークである「@botkit」の作者でもあります。

私は東アジア言語(いわゆるCJK)とUnicodeにも興味が多いです。日本語、英語、韓国語で話しかけてください。(または、漢文でも!)

Gregory's avatar
Gregory

@grishka@socialhub.activitypub.rocks

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.

洪 民憙 (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

조건문은 함수 밖으로 빼고, 반복문은 함수 안으로 넣기—좋은 경험칙 같다.

Deno's avatar
Deno

@deno_land@fosstodon.org

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

github.com/denoland/deno/relea

Emelia 👸🏻's avatar
Emelia 👸🏻

@thisismissem@hachyderm.io

Spent some time today updating parts of the @nivenly website, e.g., the list of projects on our homepage: nivenly.org/

Also moved the information on the Fediverse Security Fund to a dedicated page, outside of the blog post.

Emelia 👸🏻's avatar
Emelia 👸🏻

@thisismissem@hachyderm.io

This is fantastic work from the @piefed_meta team: peertube.wtf/w/8b6wa113MuekvbF

Blocking images via PDQ hash

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

@hongminhee@hollo.social

Anyone here have experience using @vitest with @deno_land, or setting up a unit test suite that works on , .js, and ?

Renaud Chaput's avatar
Renaud Chaput

@renchap@oisaur.com

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 🚀

github.com/mastodon/mastodon/p

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

@hongminhee@hackers.pub

MoonBit에서 가상 패키지라는 개념을 도입했는데, 내가 몇 해 전에 상생했던 것과 거의 같아서 신기하다.

Lobsters

@lobsters@mastodon.social

Introducing virtual package in MoonBit lobste.rs/s/cfiux3
moonbitlang.com/blog/virtual-p

Lobsters

@lobsters@mastodon.social

Introducing Pyrefly: A new type checker and IDE experience for Python lobste.rs/s/czvto8
engineering.fb.com/2025/05/15/

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

@hongminhee@hackers.pub

Meta introduced Pyrefly, a new type checker and IDE experience for Python.

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

@hongminhee@hollo.social · Reply to Sven Slootweg's post

@joepie91 I'm maintaining several libraries and some of them have their official fediverse accounts. If you're curious check them out!

  • @fedify: Fedify, an ActivityPub server framework
  • @botkit: BotKit, a fediverse bot framework
robin's avatar
robin

@robin_maki@planet.moe

한자문화권은 사기군... 딱히 중국어를 배운 적이 없는데 한자 부수랑 발음으로 찍어서 1챕터 스킵 깨고 듀오링고 10점이 됨

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

@hongminhee@hollo.social · Reply to @reiver ⊼ (Charles) :batman:'s post

@reiver Fedify and Hollo also have SVG icons!

@reiver ⊼ (Charles) :batman:'s avatar
@reiver ⊼ (Charles) :batman:

@reiver@mastodon.social

Fediverse Icons

github.com/reiver/fediverse-ic

Free, and in SVG format.

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

@hongminhee@hollo.social · Reply to Sven Slootweg's post

@joepie91 Things that open source projects which have their official X (formerly Twitter) account usually post on their account? News, their roadmaps, questions and answers, and so on!

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

@hongminhee@hollo.social · Reply to Sven Slootweg's post

@joepie91 Software libraries!

Fedify: ActivityPub server framework's avatar
Fedify: ActivityPub server framework

@fedify@hollo.social

Good news! We've officially added support to the roadmap. We've created a detailed issue to track our implementation plan: https://github.com/fedify-dev/fedify/issues/233.

The effort will be tackled in phases, including compatibility assessment, core adaptations for Workers' environment, KV store and message queue implementations, and finally integration with Cloudflare's ecosystem. This will be a substantial project that we'll break down into several sub-issues.

If you're interested in contributing to any specific aspect of Workers support, please comment on the main issue to coordinate efforts.

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

@hongminhee@hollo.social · Reply to 洪 民憙 (Hong Minhee) :nonbinary:'s post

Or at least, I wish they'd create a Bluesky account and turn on Bridgy Fed.

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

@hongminhee@hollo.social

I wish all the library projects I use had official fediverse accounts so I could follow them.

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

@hongminhee@hollo.social · Reply to Woojin Kim's post

@me 그냥 접근성 고려 전반을 Apple 제품들이 다른 곳들보다 더 많이 하는 것 같긴 해요.

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

@hongminhee@hollo.social

()은 Fedify에 굵직한 課題(과제)들이 좀 있는데 OSS 컨트리뷰션 아카데미에서 進行(진행)하려고 내가 안 解決(해결)하고 아껴두고 있다.

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

@hongminhee@hollo.social

@hollo 음, 이상하네요. 한 번 팔로 풀었다가 다시 팔로해 보시면…

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

@hongminhee@hollo.social

@hollo 계정 생성한 지 2주 지나야 될 겁니다.

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

@hongminhee@hackers.pub

If a module defines a custom exception type and throws that exception within the module, it absolutely must export that exception type as well. I'd think this is basic, but it seems a surprising number of packages declare exception types without exporting them.

Deno's avatar
Deno

@deno_land@fosstodon.org

What's up with Fresh? 🍋

deno.com/blog/an-update-on-fre

もちもちずきん 🍆's avatar
もちもちずきん 🍆

@Yohei_Zuho@mstdn.y-zu.org

Fedify本、TPM入稿だん!

juxtapose's avatar
juxtapose

@xt@hackers.pub · Reply to juxtapose's post

I mean you've done all the hard work for free open decentralized social networking platform right?

and you're like one inch centimeter away from branding yourself this

MSDN Magazine, June 2004
ALT text detailsMSDN Magazine, June 2004
juxtapose's avatar
juxtapose

@xt@hackers.pub

I'm not happy with how Mastodon abbreviates Mastodon to mstdn. For me it looks like Microsoft Technical Developer Network or something

msdn
ALT text detailsmsdn
← Newer
Older →