Skip to content

Instantly share code, notes, and snippets.

@danilopinotti
Created September 20, 2023 12:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danilopinotti/9f7c9101e0514c5f0ff3608bebe5535c to your computer and use it in GitHub Desktop.
Save danilopinotti/9f7c9101e0514c5f0ff3608bebe5535c to your computer and use it in GitHub Desktop.
Laravel setup Logger as JSON
<?php
namespace App\Support;
use Illuminate\Support\Facades\App;
use Symfony\Component\Uid\Factory\UlidFactory;
class ExecutionId implements \Stringable
{
private string $id;
public function __construct()
{
$prefix = App::runningInConsole() ? 'CLI:' : 'WEB:';
$this->id = $prefix . (new UlidFactory)->create();
}
public function get(): string
{
return $this->id;
}
public function __toString()
{
return $this->id;
}
}
<?php
namespace App\Support;
use Psr\Log\LoggerInterface;
/**
* @method static void emergency(string $message, array $context = [])
* @method static void alert(string $message, array $context = [])
* @method static void critical(string $message, array $context = [])
* @method static void error(string $message, array $context = [])
* @method static void warning(string $message, array $context = [])
* @method static void notice(string $message, array $context = [])
* @method static void info(string $message, array $context = [])
* @method static void debug(string $message, array $context = [])
* @method static void log(string $level, string $message, array $context = [])
* @method static LoggerInterface withContext(array $context)
*/
class Logger
{
public static function __callStatic(string $name, array $arguments)
{
$contextName = trim(basename(
data_get(debug_backtrace(), '0.file', '')
), '.php');
return app('log')
->withContext([
'executionId' => (string) app(ExecutionId::class),
'contextName' => $contextName,
])
->{$name}(...$arguments);
}
}
<?php
return [
// ..
'channels' => [
// ...
'json_daily' => [
'driver' => 'daily',
'tap' => [\App\Support\Log\LogTapJson::class],
'path' => storage_path('logs/laravel-json.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => 30,
],
],
]
<?php
namespace App\Support\Log;
use App\Support\ExecutionId;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Monolog\Formatter\JsonFormatter;
class LogJsonFormatter extends JsonFormatter
{
/**
* @inheritDoc
*/
public function format(array $record): string
{
$normalized = $this->normalize($record);
if (isset($normalized['context']) && $normalized['context'] === []) {
if ($this->ignoreEmptyContextAndExtra) {
unset($normalized['context']);
} else {
$normalized['context'] = new \stdClass;
}
}
$data = [
'level' => strtolower($normalized['level_name']),
'message' => $normalized['message'],
'context' => data_get($normalized, 'context.contextName'),
'data' => Arr::except(
data_get($normalized, 'context'),
['executionId', 'contextName']
),
'executionId' => (string) app(ExecutionId::class),
'timestamp' => Carbon::make($normalized['datetime']),
];
return $this->toJson($data, true) . ($this->appendNewline ? "\n" : '');
}
}
<?php
namespace App\Providers;
use App\Support\ExecutionId;
use Illuminate\Support\ServiceProvider;
class LogServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->singleton(ExecutionId::class, static function () {
return new ExecutionId();
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
}
<?php
namespace App\Support\Log;
use Illuminate\Log\Logger;
class LogTapJson
{
/**
* Customize the given logger instance.
*/
public function __invoke(Logger $logger): void
{
foreach ($logger->getHandlers() as $handler) {
$handler->setFormatter(new LogJsonFormatter());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment