Knotlog: PHP용 광범위 로깅
Source: Dev.to
As loggingsucks.com brilliantly articulates, traditional logging is fundamentally broken for modern applications. The problems are systemic:
Logs were built for the wrong era.
They originated when systems were monolithic and ran on single servers. Today, a single user request might touch 15+ services, multiple databases, caches, and message queues. Traditional logging practices simply don’t scale to this distributed reality.
String search is fundamentally flawed.
When you search logs for "user-123", you discover it’s logged inconsistently across services in dozens of different formats. Logs are optimized for writing, not for querying—making post‑incident investigation a painful archaeological expedition through grep sessions.
Massive volume, minimal insight.
Modern applications generate tens of thousands of log lines per second. A single successful request might emit 17 different log statements scattered across your codebase. Multiplied across thousands of concurrent users, this becomes unmanageable noise lacking the context you actually need for debugging.
The solution proposed by loggingsucks.com is simple but powerful: wide events (or canonical log lines). Instead of scattering log.info() statements throughout your code, emit one comprehensive, context‑rich structured event per request per service. This single event should contain all potentially useful debugging information: user context, business data, feature flags, error details, and more.
Knotlog 소개
Knotlog은 넓은 로깅(wide logging)을 일급 패턴으로 구현한 PHP 라이브러리입니다. 넓은 이벤트를 사후 고려사항으로 다루는 대신, Knotlog은 이를 기본 로깅 방식으로 만듭니다.
핵심 개념은 간단합니다: 요청 라이프사이클 전반에 걸쳐 컨텍스트를 구축하고, 마지막에 단일 구조화된 이벤트를 발생시킵니다. 디버깅을 고고학적인 grep 세션에서 구조화되고 쿼리 가능한 데이터 기반의 분석 쿼리로 전환합니다.
Knotlog 사용 방법
설치
composer require knotlog/knotlog
Knotlog은 PHP 8.4 이상이 필요합니다.
기본 사용법
근본적인 워크플로는 간단합니다: Log 인스턴스를 생성하고, 요청이 처리되는 동안 컨텍스트를 축적한 뒤, 마지막에 기록합니다:
use Knotlog\Log;
use Knotlog\Writer\FileWriter;
$log = new Log();
// Build up context throughout your request
$log->set('user_id', $userId);
$log->set('request_method', $_SERVER['REQUEST_METHOD']);
$log->set('request_path', $_SERVER['REQUEST_URI']);
$log->set('subscription_tier', $user->subscriptionTier);
$log->set('cart_value', $cart->total());
$log->set('response_status', 200);
$log->set('duration_ms', $duration);
// At the end of the request, emit the wide event
(new FileWriter())->write($log);
Log 클래스는 JsonSerializable을 구현하므로 구조화된 로깅 시스템을 위해 JSON으로 직접 인코딩할 수 있습니다.
예외 로깅
Knotlog은 스택 트레이스를 포함한 풍부한 예외 컨텍스트를 캡처하기 위해 ExceptionLog 클래스를 제공합니다:
use Knotlog\Misc\ExceptionLog;
try {
// Some code that may throw
} catch (Throwable $throwable) {
$log->set('exception', ExceptionLog::fromThrowable($throwable));
}
Log 클래스는 또한 exception 또는 error 키가 설정되어 있는지 확인하는 편리한 hasError() 메서드를 제공하며, 이는 로그 샘플링을 구현할 때 오류가 항상 캡처되도록 하는 데 특히 유용합니다.
PSR‑15 미들웨어 통합
Knotlog의 가장 강력한 기능 중 하나는 PSR‑15 미들웨어와의 원활한 통합입니다. 이를 통해 모든 엔드포인트를 수동으로 계측하지 않아도 요청 및 응답 컨텍스트를 자동으로 캡처할 수 있습니다.
LogRequestResponse
LogRequestResponse 미들웨어는 요청 및 응답 메타데이터를 자동으로 기록합니다:
use Knotlog\Http\LogRequestResponse;
// Logs request and response metadata
$stack->add(new LogRequestResponse($log));
다음 핸들러에 전달하기 전에 요청을 캡처하고, 응답이 돌아올 때 다시 캡처하여 두 정보를 자동으로 와이드 이벤트에 추가합니다.
LogRequestError
LogRequestError 미들웨어는 잡히지 않은 예외를 포착하고, 이를 기록하며 적절한 오류 응답을 생성합니다:
use Knotlog\Http\LogRequestError;
use Knotlog\Http\ServerErrorResponseFactory;
$errorFactory = new ServerErrorResponseFactory($responseFactory, $streamFactory);
// Logs uncaught exceptions and outputs an error response
$stack->add(new LogRequestError($errorFactory, $log));
예외가 미들웨어 스택을 통해 전파될 때, 이 미들웨어는 예외를 ExceptionLog로 캡처하고 와이드 이벤트에 추가한 뒤 깔끔한 오류 응답을 생성합니다.
LogResponseError
LogResponseError 미들웨어는 400 이상 상태 코드를 가진 모든 응답을 오류로 표시합니다:
use Knotlog\Http\LogResponseError;
// Sets error to the reason phrase for 400+ status codes
$stack->add(new LogResponseError($log));
이는 로그 샘플링과 함께 사용할 때 특히 유용합니다—성공적인 요청의 일부만 샘플링하더라도 오류 응답은 항상 기록됩니다.
미들웨어 순서
미들웨어는 다음과 같이 순서대로 배치해야 합니다(첫 번째부터 마지막까지):
LogResponseError– 오류 응답 표시LogRequestResponse– 요청/응답 메타데이터 기록LogRequestError– 잡히지 않은 예외 포착
Note: 일부 프레임워크는 미들웨어 스택에 대해 후입선출 순서를 사용하므로, 이에 맞게 조정하십시오.
Source: …
PSR‑3 로거 통합
Knotlog은 기존 로깅 패턴을 대체하도록 설계되었지만, 구조화된 이벤트를 기존 로깅 파이프라인으로 전달해야 하는 경우 PSR‑3 로거와도 통합할 수 있습니다. (구현 세부 사항은 간략히 생략)
LoggerWriter – PSR‑3 로거와의 브리지
많은 애플리케이션이 이미 PSR‑3 로거 인프라를 갖추고 있습니다. LoggerWriter는 wide events를 모든 PSR‑3 호환 로거로 라우팅하는 브리지를 제공합니다:
use Knotlog\Writer\LoggerWriter;
// 任意의 PSR‑3 로거(Monolog 등)와 함께 사용
$writer = new LoggerWriter($psrLogger);
// 로그 이벤트 기록
$writer->write($log);
LoggerWriter는 PSR‑3 메시지 보간을 사용해 wide events를 포맷합니다. 로그 레벨에 맞게 자동으로 라우팅합니다:
| 레벨 | 사용 시점 | 플레이스홀더 |
|---|---|---|
error() | 로그에 오류나 예외가 포함된 경우 | {error} |
info() | 그 외 모든 로그 이벤트 | {message} |
전체 로그 컨텍스트가 로거에 전달되어, 로거가 자체 구현에 따라 구조화된 데이터를 포맷하고 처리할 수 있습니다.
메시지와 오류 키를 로깅 스키마에 맞게 커스터마이즈할 수 있습니다:
$writer = new LoggerWriter($psrLogger, messageKey: 'msg', errorKey: 'err');
이 통합을 통해 기존 로깅 인프라를 포기하지 않고도 wide‑logging 패턴을 채택할 수 있습니다. 여러분의 PSR‑3 로거는 흩어진 문자열 메시지가 아니라 풍부하고 구조화된 컨텍스트를 받게 됩니다.
추가 기능
로그 샘플링
SampledWriter 데코레이터는 항상 오류를 캡처하면서 로그 이벤트를 샘플링할 수 있게 합니다:
use Knotlog\Writer\SampledWriter;
use Knotlog\Writer\FileWriter;
// Sample 1 in 100 requests (1%)
$writer = new SampledWriter(new FileWriter(), 100);
$writer->write($log);
오류나 예외가 포함된 모든 로그 이벤트는 샘플링 비율에 관계없이 기록됩니다. 샘플링은 성공적인 요청에만 적용되며, 이는 모든 성공 요청을 로깅하면 비용이 과도하게 발생할 수 있는 고트래픽 애플리케이션에 중요합니다.
콘솔 통합
Knotlog은 Symfony Console 이벤트 리스너를 제공하여 CLI 명령에 광범위한 로깅을 적용합니다:
use Knotlog\Console\LogCommandError;
use Knotlog\Console\LogCommandEvent;
use Symfony\Component\Console\ConsoleEvents;
// Log command metadata on execution
$eventDispatcher->addListener(
ConsoleEvents::COMMAND,
new LogCommandEvent($log)
);
// Log command error context on failure
$eventDispatcher->addListener(
ConsoleEvents::ERROR,
new LogCommandError($log)
);
핵심 요약
전통적인 로깅은 “내 코드가 무엇을 하고 있나요?” 라고 묻습니다.
와이드 로깅은 “이 요청에 무슨 일이 일어났나요?” 라고 묻습니다.
이 관점의 전환은 — Knotlog의 일급 PSR‑15 미들웨어와 PSR‑3 통합과 결합되어 — 로깅을 디버깅의 부수적인 작업에서 강력한 가시성 도구로 바꿉니다.
- 코드 전역에 로그 문을 흩어 놓는 것을 중단하세요.
- Knotlog를 사용해 포괄적이고 쿼리 가능한 컨텍스트를 캡처하기 시작하세요.
**github.com/shadowhand/knotlog**에서 더 알아보고, **loggingsucks.com**에서 철학을 읽어보세요.