로깅
번역일: 2026년 6월 27일
로깅
소개
애플리케이션 내부에서 어떤 일이 일어나고 있는지 파악할 수 있도록, Laravel은 강력한 로깅 기능을 제공합니다. 로그 메시지를 파일에 기록하거나, 시스템 오류 로그로 보내거나, 팀 전체에 알림을 보내기 위해 Slack으로 전송할 수도 있습니다.
Laravel 로깅은 "채널(channel)" 개념을 중심으로 동작합니다. 채널은 로그 정보를 기록하는 구체적인 방식을 나타냅니다. 예를 들어, single 채널은 하나의 로그 파일에 기록하고, slack 채널은 Slack으로 메시지를 전송합니다. 메시지의 심각도(severity)에 따라 여러 채널에 동시에 기록할 수도 있습니다.
내부적으로는 Monolog 라이브러리를 사용합니다. Monolog는 다양한 강력한 로그 핸들러를 지원하며, Laravel은 이를 손쉽게 구성하고 조합할 수 있는 인터페이스를 제공합니다.
설정
로깅과 관련된 모든 설정은 config/logging.php 파일에서 관리합니다. 이 파일에서 애플리케이션의 로그 채널을 정의할 수 있으므로, 어떤 채널과 옵션이 있는지 한 번 살펴보는 것을 권장합니다.
기본적으로 Laravel은 로그 기록 시 stack 채널을 사용합니다. stack 채널은 여러 로그 채널을 하나로 묶어 동시에 기록할 수 있게 해줍니다. 자세한 내용은 로그 스택 구성 섹션을 참고하세요.
채널 이름 설정
기본적으로 Monolog는 현재 환경(production, local 등)을 채널 이름으로 사용합니다. 이 값을 바꾸려면 채널 설정에 name 옵션을 추가하면 됩니다.
'stack' => [
'driver' => 'stack',
'name' => 'channel-name',
'channels' => ['single', 'slack'],
],사용 가능한 채널 드라이버
각 로그 채널은 "드라이버(driver)"에 의해 동작합니다. 드라이버는 로그 메시지를 실제로 어디에, 어떻게 기록할지를 결정합니다. 다음은 모든 Laravel 애플리케이션에서 사용할 수 있는 로그 채널 드라이버 목록입니다. 대부분의 드라이버는 config/logging.php 파일에 이미 예시 설정이 포함되어 있으니 참고하세요.
| 이름 | 설명 |
|---|---|
custom | 지정한 팩토리를 호출하여 채널을 생성하는 드라이버 |
daily | 매일 파일을 교체하는 RotatingFileHandler 기반의 Monolog 드라이버 |
errorlog | ErrorLogHandler 기반의 Monolog 드라이버 |
monolog | 지원되는 모든 Monolog 핸들러를 사용할 수 있는 팩토리 드라이버 |
papertrail | SyslogUdpHandler 기반의 Monolog 드라이버 |
single | 단일 파일 또는 경로 기반의 로거 채널 (StreamHandler) |
slack | SlackWebhookHandler 기반의 Monolog 드라이버 |
stack | 여러 채널을 하나로 묶기 위한 래퍼 드라이버 |
syslog | SyslogHandler 기반의 Monolog 드라이버 |
NOTE
monolog 및 custom 드라이버에 대한 자세한 내용은 고급 채널 커스터마이징 섹션을 참고하세요.
채널 사전 요구 사항
Single 및 Daily 채널 설정
single과 daily 채널에는 세 가지 선택적 설정 옵션이 있습니다.
| 이름 | 설명 | 기본값 |
|---|---|---|
bubble | 메시지 처리 후 다른 채널로 전파할지 여부 | true |
locking | 파일 쓰기 전에 잠금을 시도할지 여부 | false |
permission | 로그 파일 권한 | 0644 |
daily 채널의 경우, days 옵션으로 로그 파일 보존 기간을 설정할 수 있습니다.
| 이름 | 설명 | 기본값 |
|---|---|---|
days | 일별 로그 파일을 보존할 기간(일 수) | 7 |
Papertrail 채널 설정
papertrail 채널을 사용하려면 host와 port 설정이 필요합니다. 해당 값은 Papertrail에서 확인할 수 있습니다.
Slack 채널 설정
slack 채널을 사용하려면 url 설정이 필요합니다. 이 URL은 Slack 팀에서 설정한 Incoming Webhook URL이어야 합니다.
기본적으로 Slack 채널은 critical 이상의 로그만 수신합니다. config/logging.php의 Slack 채널 설정 배열에서 level 옵션을 수정하면 이 기준을 변경할 수 있습니다.
Deprecation 경고 로깅
PHP, Laravel, 그리고 각종 라이브러리는 향후 버전에서 제거될 기능에 대해 deprecation 경고를 발생시키는 경우가 많습니다. 이러한 경고를 로그로 남기고 싶다면, config/logging.php에서 deprecations 항목에 원하는 채널을 지정하면 됩니다.
'deprecations' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
'channels' => [
// ...
]또는 deprecations라는 이름의 로그 채널을 직접 정의할 수도 있습니다. 해당 이름의 채널이 존재하면 deprecation 경고는 항상 이 채널로 기록됩니다.
'channels' => [
'deprecations' => [
'driver' => 'single',
'path' => storage_path('logs/php-deprecation-warnings.log'),
],
],로그 스택 구성
앞서 설명한 것처럼, stack 드라이버를 사용하면 여러 채널을 하나의 로그 채널로 묶을 수 있습니다. 실제 운영 환경에서 흔히 사용하는 설정 예시를 살펴보겠습니다.
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['syslog', 'slack'],
],
'syslog' => [
'driver' => 'syslog',
'level' => 'debug',
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'Laravel Log',
'emoji' => ':boom:',
'level' => 'critical',
],
],이 설정을 살펴보면, stack 채널은 channels 옵션을 통해 syslog와 slack 두 채널을 묶고 있습니다. 따라서 로그 메시지를 기록하면 두 채널 모두 해당 메시지를 처리할 기회를 갖습니다. 단, 실제로 메시지가 기록되는지 여부는 메시지의 심각도(level)에 따라 달라집니다.
로그 레벨
위 예시의 syslog와 slack 채널 설정에서 level 옵션을 확인할 수 있습니다. 이 옵션은 해당 채널에 기록되기 위한 최소 로그 레벨을 지정합니다. Laravel의 로깅 기반인 Monolog는 RFC 5424 명세에 정의된 모든 로그 레벨을 지원합니다. 심각도가 높은 순서부터 낮은 순서로 나열하면 다음과 같습니다: emergency, alert, critical, error, warning, notice, info, debug.
예를 들어, debug 메서드로 로그를 기록한다고 가정해 봅시다.
Log::debug('정보성 메시지입니다.');위 설정에서 syslog 채널은 최소 레벨이 debug이므로 이 메시지를 시스템 로그에 기록합니다. 반면 slack 채널의 최소 레벨은 critical이므로 이 메시지는 Slack으로 전송되지 않습니다.
반대로 emergency 레벨로 로그를 기록하면, 두 채널의 최소 레벨을 모두 초과하므로 시스템 로그와 Slack 양쪽 모두에 기록됩니다.
Log::emergency('시스템이 다운되었습니다!');로그 메시지 작성
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\Http\Controllers\Controller;
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('무언가 발생했습니다!');여러 채널을 조합한 임시(on-demand) 로그 스택을 만들고 싶다면 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' => 'debug',
],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 클래스는 서비스 컨테이너를 통해 resolve되므로, 생성자에 선언된 의존성은 자동으로 주입됩니다.
Monolog 핸들러 채널 생성
Monolog에는 다양한 핸들러가 있지만, Laravel이 이 모든 핸들러에 대한 내장 채널을 제공하지는 않습니다. 대응하는 Laravel 로그 드라이버가 없는 특정 Monolog 핸들러를 사용하고 싶다면, monolog 드라이버를 통해 손쉽게 채널을 생성할 수 있습니다.
monolog 드라이버를 사용할 때는 handler 설정 옵션으로 사용할 핸들러 클래스를 지정합니다. 핸들러 생성자에 필요한 인자가 있다면 with 옵션으로 전달할 수 있습니다.
'logentries' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\SyslogUdpHandler::class,
'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',
],
],사용하는 Monolog 핸들러가 자체 포매터를 제공하는 경우, formatter 옵션 값을 default로 설정하면 됩니다.
'newrelic' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\NewRelicHandler::class,
'formatter' => 'default',
],Monolog 프로세서
Monolog는 메시지를 기록하기 전에 처리하는 프로세서(processor) 기능도 지원합니다. 직접 프로세서를 만들거나 Monolog에서 제공하는 기존 프로세서를 사용할 수 있습니다.
monolog 드라이버 채널의 프로세서를 커스터마이징하려면 채널 설정에 processors 옵션을 추가하세요.
'memory' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\StreamHandler::class,
'with' => [
'stream' => 'php://stderr',
],
'processors' => [
// 단순 지정...
Monolog\Processor\MemoryUsageProcessor::class,
// 옵션 포함...
[
'processor' => Monolog\Processor\PsrLogMessageProcessor::class,
'with' => ['removeUsedContextFields' => true],
],
],
],팩토리를 통한 커스텀 채널 생성
Monolog의 인스턴스화와 설정을 완전히 직접 제어하는 커스텀 채널을 만들고 싶다면, config/logging.php에서 custom 드라이버 타입을 지정하면 됩니다. via 옵션에 Monolog 인스턴스를 생성할 팩토리 클래스 이름을 지정하세요.
'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(/* ... */);
}
}Pail로 로그 실시간 확인
디버깅이나 오류 모니터링을 위해 애플리케이션의 로그를 실시간으로 확인해야 할 때가 종종 있습니다.
Laravel Pail은 커맨드라인에서 직접 애플리케이션 로그를 손쉽게 확인할 수 있는 패키지입니다. 기본 tail 명령어와 달리, Pail은 Sentry나 Flare를 포함한 모든 로그 드라이버와 호환됩니다. 또한 원하는 로그를 빠르게 찾을 수 있도록 다양한 필터 옵션을 제공합니다.
설치
WARNING
Laravel Pail을 사용하려면 PHP 8.2 이상과 PCNTL 확장이 필요합니다.
Composer로 Pail을 프로젝트에 설치하세요.
composer require laravel/pail사용법
로그 실시간 확인을 시작하려면 pail 명령어를 실행하세요.
php artisan pail출력 상세도를 높이고 로그 내용이 잘리지 않도록 하려면 -v 옵션을 사용하세요.
php artisan pail -v최대 상세도로 예외 스택 트레이스까지 확인하려면 -vv 옵션을 사용하세요.
php artisan pail -vv실시간 확인을 중지하려면 Ctrl+C를 누르세요.
로그 필터링
`--filter`
--filter 옵션으로 로그의 타입, 파일, 메시지, 스택 트레이스 내용을 기준으로 필터링할 수 있습니다.
php artisan pail --filter="QueryException"`--message`
메시지 내용만을 기준으로 필터링하려면 --message 옵션을 사용하세요.
php artisan pail --message="User created"`--level`
--level 옵션으로 로그 레벨을 기준으로 필터링할 수 있습니다.
php artisan pail --level=error--user
특정 사용자가 인증된 상태에서 기록된 로그만 확인하려면 --user 옵션에 사용자 ID를 전달하세요.
php artisan pail --user=1