soulbork's avatar

Send logs to a custom path with variables

I am building an app that lets users play games. I want to log all the events that occur in a game, such as when a player joins the game, and make that log visible to players. In my mind, the best way to do this is to create individual game log files like 'logs/games/{$game_id}.log'.

I have setup a custom log channel in config\logging.php but I cannot set a dynamic path. If I try something like 'path' => storage_path('logs/games/' . $game_id . '.log') I get an error message that $game_id is undefined, which makes sense. The problem is I cannot find any way to do this.

I have tried looking into tap but I haven't been able to make this work. My app is only going to know the game_id at the time of the event, so I cannot define a path until that happens. Any suggestions are very welcome.

0 likes
2 replies
soulbork's avatar

I'm using this workaround from the docs in the meantime, though it's obviously not ideal. Posting it here in case anyone with the same problem finds this question. Still working on a more structured approach.

        Log::build([
            'driver' => 'single',
            'path' => storage_path('logs/games/' . $game_id . '.log'),
          ])->info("info");
soulbork's avatar
soulbork
OP
Best Answer
Level 1

Solution found:

  1. Create a file in app/Logging/RuntimeFileHandler.php
namespace App\Logging;

use Monolog\Handler\AbstractProcessingHandler;
use Monolog\LogRecord;

class RuntimeFileHandler extends AbstractProcessingHandler
{
    protected function write(LogRecord $record): void
    {
        $filename = $this->getFilename($record);
        file_put_contents($filename, (string) $record['formatted'], FILE_APPEND);
    }

    protected function getFilename(LogRecord $record): string
    {
        // You can customize this method to generate the desired filename based on the log record.
        // Here's an example that uses the 'context' key to store the desired filename:
        $filename = $record['context']['filename'] ?? 'default.log';

        // Make sure the logs directory exists
        $logsDirectory = storage_path('logs/custom');
        if (!file_exists($logsDirectory)) {
            mkdir($logsDirectory, 0777, true);
        }

        return $logsDirectory . '/' . $filename;
    }
}

\2. Update channels in config/logging.php:

use App\Logging\RuntimeFileHandler;
use Monolog\Formatter\LineFormatter;

return [
    // ...

    'channels' => [
        // ...

        'runtime_file' => [
            'driver' => 'custom',
            'via' => function ($config) {
                $handler = new RuntimeFileHandler(Logger::DEBUG);
                $handler->setFormatter(new LineFormatter(null, null, true, true));

                return new Logger('runtime_file', [$handler]);
            },
        ],
    ],
];

\3. Call it like this

use Illuminate\Support\Facades\Log;

Log::channel('runtime_file')->info('This is a log message', ['filename' => 'my_log_file.log']);

Please or to participate in this conversation.