로깅
번역일: 2026년 6월 25일
로깅
소개
애플리케이션 내부에서 어떤 일이 일어나고 있는지 파악할 수 있도록, Laravel은 강력한 로깅 서비스를 제공합니다. 파일, 시스템 에러 로그, 또는 Slack을 통해 팀 전체에 알림을 보내는 것까지 다양한 방식으로 로그를 기록할 수 있습니다.
Laravel 로깅은 채널(channel) 개념을 기반으로 합니다. 각 채널은 로그 정보를 기록하는 특정 방식을 나타냅니다. 예를 들어, single 채널은 단일 파일에 로그를 기록하고, slack 채널은 Slack으로 메시지를 전송합니다. 로그 메시지는 심각도에 따라 여러 채널에 동시에 기록될 수도 있습니다.
Laravel은 내부적으로 Monolog 라이브러리를 활용합니다. Monolog는 다양하고 강력한 로그 핸들러를 지원하며, Laravel은 이를 손쉽게 조합하여 원하는 로그 처리 방식을 구성할 수 있게 해줍니다.
설정
로깅 동작을 제어하는 모든 설정 옵션은 config/logging.php 파일에 모여 있습니다. 이 파일에서 애플리케이션의 로그 채널을 설정할 수 있으므로, 사용 가능한 채널과 각 옵션을 꼼꼼히 살펴보시기 바랍니다.
기본적으로 Laravel은 로그 메시지를 기록할 때 stack 채널을 사용합니다. stack 채널은 여러 로그 채널을 하나로 묶어 주는 역할을 합니다. 스택 구성에 대한 자세한 내용은 아래 문서를 참고하세요.
사용 가능한 채널 드라이버
각 로그 채널은 "드라이버"에 의해 동작합니다. 드라이버는 로그 메시지가 실제로 어떻게, 어디에 기록되는지를 결정합니다. 모든 Laravel 애플리케이션에서 아래의 로그 채널 드라이버를 사용할 수 있으며, 대부분은 config/logging.php에 이미 설정 예시가 포함되어 있습니다.
| 이름 | 설명 |
|---|---|
custom | 지정한 팩토리를 호출하여 채널을 생성하는 드라이버 |
daily | 매일 파일을 교체하는 RotatingFileHandler 기반 Monolog 드라이버 |
errorlog | ErrorLogHandler 기반 Monolog 드라이버 |
monolog | 지원되는 모든 Monolog 핸들러를 사용할 수 있는 Monolog 팩토리 드라이버 |
papertrail | SyslogUdpHandler 기반 Monolog 드라이버 |
single | 단일 파일 또는 경로에 기록하는 채널 (StreamHandler) |
slack | SlackWebhookHandler 기반 Monolog 드라이버 |
stack | 여러 채널을 하나로 묶는 "멀티 채널" 래퍼 |
syslog | SyslogHandler 기반 Monolog 드라이버 |
NOTE
monolog 및 custom 드라이버에 대한 자세한 내용은 고급 채널 커스터마이징 문서를 참고하세요.
채널 이름 설정
기본적으로 Monolog는 production이나 local과 같이 현재 환경 이름을 채널 이름으로 사용합니다. 이 값을 변경하려면 채널 설정에 name 옵션을 추가하면 됩니다.
'stack' => [
'driver' => 'stack',
'name' => 'channel-name',
'channels' => ['single', 'slack'],
],채널 사전 요구 사항
Single 및 Daily 채널 설정
single과 daily 채널에는 세 가지 선택적 설정 옵션이 있습니다: bubble, permission, locking.
| 이름 | 설명 | 기본값 |
|---|---|---|
bubble | 메시지 처리 후 다른 채널로 전파할지 여부 | true |
locking | 파일 쓰기 전 로그 파일 잠금 시도 여부 | false |
permission | 로그 파일의 권한 설정 | 0644 |
daily 채널의 파일 보관 기간은 LOG_DAILY_DAYS 환경 변수 또는 days 설정 옵션으로 조정할 수 있습니다.
| 이름 | 설명 | 기본값 |
|---|---|---|
days | 일별 로그 파일 보관 일수 | 14 |
Papertrail 채널 설정
papertrail 채널을 사용하려면 host와 port 설정이 필요합니다. PAPERTRAIL_URL과 PAPERTRAIL_PORT 환경 변수로 설정할 수 있으며, 해당 값은 Papertrail에서 확인할 수 있습니다.
Slack 채널 설정
slack 채널을 사용하려면 url 설정이 필요합니다. LOG_SLACK_WEBHOOK_URL 환경 변수로 지정할 수 있으며, 이 URL은 Slack 팀에서 설정한 Incoming Webhook URL이어야 합니다.
기본적으로 Slack은 critical 레벨 이상의 로그만 수신합니다. LOG_LEVEL 환경 변수 또는 Slack 채널 설정 배열의 level 옵션을 통해 이 기준을 변경할 수 있습니다.
Deprecation 경고 로깅
PHP, Laravel, 그리고 각종 라이브러리는 특정 기능이 deprecated되어 향후 버전에서 제거될 예정임을 경고하는 메시지를 남깁니다. 이러한 경고를 로그로 기록하고 싶다면, LOG_DEPRECATIONS_CHANNEL 환경 변수 또는 config/logging.php에서 원하는 채널을 지정할 수 있습니다.
'deprecations' => [
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
],
'channels' => [
// ...
]또는 deprecations라는 이름의 로그 채널을 직접 정의할 수도 있습니다. 이 이름의 채널이 존재하면 항상 deprecation 경고를 해당 채널로 기록합니다.
'channels' => [
'deprecations' => [
'driver' => 'single',
'path' => storage_path('logs/php-deprecation-warnings.log'),
],
],로그 스택 구성
앞서 설명했듯이, stack 드라이버를 사용하면 여러 채널을 하나의 로그 채널로 편리하게 묶을 수 있습니다. 실제 운영 환경에서 자주 볼 수 있는 예시 설정을 살펴보겠습니다.
'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['syslog', 'slack'], // 'ignore_exceptions' => false, ], 'syslog' => [ 'driver' => 'syslog', 'level' => env('LOG_LEVEL', 'debug'), 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), 'replace_placeholders' => true, ], 'slack' => [ 'driver' => 'slack', 'url' => env('LOG_SLACK_WEBHOOK_URL'), 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), 'level' => env('LOG_LEVEL', 'critical'), 'replace_placeholders' => true, ],],이 설정을 분석해 보겠습니다. stack 채널은 channels 옵션을 통해 syslog와 slack, 두 채널을 하나로 묶고 있습니다. 따라서 로그 메시지를 기록할 때 두 채널 모두에 전달됩니다. 단, 실제로 메시지가 기록되는지는 메시지의 심각도(레벨)에 따라 결정됩니다.
로그 레벨
위 예시에서 syslog와 slack 채널 설정에 있는 level 옵션에 주목하세요. 이 옵션은 해당 채널에 기록되기 위한 메시지의 최소 레벨을 결정합니다. Laravel 로깅의 기반인 Monolog는 RFC 5424 명세에서 정의한 모든 로그 레벨을 지원합니다. 심각도 높은 순서대로 나열하면 다음과 같습니다: emergency, alert, critical, error, warning, notice, info, debug.
예를 들어, debug 메서드로 메시지를 기록한다고 가정해 보겠습니다.
Log::debug('An informational message.');위 설정 기준으로 syslog 채널은 이 메시지를 시스템 로그에 기록합니다. 그러나 critical 레벨 이상이 아니므로 Slack에는 전송되지 않습니다. 반면, emergency 메시지를 기록하면 두 채널의 최소 레벨 기준을 모두 충족하므로 시스템 로그와 Slack 양쪽에 기록됩니다.
Log::emergency('The system is down!');로그 메시지 작성
Log 파사드를 사용하여 로그를 기록할 수 있습니다. 앞서 언급했듯이, RFC 5424 명세에서 정의한 8가지 로그 레벨 메서드를 제공합니다: emergency, alert, critical, error, warning, notice, info, debug.
use Illuminate\Support\Facades\Log;
Log::emergency($message);
Log::alert($message);
Log::critical($message);
Log::error($message);
Log::warning($message);
Log::notice($message);
Log::info($message);
Log::debug($message);이 메서드들 중 하나를 호출하면 해당 레벨로 메시지가 기록됩니다. 기본적으로 logging 설정 파일에서 지정한 기본 로그 채널에 기록됩니다.
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* 주어진 사용자의 프로필을 표시합니다.
*/
public function show(string $id): View
{
Log::info('사용자 프로필 조회: {id}', ['id' => $id]);
return view('user.profile', [
'user' => User::findOrFail($id)
]);
}
}컨텍스트 정보
로그 메서드에 컨텍스트 데이터 배열을 함께 전달할 수 있습니다. 이 데이터는 로그 메시지와 함께 형식에 맞게 출력됩니다.
use Illuminate\Support\Facades\Log;
Log::info('사용자 {id} 로그인 실패.', ['id' => $user->id]);특정 채널의 이후 모든 로그 항목에 공통 컨텍스트 정보를 포함시키고 싶을 때도 있습니다. 예를 들어, 각 요청에 고유한 요청 ID를 로그에 함께 기록하고 싶다면 Log 파사드의 withContext 메서드를 활용하세요.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;
class AssignRequestId
{
/**
* 들어오는 요청을 처리합니다.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$requestId = (string) Str::uuid();
Log::withContext([
'request-id' => $requestId
]);
$response = $next($request);
$response->headers->set('Request-Id', $requestId);
return $response;
}
}모든 로그 채널에 걸쳐 컨텍스트 정보를 공유하려면 Log::shareContext() 메서드를 사용하세요. 이 메서드는 이미 생성된 채널은 물론, 이후에 생성되는 채널에도 컨텍스트 정보를 제공합니다.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;
class AssignRequestId
{
/**
* 들어오는 요청을 처리합니다.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$requestId = (string) Str::uuid();
Log::shareContext([
'request-id' => $requestId
]);
// ...
}
}NOTE
큐 Job 처리 중에 로그 컨텍스트를 공유해야 한다면 Job 미들웨어를 활용하세요.
특정 채널에 기록하기
기본 채널이 아닌 특정 채널에 로그를 기록하고 싶을 때는 Log 파사드의 channel 메서드를 사용하세요.
use Illuminate\Support\Facades\Log;
Log::channel('slack')->info('무언가 발생했습니다!');여러 채널을 묶어 즉석 로그 스택을 구성하려면 stack 메서드를 사용하세요.
Log::stack(['single', 'slack'])->info('무언가 발생했습니다!');온디맨드 채널
설정 파일에 미리 정의하지 않고 런타임에 설정 배열을 직접 넘겨 즉석 채널을 만들 수도 있습니다. Log 파사드의 build 메서드에 설정 배열을 전달하면 됩니다.
use Illuminate\Support\Facades\Log;
Log::build([
'driver' => 'single',
'path' => storage_path('logs/custom.log'),
])->info('무언가 발생했습니다!');온디맨드 채널을 즉석 스택에 포함시키려면, stack 메서드에 넘기는 배열에 해당 채널 인스턴스를 함께 전달하면 됩니다.
use Illuminate\Support\Facades\Log;
$channel = Log::build([
'driver' => 'single',
'path' => storage_path('logs/custom.log'),
]);
Log::stack(['slack', $channel])->info('무언가 발생했습니다!');Monolog 채널 커스터마이징
채널별 Monolog 커스터마이징
기존 채널의 Monolog 설정을 세밀하게 제어해야 할 때가 있습니다. 예를 들어, Laravel 기본 제공 single 채널에 커스텀 Monolog FormatterInterface 구현체를 적용하고 싶은 경우가 있습니다.
시작하려면 채널 설정에 tap 배열을 정의하세요. tap 배열에는 Monolog 인스턴스가 생성된 후 이를 커스터마이징할 클래스 목록을 지정합니다. 이 클래스들을 둘 위치에 대한 규칙은 없으며, 애플리케이션 내 원하는 디렉토리에 자유롭게 생성할 수 있습니다.
'single' => [
'driver' => 'single',
'tap' => [App\Logging\CustomizeFormatter::class],
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'replace_placeholders' => true,
],tap 옵션을 설정했다면, Monolog 인스턴스를 커스터마이징할 클래스를 정의하세요. 이 클래스에는 Illuminate\Log\Logger 인스턴스를 받는 __invoke 메서드 하나만 있으면 됩니다. Illuminate\Log\Logger 인스턴스는 모든 메서드 호출을 내부 Monolog 인스턴스로 위임합니다.
<?php
namespace App\Logging;
use Illuminate\Log\Logger;
use Monolog\Formatter\LineFormatter;
class CustomizeFormatter
{
/**
* 주어진 로거 인스턴스를 커스터마이징합니다.
*/
public function __invoke(Logger $logger): void
{
foreach ($logger->getHandlers() as $handler) {
$handler->setFormatter(new LineFormatter(
'[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
));
}
}
}NOTE
모든 tap 클래스는 서비스 컨테이너를 통해 해석되므로, 생성자에서 의존성을 선언하면 자동으로 주입됩니다.
Monolog 핸들러 채널 생성
Monolog는 다양한 핸들러를 제공하지만, Laravel이 모든 핸들러에 대해 기본 채널을 제공하지는 않습니다. 특정 Monolog 핸들러를 직접 채널로 사용하고 싶다면 monolog 드라이버로 간단히 생성할 수 있습니다.
monolog 드라이버를 사용할 때는 handler 옵션으로 사용할 핸들러 클래스를 지정합니다. 핸들러 생성자에 전달할 인수가 필요하다면 handler_with 옵션으로 지정할 수 있습니다.
'logentries' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\SyslogUdpHandler::class,
'handler_with' => [
'host' => 'my.logentries.internal.datahubhost.company.com',
'port' => '10000',
],
],Monolog 포매터
monolog 드라이버를 사용할 때 기본 포매터는 Monolog의 LineFormatter입니다. formatter와 formatter_with 설정 옵션을 통해 핸들러에 전달할 포매터 타입을 커스터마이징할 수 있습니다.
'browser' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\BrowserConsoleHandler::class,
'formatter' => Monolog\Formatter\HtmlFormatter::class,
'formatter_with' => [
'dateFormat' => 'Y-m-d',
],
],핸들러가 자체 포매터를 제공하는 경우에는 formatter 옵션 값을 default로 지정하면 됩니다.
'newrelic' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\NewRelicHandler::class,
'formatter' => 'default',
],Monolog 프로세서
Monolog는 메시지를 기록하기 전에 가공하는 프로세서도 지원합니다. 직접 프로세서를 만들거나 Monolog에서 제공하는 기존 프로세서를 사용할 수 있습니다.
monolog 드라이버 채널에 프로세서를 추가하려면 processors 설정 값을 지정하세요.
'memory' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\StreamHandler::class,
'handler_with' => [
'stream' => 'php://stderr',
],
'processors' => [
// 간단한 방식...
Monolog\Processor\MemoryUsageProcessor::class,
// 옵션을 함께 지정하는 방식...
[
'processor' => Monolog\Processor\PsrLogMessageProcessor::class,
'with' => ['removeUsedContextFields' => true],
],
],
],팩토리를 이용한 커스텀 채널 생성
Monolog의 인스턴스화 및 설정을 완전히 직접 제어하는 커스텀 채널을 만들고 싶다면, config/logging.php에서 드라이버 타입을 custom으로 지정하세요. 설정에는 Monolog 인스턴스를 생성할 팩토리 클래스 이름을 via 옵션으로 포함해야 합니다.
'channels' => [
'example-custom-channel' => [
'driver' => 'custom',
'via' => App\Logging\CreateCustomLogger::class,
],
],custom 드라이버 채널을 설정했다면, Monolog 인스턴스를 생성할 클래스를 정의하세요. 이 클래스에는 채널 설정 배열을 인수로 받아 Monolog 로거 인스턴스를 반환하는 __invoke 메서드 하나만 있으면 됩니다.
<?php
namespace App\Logging;
use Monolog\Logger;
class CreateCustomLogger
{
/**
* 커스텀 Monolog 인스턴스를 생성합니다.
*/
public function __invoke(array $config): Logger
{
return new Logger(/* ... */);
}
}