로깅
번역일: 2026년 6월 25일
로깅
소개
애플리케이션 내부에서 무슨 일이 일어나고 있는지 파악하기 위해, Laravel은 강력한 로깅 서비스를 제공합니다. 파일, 시스템 에러 로그, 또는 Slack으로 메시지를 보내 팀 전체에 알림을 전달하는 것도 가능합니다.
Laravel 로깅은 채널(channel) 개념을 중심으로 동작합니다. 각 채널은 로그 정보를 기록하는 특정 방식을 나타냅니다. 예를 들어 single 채널은 하나의 파일에 로그를 기록하고, slack 채널은 Slack으로 메시지를 전송합니다. 메시지의 심각도에 따라 여러 채널에 동시에 기록할 수도 있습니다.
내부적으로는 Monolog 라이브러리를 사용합니다. Monolog는 다양하고 강력한 로그 핸들러를 지원하며, Laravel은 이를 쉽게 설정하고 조합할 수 있도록 도와줍니다.
설정
로깅 동작을 제어하는 모든 설정은 config/logging.php 파일에 모여 있습니다. 이 파일에서 로그 채널을 구성할 수 있으므로, 사용 가능한 채널과 각 옵션을 꼼꼼히 살펴보시기 바랍니다.
기본적으로 Laravel은 stack 채널을 사용합니다. stack 채널은 여러 로그 채널을 하나로 묶어주는 역할을 합니다. 스택 구성 방법은 아래 문서를 참고하세요.
사용 가능한 채널 드라이버
각 로그 채널은 드라이버(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 드라이버에 대한 더 자세한 내용은 고급 채널 커스터마이징 문서를 참고하세요.
채널 이름 설정
기본적으로 Monolog는 현재 환경(production, local 등)을 채널 이름으로 사용합니다. 이 값을 변경하려면 채널 설정에 name 옵션을 추가하세요.
'stack' => [
'driver' => 'stack',
'name' => 'channel-name',
'channels' => ['single', 'slack'],
],채널 사전 요건
Single / Daily 채널 설정
single과 daily 채널은 세 가지 선택적 설정 옵션을 제공합니다.
| 이름 | 설명 | 기본값 |
|---|---|---|
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 환경 변수로 설정하며, Slack에서 생성한 수신 웹훅(incoming webhook) URL을 입력해야 합니다.
기본적으로 Slack은 critical 레벨 이상의 로그만 수신합니다. LOG_LEVEL 환경 변수 또는 Slack 채널 설정의 level 옵션으로 이 기준을 조정할 수 있습니다.
지원 중단 경고 로깅
PHP, Laravel, 그 외 라이브러리는 특정 기능이 더 이상 지원되지 않으며 향후 버전에서 제거될 예정임을 알리는 지원 중단(deprecation) 경고를 발생시킵니다. 이 경고를 로그로 남기려면 LOG_DEPRECATIONS_CHANNEL 환경 변수를 설정하거나 config/logging.php에 다음과 같이 지정하세요.
'deprecations' => [
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
],
'channels' => [
// ...
]또는 deprecations라는 이름의 로그 채널을 직접 정의할 수도 있습니다. 해당 이름의 채널이 존재하면 지원 중단 경고는 항상 이 채널에 기록됩니다.
'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('정보성 메시지입니다.');위 설정에서 syslog 채널(최소 레벨: debug)은 이 메시지를 시스템 로그에 기록하지만, slack 채널(최소 레벨: critical)은 debug가 임계값보다 낮으므로 전송하지 않습니다.
반면 emergency 메시지를 기록하면:
Log::emergency('시스템이 다운되었습니다!');emergency는 두 채널의 최소 레벨을 모두 초과하므로, 시스템 로그와 Slack 양쪽에 모두 기록됩니다.
로그 메시지 작성
Log 파사드를 사용하여 로그를 기록할 수 있습니다. RFC 5424에서 정의한 8가지 레벨에 대응하는 메서드를 제공합니다.
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);각 메서드를 호출하면 해당 레벨로 기본 로그 채널에 메시지가 기록됩니다. 아래는 컨트롤러에서 로그를 남기는 예시입니다.
<?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]);특정 채널의 이후 모든 로그 항목에 공통 컨텍스트 정보를 포함시키고 싶을 때는 Log 파사드의 withContext 메서드를 사용합니다. 예를 들어 각 요청에 요청 ID를 부여하여 모든 로그에 포함시키는 미들웨어를 다음과 같이 작성할 수 있습니다.
<?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 채널에 커스텀 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 인스턴스를 커스터마이징하는 클래스를 작성합니다. 이 클래스는 __invoke 메서드 하나만 있으면 되며, Illuminate\Log\Logger 인스턴스를 인자로 받습니다. 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이 모든 핸들러에 대한 내장 채널을 제공하지는 않습니다. 이 경우 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',
],
],핸들러가 자체 포매터를 제공하는 경우, 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,
'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,
],
],이후 Monolog 인스턴스를 생성하는 클래스를 작성합니다. 이 클래스는 __invoke 메서드 하나만 있으면 되며, Monolog 로거 인스턴스를 반환해야 합니다. 채널 설정 배열이 유일한 인자로 전달됩니다.
<?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