English:
When I started building Fedify, an ActivityPub server framework, I ran into a problem that surprised me: I couldn't figure out how to add logging.
Not because logging is hard—there are dozens of mature logging libraries for JavaScript. The problem was that they're primarily designed for applications, not for libraries that want to stay unobtrusive.
I wrote about this a few months ago, and the response was modest—some interest, some skepticism, and quite a bit of debate about whether the post was AI-generated. I'll be honest: English isn't my first language, so I use LLMs to polish my writing. But the ideas and technical content are mine.
Several readers wanted to see a real-world example rather than theory.
The problem: existing loggers assume you're building an app
Fedify helps developers build federated social applications using the ActivityPub protocol. If you've ever worked with federation, you know debugging can be painful. When an activity fails to deliver, you need to answer questions like:
- Did the HTTP request actually go out?
- Was the signature generated correctly?
- Did the remote server reject it? Why?
- Was there a problem parsing the response?
These questions span multiple subsystems: HTTP handling, cryptographic signatures, JSON-LD processing, queue management, and more. Without good logging, debugging turns into guesswork.
But here's the dilemma I faced as a library author: if I add verbose logging to help with debugging, I risk annoying users who don't want their console cluttered with Fedify's internal chatter. If I stay silent, users struggle to diagnose issues.
I looked at the existing options. With winston or Pino, I would have to either:
- Configure a logger inside Fedify (imposing my choices on users), or
- Ask users to pass a logger instance to Fedify (adding boilerplate)
There's also debug, which is designed for this use case. But it doesn't give you structured, level-based logs that ops teams expect—and it relies on environment variables, which some runtimes like Deno restrict by default for security reasons.
None of these felt right. So I built LogTape—a logging library designed from the ground up for library authors. And Fedify became its first real user.
The solution: hierarchical categories with zero default output
The key insight was simple: a library should be able to log without producing any output unless the application developer explicitly enables it.
Fedify uses LogTape's hierarchical category system to give users fine-grained control over what they see. Here's how the categories are organized:
| Category |
What it logs |
["fedify"] |
Everything from the library |
["fedify", "federation", "inbox"] |
Incoming activities |
["fedify", "federation", "outbox"] |
Outgoing activities |
["fedify", "federation", "http"] |
HTTP requests and responses |
["fedify", "sig", "http"] |
HTTP Signature operations |
["fedify", "sig", "ld"] |
Linked Data Signature operations |
["fedify", "sig", "key"] |
Key generation and retrieval |
["fedify", "runtime", "docloader"] |
JSON-LD document loading |
["fedify", "webfinger", "lookup"] |
WebFinger resource lookups |
…and about a dozen more. Each category corresponds to a distinct subsystem.
This means a user can configure logging like this:
await configure({
sinks: { console: getConsoleSink() },
loggers: [
// Show errors from all of Fedify
{ category: "fedify", sinks: ["console"], lowestLevel: "error" },
// But show debug info for inbox processing specifically
{ category: ["fedify", "federation", "inbox"], sinks: ["console"], lowestLevel: "debug" },
],
});
When something goes wrong with incoming activities, they get detailed logs for that subsystem while keeping everything else quiet. No code changes required—just configuration.
Request tracing with implicit contexts
The hierarchical categories solved the filtering problem, but there was another challenge: correlating logs across async boundaries.
In a federated system, a single user action might trigger a cascade of operations: fetch a remote actor, verify their signature, process the activity, fan out to followers, and so on. When something fails, you need to correlate all the log entries for that specific request.
Fedify uses LogTape's implicit context feature to automatically tag every log entry with a requestId:
await configure({
sinks: {
file: getFileSink("fedify.jsonl", { formatter: jsonLinesFormatter })
},
loggers: [
{ category: "fedify", sinks: ["file"], lowestLevel: "info" },
],
contextLocalStorage: new AsyncLocalStorage(), // Enables implicit contexts
});
With this configuration, every log entry automatically includes a requestId property. When you need to debug a specific request, you can filter your logs:
jq 'select(.properties.requestId == "abc-123")' fedify.jsonl
And you'll see every log entry from that request—across all subsystems, all in order. No manual correlation needed.
The requestId is derived from standard headers when available (X-Request-Id, Traceparent, etc.), so it integrates naturally with existing observability infrastructure.
What users actually see
So what does all this configuration actually mean for someone using Fedify?
If a Fedify user doesn't configure LogTape at all, they see nothing. No warnings about missing configuration, no default output, and minimal performance overhead—the logging calls are essentially no-ops.
For basic visibility, they can enable error-level logging for all of Fedify with three lines of configuration. When debugging a specific issue, they can enable debug-level logging for just the relevant subsystem.
And if they're running in production with serious observability requirements, they can pipe structured JSON logs to their monitoring system with request correlation built in.
The same library code supports all these scenarios—whether the user is running on Node.js, Deno, Bun, or edge functions, without extra polyfills or shims. The user decides what they need.
Lessons learned
Building Fedify with LogTape taught me a few things:
Design your categories early. The hierarchical structure should reflect how users will actually want to filter logs. I organized Fedify's categories around subsystems that users might need to debug independently.
Use structured logging. Properties like requestId, activityId, and actorId are far more useful than string interpolation when you need to analyze logs programmatically.
Implicit contexts turned out to be more useful than I expected. Being able to correlate logs across async boundaries without passing context manually made debugging distributed operations much easier. When a user reports that activity delivery failed, I can give them a single jq command to extract everything relevant.
Trust your users. Some library authors worry about exposing too much internal detail through logs. I've found the opposite—users appreciate being able to see what's happening when they need to. The key is making it opt-in.
Try it yourself
If you're building a library and struggling with the logging question—how much to log, how to give users control, how to avoid being noisy—I'd encourage you to look at how Fedify does it.
The Fedify logging documentation explains everything in detail. And if you want to understand the philosophy behind LogTape's design, my earlier post covers that.
LogTape isn't trying to replace winston or Pino for application developers who are happy with those tools. It fills a different gap: logging for libraries that want to stay out of the way until users need them. If that's what you're looking for, it might be a better fit than the usual app-centric loggers.
한국어:
Fedify, ActivityPub 서버 프레임워크를 개발하기 시작했을 때, 의외의 문제에 부딪혔습니다: 로깅을 추가하는 방법을 찾지 못했습니다.
로깅 자체가 어려워서가 아닙니다—JavaScript용 성숙한 로깅 라이브러리는 수십 개가 있습니다. 문제는 이들이 주로 애플리케이션을 위해 설계되었지, 방해가 되지 않기를 원하는 라이브러리를 위한 것이 아니라는 점이었습니다.
저는 몇 달 전에 이에 대해 글을 썼고, 반응은 소박했습니다—약간의 관심, 약간의 회의론, 그리고 이 글이 AI로 생성되었는지에 대한 꽤 많은 논쟁이 있었습니다. 솔직히 말하자면: 영어는 제 모국어가 아니기 때문에 글을 다듬기 위해 LLM을 사용합니다. 하지만 아이디어와 기술적 내용은 제 것입니다.
몇몇 독자들은 이론보다 실제 사례를 보고 싶어했습니다.
문제: 기존 로거들은 앱을 만든다고 가정합니다
Fedify는 개발자들이 ActivityPub 프로토콜을 사용하여 연합형 소셜 애플리케이션을 구축하는 데 도움을 줍니다. 연합(federation)과 작업해 본 적이 있다면, 디버깅이 얼마나 고통스러울 수 있는지 아실 겁니다. 활동(activity) 전달이 실패했을 때, 다음과 같은 질문에 답해야 합니다:
- HTTP 요청이 실제로 나갔나요?
- 서명이 올바르게 생성되었나요?
- 원격 서버가 이를 거부했나요? 왜 그랬나요?
- 응답 파싱에 문제가 있었나요?
이러한 질문들은 여러 하위 시스템에 걸쳐 있습니다: HTTP 처리, 암호화 서명, JSON-LD 처리, 큐 관리 등. 좋은 로깅 없이는 디버깅이 추측 게임이 됩니다.
하지만 라이브러리 작성자로서 제가 직면한 딜레마는 이것이었습니다: 디버깅을 돕기 위해 상세한 로깅을 추가하면, 콘솔이 Fedify의 내부 잡담으로 어지러워지는 것을 원치 않는 사용자들을 짜증나게 할 위험이 있습니다. 침묵을 지키면, 사용자들은 문제를 진단하는 데 어려움을 겪습니다.
기존 옵션들을 살펴봤습니다. winston이나 Pino를 사용하면 다음 중 하나를 해야 했습니다:
- Fedify 내부에 로거를 구성하거나(사용자에게 내 선택을 강요), 또는
- 사용자에게 로거 인스턴스를 Fedify에 전달하도록 요청(상용구 코드 추가)
또한 debug도 있는데, 이는 이런 사용 사례를 위해 설계되었습니다. 하지만 운영 팀이 기대하는 구조화된, 레벨 기반 로그를 제공하지 않습니다—그리고 환경 변수에 의존하는데, Deno와 같은 일부 런타임은 보안상의 이유로 기본적으로 이를 제한합니다.
이 중 어느 것도 적합하지 않았습니다. 그래서 저는 LogTape—라이브러리 작성자를 위해 처음부터 설계된 로깅 라이브러리를 만들었습니다. 그리고 Fedify는 그 첫 번째 실제 사용자가 되었습니다.
해결책: 기본 출력이 없는 계층적 카테고리
핵심 통찰은 간단했습니다: 라이브러리는 애플리케이션 개발자가 명시적으로 활성화하지 않는 한 어떤 출력도 생성하지 않고 로깅할 수 있어야 합니다.
Fedify는 LogTape의 계층적 카테고리 시스템을 사용하여 사용자에게 보고 싶은 것에 대한 세밀한 제어 권한을 제공합니다. 카테고리는 다음과 같이 구성되어 있습니다:
| 카테고리 |
로깅 내용 |
["fedify"] |
라이브러리의 모든 것 |
["fedify", "federation", "inbox"] |
수신 활동 |
["fedify", "federation", "outbox"] |
발신 활동 |
["fedify", "federation", "http"] |
HTTP 요청 및 응답 |
["fedify", "sig", "http"] |
HTTP 서명 작업 |
["fedify", "sig", "ld"] |
링크드 데이터 서명 작업 |
["fedify", "sig", "key"] |
키 생성 및 검색 |
["fedify", "runtime", "docloader"] |
JSON-LD 문서 로딩 |
["fedify", "webfinger", "lookup"] |
WebFinger 리소스 조회 |
...그리고 약 십여 개 더 있습니다. 각 카테고리는 별개의 하위 시스템에 해당합니다.
이는 사용자가 다음과 같이 로깅을 구성할 수 있음을 의미합니다:
await configure({
sinks: { console: getConsoleSink() },
loggers: [
// Fedify의 모든 오류 표시
{ category: "fedify", sinks: ["console"], lowestLevel: "error" },
// 하지만 특별히 inbox 처리에 대한 디버그 정보 표시
{ category: ["fedify", "federation", "inbox"], sinks: ["console"], lowestLevel: "debug" },
],
});
수신 활동에 문제가 생기면, 다른 모든 것은 조용히 유지하면서 해당 하위 시스템에 대한 상세한 로그를 얻을 수 있습니다. 코드 변경이 필요 없습니다—단지 구성만 필요합니다.
암시적 컨텍스트를 통한 요청 추적
계층적 카테고리는 필터링 문제를 해결했지만, 또 다른 과제가 있었습니다: 비동기 경계를 넘어 로그를 연관시키는 것입니다.
연합 시스템에서는 단일 사용자 작업이 일련의 작업을 촉발할 수 있습니다: 원격 액터 가져오기, 서명 확인, 활동 처리, 팔로워에게 전파 등. 무언가 실패했을 때, 해당 특정 요청에 대한 모든 로그 항목을 연관시켜야 합니다.
Fedify는 LogTape의 암시적 컨텍스트 기능을 사용하여 모든 로그 항목에 자동으로 requestId를 태그합니다:
await configure({
sinks: {
file: getFileSink("fedify.jsonl", { formatter: jsonLinesFormatter })
},
loggers: [
{ category: "fedify", sinks: ["file"], lowestLevel: "info" },
],
contextLocalStorage: new AsyncLocalStorage(), // 암시적 컨텍스트 활성화
});
이 구성을 사용하면 모든 로그 항목에 자동으로 requestId 속성이 포함됩니다. 특정 요청을 디버깅해야 할 때 로그를 필터링할 수 있습니다:
jq 'select(.properties.requestId == "abc-123")' fedify.jsonl
그러면 해당 요청의 모든 로그 항목을 볼 수 있습니다—모든 하위 시스템에 걸쳐, 모두 순서대로. 수동 상관관계 분석이 필요 없습니다.
requestId는 가능한 경우 표준 헤더(X-Request-Id, Traceparent 등)에서 파생되므로, 기존 관찰성 인프라와 자연스럽게 통합됩니다.
사용자가 실제로 보는 것
그렇다면 이 모든 구성이 Fedify를 사용하는 사람에게 실제로 어떤 의미가 있을까요?
Fedify 사용자가 LogTape를 전혀 구성하지 않으면, 아무것도 보이지 않습니다. 누락된 구성에 대한 경고도 없고, 기본 출력도 없으며, 성능 오버헤드도 최소화됩니다—로깅 호출은 본질적으로 아무 작업도 하지 않습니다.
기본적인 가시성을 위해, 세 줄의 구성으로 Fedify의 모든 오류 수준 로깅을 활성화할 수 있습니다. 특정 문제를 디버깅할 때는 관련 하위 시스템에 대해서만 디버그 수준 로깅을 활성화할 수 있습니다.
그리고 심각한 관찰성 요구 사항이 있는 프로덕션 환경에서 실행 중이라면, 요청 상관관계가 내장된 구조화된 JSON 로그를 모니터링 시스템으로 전송할 수 있습니다.
동일한 라이브러리 코드가 이 모든 시나리오를 지원합니다—사용자가 Node.js, Deno, Bun 또는 엣지 함수에서 실행하든, 추가 폴리필이나 심(shim) 없이 가능합니다. 사용자가 필요한 것을 결정합니다.
배운 교훈
LogTape로 Fedify를 구축하면서 몇 가지를 배웠습니다:
카테고리를 일찍 설계하세요. 계층적 구조는 사용자가 실제로 로그를 필터링하고 싶어하는 방식을 반영해야 합니다. 저는 Fedify의 카테고리를 사용자가 독립적으로 디버깅해야 할 수 있는 하위 시스템을 중심으로 구성했습니다.
구조화된 로깅을 사용하세요. requestId, activityId, actorId와 같은 속성은 프로그래밍 방식으로 로그를 분석해야 할 때 문자열 보간보다 훨씬 더 유용합니다.
암시적 컨텍스트가 예상보다 더 유용한 것으로 판명되었습니다. 컨텍스트를 수동으로 전달하지 않고도 비동기 경계를 넘어 로그를 연관시킬 수 있어 분산 작업 디버깅이 훨씬 쉬워졌습니다. 사용자가 활동 전달이 실패했다고 보고할 때, 관련된 모든 것을 추출하는 단일 jq 명령을 제공할 수 있습니다.
사용자를 신뢰하세요. 일부 라이브러리 작성자는 로그를 통해 너무 많은 내부 세부 정보를 노출하는 것을 걱정합니다. 저는 반대의 경험을 했습니다—사용자들은 필요할 때 무슨 일이 일어나고 있는지 볼 수 있다는 것을 감사하게 생각합니다. 핵심은 옵트인(opt-in) 방식으로 만드는 것입니다.
직접 시도해 보세요
라이브러리를 구축하면서 로깅 문제—얼마나 많이 로깅할지, 사용자에게 어떻게 제어권을 줄지, 어떻게 시끄럽지 않게 할지—로 고민하고 계시다면, Fedify가 어떻게 하는지 살펴보시길 권장합니다.
Fedify 로깅 문서에서 모든 것을 자세히 설명합니다. 그리고 LogTape 설계 철학을 이해하고 싶다면, 제 이전 글에서 다루고 있습니다.
LogTape은 해당 도구에 만족하는 애플리케이션 개발자를 위해 winston이나 Pino를 대체하려는 것이 아닙니다. 이는 다른 간극을 메웁니다: 사용자가 필요로 할 때까지 방해가 되지 않기를 원하는 라이브러리를 위한 로깅입니다. 그것이 여러분이 찾고 있는 것이라면, 일반적인 앱 중심 로거보다 더 적합할 수 있습니다.
한국어(대한민국):
Fedify, ActivityPub 서버 프레임워크를 개발하기 시작했을 때, 의외의 문제에 부딪혔습니다: 로깅을 추가하는 방법을 찾지 못했습니다.
로깅 자체가 어려워서가 아닙니다—JavaScript용 성숙한 로깅 라이브러리는 수십 개가 있습니다. 문제는 이들이 주로 애플리케이션을 위해 설계되었지, 방해가 되지 않기를 원하는 라이브러리를 위한 것이 아니라는 점이었습니다.
저는 몇 달 전에 이에 대해 글을 썼고, 반응은 적당했습니다—약간의 관심, 약간의 회의론, 그리고 이 글이 AI로 생성되었는지에 대한 꽤 많은 논쟁이 있었습니다. 솔직히 말하자면: 영어는 제 모국어가 아니기 때문에 글을 다듬기 위해 LLM을 사용합니다. 하지만 아이디어와 기술적 내용은 제 것입니다.
몇몇 독자들은 이론보다 실제 사례를 보고 싶어했습니다.
문제: 기존 로거들은 앱을 만든다고 가정합니다
Fedify는 개발자들이 ActivityPub 프로토콜을 사용하여 연합형 소셜 애플리케이션을 구축하는 데 도움을 줍니다. 연합(federation)과 작업해 본 적이 있다면, 디버깅이 얼마나 고통스러울 수 있는지 아실 겁니다. 활동(activity) 전달이 실패했을 때, 다음과 같은 질문에 답해야 합니다:
- HTTP 요청이 실제로 나갔나요?
- 서명이 올바르게 생성되었나요?
- 원격 서버가 이를 거부했나요? 왜 그랬나요?
- 응답 파싱에 문제가 있었나요?
이러한 질문들은 여러 하위 시스템에 걸쳐 있습니다: HTTP 처리, 암호화 서명, JSON-LD 처리, 큐 관리 등. 적절한 로깅 없이는 디버깅이 추측 게임이 됩니다.
하지만 라이브러리 작성자로서 제가 직면한 딜레마는 이것이었습니다: 디버깅을 돕기 위해 상세한 로깅을 추가하면, Fedify의 내부 메시지로 콘솔이 어지러워지는 것을 원치 않는 사용자들을 짜증나게 할 위험이 있습니다. 반면 아무 말도 하지 않으면, 사용자들은 문제를 진단하는 데 어려움을 겪습니다.
기존 옵션들을 살펴봤습니다. winston이나 Pino를 사용하면 다음 중 하나를 해야 했습니다:
- Fedify 내부에 로거를 구성하거나(사용자에게 내 선택을 강요), 또는
- 사용자에게 로거 인스턴스를 Fedify에 전달하도록 요청(상용구 코드 추가)
또한 debug도 있는데, 이는 이런 사용 사례를 위해 설계되었습니다. 하지만 운영 팀이 기대하는 구조화된 레벨 기반 로그를 제공하지 않으며, 보안상의 이유로 Deno와 같은 일부 런타임에서 기본적으로 제한하는 환경 변수에 의존합니다.
이 중 어느 것도 적합하지 않았습니다. 그래서 저는 LogTape—라이브러리 작성자를 위해 처음부터 설계된 로깅 라이브러리를 만들었습니다. 그리고 Fedify는 그 첫 번째 실제 사용자가 되었습니다.
해결책: 기본 출력이 없는 계층적 카테고리
핵심 통찰은 간단했습니다: 라이브러리는 애플리케이션 개발자가 명시적으로 활성화하지 않는 한 어떤 출력도 생성하지 않고 로깅할 수 있어야 합니다.
Fedify는 LogTape의 계층적 카테고리 시스템을 사용하여 사용자에게 보고 싶은 것에 대한 세밀한 제어 기능을 제공합니다. 카테고리는 다음과 같이 구성되어 있습니다:
| 카테고리 |
로깅 내용 |
["fedify"] |
라이브러리의 모든 것 |
["fedify", "federation", "inbox"] |
수신 활동 |
["fedify", "federation", "outbox"] |
발신 활동 |
["fedify", "federation", "http"] |
HTTP 요청 및 응답 |
["fedify", "sig", "http"] |
HTTP 서명 작업 |
["fedify", "sig", "ld"] |
링크드 데이터 서명 작업 |
["fedify", "sig", "key"] |
키 생성 및 검색 |
["fedify", "runtime", "docloader"] |
JSON-LD 문서 로딩 |
["fedify", "webfinger", "lookup"] |
WebFinger 리소스 조회 |
...그리고 약 십여 개 더 있습니다. 각 카테고리는 별개의 하위 시스템에 해당합니다.
이는 사용자가 다음과 같이 로깅을 구성할 수 있음을 의미합니다:
await configure({
sinks: { console: getConsoleSink() },
loggers: [
// Fedify의 모든 오류 표시
{ category: "fedify", sinks: ["console"], lowestLevel: "error" },
// 하지만 특별히 inbox 처리에 대한 디버그 정보 표시
{ category: ["fedify", "federation", "inbox"], sinks: ["console"], lowestLevel: "debug" },
],
});
수신 활동에 문제가 생기면, 다른 모든 것은 조용히 유지하면서 해당 하위 시스템에 대한 상세한 로그를 얻을 수 있습니다. 코드 변경이 필요 없이 구성만으로 가능합니다.
암시적 컨텍스트를 통한 요청 추적
계층적 카테고리는 필터링 문제를 해결했지만, 또 다른 과제가 있었습니다: 비동기 경계를 넘어 로그를 연관시키는 것입니다.
연합 시스템에서는 단일 사용자 작업이 일련의 작업을 촉발할 수 있습니다: 원격 액터 가져오기, 서명 확인, 활동 처리, 팔로워에게 전파 등. 무언가 실패했을 때, 해당 특정 요청에 대한 모든 로그 항목을 연관시켜야 합니다.
Fedify는 LogTape의 암시적 컨텍스트 기능을 사용하여 모든 로그 항목에 자동으로 requestId를 태그합니다:
await configure({
sinks: {
file: getFileSink("fedify.jsonl", { formatter: jsonLinesFormatter })
},
loggers: [
{ category: "fedify", sinks: ["file"], lowestLevel: "info" },
],
contextLocalStorage: new AsyncLocalStorage(), // 암시적 컨텍스트 활성화
});
이 구성을 통해 모든 로그 항목에는 자동으로 requestId 속성이 포함됩니다. 특정 요청을 디버깅해야 할 때 로그를 필터링할 수 있습니다:
jq 'select(.properties.requestId == "abc-123")' fedify.jsonl
그러면 해당 요청의 모든 로그 항목을 볼 수 있습니다—모든 하위 시스템에 걸쳐, 모두 순서대로. 수동 상관관계 분석이 필요 없습니다.
requestId는 가능한 경우 표준 헤더(X-Request-Id, Traceparent 등)에서 파생되므로 기존 관찰성 인프라와 자연스럽게 통합됩니다.
사용자가 실제로 보는 것
그렇다면 이 모든 구성이 Fedify를 사용하는 사람들에게 실제로 어떤 의미가 있을까요?
Fedify 사용자가 LogTape를 전혀 구성하지 않으면, 아무것도 보이지 않습니다. 누락된 구성에 대한 경고도 없고, 기본 출력도 없으며, 성능 오버헤드도 최소화됩니다—로깅 호출은 본질적으로 아무 작업도 하지 않습니다.
기본적인 가시성을 위해, 세 줄의 구성으로 Fedify의 모든 오류 수준 로깅을 활성화할 수 있습니다. 특정 문제를 디버깅할 때는 관련 하위 시스템에 대해서만 디버그 수준 로깅을 활성화할 수 있습니다.
그리고 심각한 관찰성 요구 사항이 있는 프로덕션 환경에서 실행하는 경우, 요청 상관관계가 내장된 구조화된 JSON 로그를 모니터링 시스템으로 전송할 수 있습니다.
동일한 라이브러리 코드가 이 모든 시나리오를 지원합니다—사용자가 Node.js, Deno, Bun 또는 엣지 함수에서 실행하든, 추가 폴리필이나 심(shim) 없이 가능합니다. 사용자가 필요한 것을 결정합니다.
배운 교훈
LogTape로 Fedify를 구축하면서 몇 가지를 배웠습니다:
카테고리를 일찍 설계하세요. 계층적 구조는 사용자가 실제로 로그를 필터링하고 싶어하는 방식을 반영해야 합니다. 저는 Fedify의 카테고리를 사용자가 독립적으로 디버깅해야 할 수 있는 하위 시스템을 중심으로 구성했습니다.
구조화된 로깅을 사용하세요. requestId, activityId, actorId와 같은 속성은 프로그래밍 방식으로 로그를 분석해야 할 때 문자열 보간보다 훨씬 더 유용합니다.
암시적 컨텍스트는 예상보다 더 유용했습니다. 컨텍스트를 수동으로 전달하지 않고도 비동기 경계를 넘어 로그를 연관시킬 수 있어 분산 작업 디버깅이 훨씬 쉬워졌습니다. 사용자가 활동 전달이 실패했다고 보고할 때, 관련된 모든 것을 추출하는 단일 jq 명령을 제공할 수 있습니다.
사용자를 신뢰하세요. 일부 라이브러리 작성자는 로그를 통해 너무 많은 내부 세부 정보를 노출하는 것을 걱정합니다. 저는 반대의 경험을 했습니다—사용자들은 필요할 때 무슨 일이 일어나고 있는지 볼 수 있다는 것을 감사하게 생각합니다. 핵심은 옵트인(opt-in) 방식으로 만드는 것입니다.
직접 시도해 보세요
라이브러리를 구축하면서 로깅 문제—얼마나 많이 로깅할지, 사용자에게 어떻게 제어권을 줄지, 어떻게 시끄럽지 않게 할지—로 고민하고 계시다면, Fedify가 어떻게 하는지 살펴보시길 권장합니다.
Fedify 로깅 문서에서 모든 것을 자세히 설명합니다. 그리고 LogTape 설계 철학을 이해하고 싶다면, 이전 포스트에서 다루고 있습니다.
LogTape은 winston이나 Pino에 만족하는 애플리케이션 개발자들을 위한 대체품이 되려는 것이 아닙니다. 이는 다른 간극을 메웁니다: 사용자가 필요로 할 때까지 방해가 되지 않기를 원하는 라이브러리를 위한 로깅입니다. 그것이 당신이 찾고 있는 것이라면, 일반적인 앱 중심 로거보다 더 적합할 수 있습니다.