Monolog를 이용한 로깅(Logging)
서버에서 발생하는 에러, 데이터베이스 트랜잭션 성공/실패 여부, 보안 침해 시도 등을 추적하려면 체계적인 로깅 시스템이 필수적입니다. PHP 생태계의 표준 로깅 라이브러리인 Monolog를 Slim 프레임워크와 연동하여 파일(File)에 로그를 남기는 방법을 알아봅니다.
1. Monolog 인스턴스 구성 (DI Container)
DI(Dependency Injection) 컨테이너에 Logger를 등록하면 앱 전역 라우트 콜백에서 $this->get('logger') 형태로 쉽게 불러와 사용할 수 있습니다.
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$container->set('logger', function () {
// 1. "app" 이라는 이름의 채널을 가진 Logger 인스턴스 생성
$logger = new Logger('app');
// 2. 로그를 기록할 파일 경로 설정 (일자별 분리도 가능)
// 두 번째 인자는 최소 로그 레벨입니다. (DEBUG 이상 모두 기록)
$fileHandler = new StreamHandler(__DIR__ . '/../logs/app.log', Logger::DEBUG);
// 3. 핸들러 장착
$logger->pushHandler($fileHandler);
return $logger;
});
?>
| 주요 로그 레벨 (RFC 5424) |
사용 목적 |
| DEBUG |
개발 중 상세한 진행 과정 확인 및 디버깅 용도 |
| INFO |
일반적인 주요 이벤트 (유저 가입, 결제 성공 등) |
| WARNING |
시스템 중단은 아니지만 주의가 필요한 비정상적인 상태 |
| ERROR / CRITICAL |
즉각적인 조치가 필요한 심각한 런타임 에러나 DB 연결 실패 |
2. 라우트 내에서 로그 기록하기
주입된 logger를 호출해 info(), error() 등의 메서드를 사용합니다.
<?php
$app->post('/payment', function ($request, $response) {
// 컨테이너에서 로거 꺼내기
$logger = $this->get('logger');
$data = $request->getParsedBody();
try {
$logger->info("결제 트랜잭션 시작", ['user_id' => $data['user_id']]);
// 결제 로직 수행...
$logger->info("결제 승인 완료", ['amount' => $data['amount'], 'pg' => 'Toss']);
return $response->withStatus(200);
} catch (Exception $e) {
// 오류 발생 시 스택 트레이스 정보와 함께 ERROR 로그 기록
$logger->error("결제 실패: " . $e->getMessage(), ['trace' => $e->getTraceAsString()]);
return $response->withStatus(500);
}
});
?>
[2026-05-22 17:05:00] app.INFO: 결제 트랜잭션 시작 {"user_id":1024} []
[2026-05-22 17:05:01] app.INFO: 결제 승인 완료 {"amount":15000,"pg":"Toss"} []
[2026-05-22 17:05:05] app.INFO: 결제 트랜잭션 시작 {"user_id":1025} []
[2026-05-22 17:05:06] app.ERROR: 결제 실패: PG사 연동 타임아웃 예외 발생 {"trace":"#0 /var/www/html/src/..."} []
💡 Context 배열의 중요성
메시지를 문자열 결합($msg . $id)으로 남기는 것보다, 로거의 두 번째 인자인 Context 배열에 데이터를 분리하여 넘기는 것이 강력히 권장됩니다. JSON 포맷으로 구조화되어 추후 Datadog, ElasticSearch 같은 로그 수집 도구에서 검색하기가 압도적으로 유리해집니다.
로그 적재 형태 확인
위의 코드 실행 결과 블록에서 서버의 app.log 파일 내부에 Monolog가 어떤 포맷으로 텍스트를 적재하는지 관찰해 보세요. INFO 레벨과 ERROR 레벨이 명확히 구분되며, Context 배열에 담았던 데이터들이 JSON 형식으로 깔끔하게 남은 것을 볼 수 있습니다.