Laravel 8 logging AWS Cloudwatch, cách implement log tới AWS Cloudwatch và truy vấn cloudwatch cho việc đọc query

Mở đầu Logging là một việc cực kì quan trọng, và Cloudwatch là một nền tảng visualize và query log khá tốt đến từ AWS, chắc chắn các hệ thống trên AWS thường đều log tới e này, hôm nay mình xin giới thiệu cách để laravel 8 có thể dễ dàng push log tới

Mở đầu

Logging là một việc cực kì quan trọng, và Cloudwatch là một nền tảng visualize và query log khá tốt đến từ AWS, chắc chắn các hệ thống trên AWS thường đều log tới e này, hôm nay mình xin giới thiệu cách để laravel 8 có thể dễ dàng push log tới Cloudwatch, bắt đầu thôi 😁

Cách implement

Cài package

composer require maxbanton/cwh:^2.0

Tạo class CloudWatchLoggerFactory để tạo drive custom push lên cloudwatch

classCloudWatchLoggerFactory{/**
     * Create a custom Monolog instance.
     *
     * @param  array  $config
     * @return MonologLogger
     */publicfunction__invoke(array$config){$sdkParams=$config['sdk'];$tags=$config['tags']??[];$name=$config['name']??'cloudwatch';// Instantiate AWS SDK CloudWatch Logs Client$client=newCloudWatchLogsClient($sdkParams);// Log group name, will be created if none$groupName=$config['group'];// Log stream name, will be created if none$streamName=$config['stream'];// Days to keep logs, 14 by default. Set to `null` to allow indefinite retention.$retentionDays=$config['retention'];// Instantiate handler (tags are optional)$handler=newCloudWatch($client,$groupName,$streamName,$retentionDays,10000,$tags);// Create a log channel$logger=newLogger($name);// Set handler$logger->pushHandler($handler);return$logger;}}

Trong config/logging.php, thêm setting driver custom cho loại log này 😄

'channels'=>['stack'=>['driver'=>'stack','channels'=>['single'],'ignore_exceptions'=>false,],'cloudwatch_error_log'=>['driver'=>'custom','via'=>AppServicesCloudWatchLoggerFactory::class,'sdk'=>['region'=>env('AWS_DEFAULT_REGION','ap-northeast-1'),'version'=>'latest',],'retention'=>30,'level'=>'info','group'=>env('CLOUDWATCH_LOG_GROUP','group-log'),'stream'=>env('CLOUDWATCH_LOG_STREAM','error-log'),],

Lựa chọn exception hợp lí để push lên cloudwatch, ở đây mình sẽ edit trong app/Exceptions/Handler.php, đây là nơi laravel hỗ trợ cho chúng ta custom cho toàn bộ quá trình handle Exception, bao gồm việc logging error và format response error của API, như lỗi 404 thì trả về thế nào, lỗi 500 thì trả về thế nào, … về phần mình, mình đính kèm thêm param request vào lỗi, như vậy chúng ta sẽ nắm bắt được chính xác request nào đã gây ra lỗi, thuận tiện hơn cho việc debug.

/**
     * Report or log an exception.
     *
     * @param  Throwable  $e
     * @return void
     *
     * @throws Throwable
     */publicfunctionreport(Throwable$e){$exceptionExcluse=[RouteNotFoundException::class,NotFoundHttpException::class,AuthorizationException::class,ValidationException::class,];if(!in_array(get_class($e),$exceptionExcluse)){$this->logPrettyError($e);}parent::report($e);}

Như các bạn thấy, các lỗi common như RouteNotFoundException hay NotFoundHttpException hoặc ValidationException, là do user nhập sai url hoặc form input thôi, nên ko đủ quan trọng để chúng ta phải log lại để điều tra :#)

Hàm log lỗi thêm param request:

privatefunctionlogPrettyError(Throwable$e){$request=request();$log=['access'=>['request'=>$request->all(),'method'=>$request->method(),'path'=>$request->path(),],'error'=>['class'=>get_class($e),'code'=>$e->getCode(),'message'=>$e->getMessage(),'file'=>$e->getFile(),'line'=>$e->getLine(),],];getLogger()->error(json_encode($log));}

Đừng quên chọn driver logging đúng với môi trường cần log cloudwatch nhé (thường là STG và PROD)

LOG_CHANNEL=cloudwatch_error_log
CLOUDWATCH_LOG_GROUP=
CLOUDWATCH_LOG_STREAM=

Thêm 2 helper để viết log trong code được tiện lợi hơn

/**
 * Get logger
 */if(!function_exists('getLogger')){functiongetLogger(){returnLog::channel(env('LOG_CHANNEL','daily'));}}/**
 * Log info
 */if(!function_exists('logInfo')){functionlogInfo($info){getLogger()->info($info);}}/**
 * Log error
 */if(!function_exists('logError')){functionlogError($e){$logger=getLogger();if($einstanceofException){$logger->error($e->getMessage().' on line '.$e->getLine().' of file '.$e->getFile());}else{$logger->error($e);}}}

Cách filter trên cloudwatch thuận tiện cho việc đọc log

Dựa trên việc log phía trên, mình có 1 số cách query đọc log thuận tiện hơn

  • query log lấy 20 cái gần nhất
fields @timestamp, @message | sort @timestamp desc | limit 25
  • query get số lượng message chứa Exception trong vòng 1h
filter @message like /Exception/ 
    | stats count(*) as exceptionCount by bin(1h)
    | sort exceptionCount desc
    
  • query log không chứa Exception
fields @message | filter @message not like /Exception/
  • query đọc log lỗi đến từ API product
fields @timestamp, @message
| sort @timestamp desc
| filter @message like /ERROR(.*)api\/v1\/products/

Tham khảo:
https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html

Hy vọng việc logging cloudwatch với laravel sẽ cực kì dễ dàng cho bà con, thank for reading 😁

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ