로깅
소개
애플리케이션 내부에서 무슨 일이 일어나고 있는지 더 자세히 알아보는 데 도움을 주기 위해 Laravel은 파일, 시스템 오류 로그, 심지어 전체 팀에 알리기 위한 Slack에 메시지를 로깅할 수 있는 강력한 로깅 서비스를 제공합니다.
Laravel 로깅은 "채널"을 기반으로 합니다. 각 채널은 로그 정보를 쓰는 특정 방법을 나타냅니다. 예를 들어, single 채널은 로그 파일을 단일 로그 파일에 쓰는 반면, slack 채널은 로그 메시지를 Slack으로 보냅니다. 로그 메시지는 심각도에 따라 여러 채널에 작성될 수 있습니다.
내부적으로 Laravel은 다양한 강력한 로그 핸들러를 지원하는 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 드라이버입니다. |
monolog 및 custom 드라이버에 대한 자세한 내용은 고급 채널 사용자 정의에 대한 설명서를 확인하세요.
채널 이름 구성
기본적으로 Monolog는 production 또는 local과 같은 현재 환경과 일치하는 "채널 이름"으로 인스턴스화됩니다. 이 값을 변경하려면 채널 구성에 name 옵션을 추가하면 됩니다.
'stack' => [ 'driver' => 'stack', 'name' => 'channel-name', 'channels' => ['single', 'slack'],],
채널 전제 조건
단일 및 일일 채널 구성
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 팀에 대해 구성한 수신 웹훅 URL과 일치해야 합니다.
기본적으로 Slack은 critical 수준 이상에서만 로그를 수신합니다. 그러나 LOG_LEVEL 환경 변수를 사용하거나 Slack 로그 채널의 구성 배열 내에서 level 구성 옵션을 수정하여 이를 조정할 수 있습니다.
더 이상 사용되지 않는 경고 로깅
PHP, Laravel 및 기타 라이브러리는 종종 일부 기능이 더 이상 사용되지 않으며 향후 버전에서 제거될 것임을 사용자에게 알립니다. 이러한 더 이상 사용되지 않는 경고를 기록하려면 LOG_DEPRECATIONS_CHANNEL 환경 변수를 사용하거나 애플리케이션의 config/logging.php 구성 파일 내에서 기본 deprecations 로그 채널을 지정할 수 있습니다.
'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, ],],
로그 채널 설정입니다.
스택 채널은 'syslog'와 'slack' 채널을 사용하며, 예외를 무시하지 않습니다.
syslog 채널은 시스템 로그 드라이버를 사용하고, 로그 레벨은 환경변수 LOG_LEVEL에 설정된 값(기본값: debug)을 사용하며, 시설은 환경변수 LOG_SYSLOG_FACILITY에 설정된 값(기본값: LOG_USER)을 사용합니다. 자리 표시자를 대체합니다.
slack 채널은 슬랙 드라이버를 사용하고, 웹훅 URL은 환경변수 LOG_SLACK_WEBHOOK_URL에 설정된 값을 사용합니다. 사용자 이름은 환경변수 LOG_SLACK_USERNAME에 설정된 값(기본값: Laravel Log)을 사용하고, 이모지는 환경변수 LOG_SLACK_EMOJI에 설정된 값(기본값: :boom:)을 사용합니다. 로그 레벨은 환경변수 LOG_LEVEL에 설정된 값(기본값: critical)을 사용하고, 자리 표시자를 대체합니다.
이 구성을 분석해 보겠습니다. 먼저, stack 채널은 channels 옵션을 통해 syslog 및 slack이라는 두 개의 다른 채널을 통합하는 것을 알 수 있습니다. 따라서 메시지를 로깅할 때 이러한 두 채널 모두 메시지를 로깅할 기회를 갖게 됩니다. 그러나 아래에서 보게 되듯이 이러한 채널이 실제로 메시지를 로깅하는지 여부는 메시지의 심각도 / "수준"에 따라 결정될 수 있습니다.
로그 수준
위 예제의 syslog 및 slack 채널 구성에 있는 level 구성 옵션을 주목하십시오. 이 옵션은 채널에서 로깅하기 위해 메시지가 가져야 하는 최소 "수준"을 결정합니다. Laravel의 로깅 서비스를 구동하는 Monolog는 RFC 5424 사양에 정의된 모든 로그 수준을 제공합니다. 심각도 내림차순으로 이러한 로그 수준은 emergency, alert, critical, error, warning, notice, info 및 debug입니다.
따라서 debug 메서드를 사용하여 메시지를 로깅한다고 가정해 보겠습니다.
Log::debug('정보 메시지입니다.');
구성된 대로라면 syslog 채널은 메시지를 시스템 로그에 쓰지만, 오류 메시지가 critical 이상이 아니므로 Slack으로 전송되지 않습니다. 그러나 emergency 메시지를 로깅하면 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 ]); // ... }}
큐 대기 중인 작업을 처리하는 동안 로그 컨텍스트를 공유해야 하는 경우 작업 미들웨어를 활용할 수 있습니다.
특정 채널에 쓰기
경우에 따라 애플리케이션의 기본 채널이 아닌 다른 채널에 메시지를 로깅할 수 있습니다. Log 파사드에서 channel 메서드를 사용하여 구성 파일에 정의된 채널을 검색하고 로깅할 수 있습니다.
use Illuminate\Support\Facades\Log; Log::channel('slack')->info('무언가 발생했습니다!');
여러 채널로 구성된 주문형 로깅 스택을 만들려면 stack 메서드를 사용할 수 있습니다.
Log::stack(['single', 'slack'])->info('무언가 발생했습니다!');
주문형 채널
애플리케이션의 logging 구성 파일에 해당 구성이 없어도 런타임에 구성을 제공하여 주문형 채널을 만드는 것도 가능합니다. 이를 위해 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%' )); } }}
모든 "탭" 클래스는 서비스 컨테이너에서 해결되므로 필요한 생성자 종속성이 자동으로 주입됩니다.
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는 로깅하기 전에 메시지를 처리할 수도 있습니다. 고유한 프로세서를 만들거나 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, ],],
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은 명령줄에서 직접 Laravel 애플리케이션의 로그 파일을 쉽게 분석할 수 있도록 하는 패키지입니다. 표준 tail 명령과 달리 Pail은 Sentry 또는 Flare를 포함한 모든 로그 드라이버와 함께 작동하도록 설계되었습니다. 또한 Pail은 필요한 항목을 빠르게 찾는 데 도움이 되는 유용한 필터 집합을 제공합니다.
설치
시작하려면 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