faustoff/laravel-contextify

Contextual logging with inline notifications for Laravel.

Installs: 1 056

Dependents: 0

Suggesters: 0

Security: 0

Stars: 5

Watchers: 1

Forks: 0

Open Issues: 1

pkg:composer/faustoff/laravel-contextify

4.1.1 2025-12-31 12:06 UTC

README

Packagist Version Packagist Downloads Packagist License GitHub Repo stars

Laravel Contextify

Contextual logging with inline notifications for Laravel.

use Faustoff\Contextify\Facades\Contextify;

Contextify::notice('Order created', ['id' => $id])->notify(['mail']);
// [2025-01-01 12:00:00] production.NOTICE: Order created {"id":1} {"trace_id":"4f9c2a1b"}

Laravel Contextify enhances Laravel's logging with two main features:

  1. Inline Notificationssend notifications alongside logging without splitting code into multiple lines for logging and notifying.
  2. Automatic Context Enrichment — logs and notifications include extra contextual data from configured Context Providers (built-in: Trace ID, Process ID, Hostname, Call file and line, and more), helping you keep messages short and clean by moving additional context out of the message itself into a separate area.

Provides Contextify facade compatible with Laravel's Log facade: same methods (debug, info, notice, warning, error, critical, alert, emergency) with identical parameters, plus a chainable notify() method.

Name origin: “Contextify” combines Context and Notify, reflecting its dual purpose — to enrich logs with contextual data and to send notifications for log events.

Features

Requirements

  • PHP 8.0 or higher
  • Laravel 8.0 or higher
  • Monolog 2.0 or higher

Installation

Install the package via Composer:

composer require faustoff/laravel-contextify

Configuration

Optionally, publish the configuration file:

php artisan vendor:publish --tag=contextify-config

This creates config/contextify.php for configuring Context Providers and notifications.

Environment Variables

Add to .env to configure notification recipients:

CONTEXTIFY_MAIL_ADDRESSES=admin@example.com,team@example.com
CONTEXTIFY_TELEGRAM_CHAT_ID=123456789

Note: Telegram notifications require the laravel-notification-channels/telegram package to be installed manually.

Usage

Writing Logs

Use the Contextify facade like Laravel's Log facade. Logs automatically include extra context from Context Providers configured for logging:

use Faustoff\Contextify\Facades\Contextify;

Contextify::debug('Debug message', ['key' => 'value']);
// [2025-01-01 12:00:00] local.DEBUG: Debug message {"key":"value"} {"pid":12345,"trace_id":"4f9c2a1bd3e7a8f0","file":"app/Services/ExampleService.php:42","class":"App\\Services\\ExampleService"}

Contextify::info('User logged in', ['user_id' => 123]);
// [2025-01-01 12:00:00] local.INFO: User logged in {"user_id":123} {"pid":12345,"trace_id":"4f9c2a1bd3e7a8f0","file":"app/Http/Controllers/Auth/LoginController.php:55","class":"App\\Http\\Controllers\\Auth\\LoginController"}

Contextify::notice('Important notice');
// [2025-01-01 12:00:00] local.NOTICE: Important notice  {"pid":12345,"trace_id":"4f9c2a1bd3e7a8f0","file":"routes/web.php:10","class":null}

// ... and the same for warning, error, critical, alert and emergency

Sending Notifications

Chain notify() after any logging method to send notifications. Notifications include the log message, context, and extra context from Context Providers configured for notifications.

Filter channels using only and except parameters:

use Faustoff\Contextify\Facades\Contextify;

Contextify::error('Payment processing failed', ['order_id' => 456])->notify();
// [2025-01-01 12:00:00] local.ERROR: Payment processing failed {"order_id":456} {"pid":12345,"trace_id":"4f9c2a1bd3e7a8f0","file":"app/Http/Controllers/Api/OrderController.php:133","class":"App\\Http\\Controllers\\Api\\OrderController"}
// Notification with context {"order_id":456} and extra context sent to all configured notification channels

Contextify::critical('Database connection lost')->notify(only: ['mail']);
// [2025-01-01 12:00:00] local.CRITICAL: Database connection lost  {"pid":12345,"trace_id":"4f9c2a1bd3e7a8f0","file":"app/Console/Commands/MonitorCommand.php:71","class":"App\\Console\\Commands\\MonitorCommand"}
// Notification with extra context sent to a mail channel only

Contextify::alert('Security breach detected')->notify(except: ['telegram']);
// [2025-01-01 12:00:00] local.ALERT: Security breach detected  {"pid":12345,"trace_id":"4f9c2a1bd3e7a8f0","file":"app/Providers/AppServiceProvider.php:25","class":"App\\Providers\\AppServiceProvider"}
// Notification with extra context sent to all configured notification channels except a Telegram channel

Control notification sending conditionally using the shouldNotify parameter:

use Illuminate\Support\Facades\App;
use Faustoff\Contextify\Facades\Contextify;

Contextify::error('Payment processing failed')->notify(shouldNotify: App::isProduction());

If necessary, you can override the default implementation of the LogNotification:

namespace App\Notifications;

use Faustoff\Contextify\Notifications\LogNotification;

class CustomLogNotification extends LogNotification
{
    // Override methods as needed
}

Update configuration:

'notifications' => [
    'class' => \App\Notifications\CustomLogNotification::class,
],

Exception Notifications

Exceptions are automatically reported via notifications (enabled by default). Notifications include exception details (message and stack trace) and extra context from Context Providers configured for notifications.

If necessary, you can override the default implementation of the ExceptionNotification:

namespace App\Notifications;

use Faustoff\Contextify\Notifications\ExceptionNotification;

class CustomExceptionNotification extends ExceptionNotification
{
    // Override methods as needed
}

Update configuration:

'notifications' => [
    'exception_class' => \App\Notifications\CustomExceptionNotification::class,
],

To disable automatic exception notifications, set reportable to null:

'notifications' => [
    'reportable' => null,
],

Note: ExceptionNotificationFailedException prevents infinite loops when exception notifications fail.

Context Providers

Context Providers add extra contextual data to logs and notifications, helping you keep log entry and notification messages short and clean by moving additional context out of the message itself into a separate context area. The contextual data is still present in the log entry or notification, but it's separated from the message itself—keeping the message focused while preserving all information for searching and analysis. You no longer need to worry about explicitly passing the required contextual data each time, as it will be added automatically.

Static Context Providers

Static providers return data that remains constant throughout the request/process lifecycle. They implement StaticContextProviderInterface.

Built-in:

Refreshing Static Context

Static context is cached during application boot. Use touch() to refresh it manually, useful when a process is forked (e.g., queue workers) to generate a new trace ID:

use Faustoff\Contextify\Facades\Contextify;
use Faustoff\Contextify\Context\Providers\TraceIdContextProvider;

// Refresh specific provider (e.g., generate new trace ID)
Contextify::touch(TraceIdContextProvider::class);

// Refresh all static providers
Contextify::touch();

Dynamic Context Providers

Dynamic providers refresh data on each log call. They implement DynamicContextProviderInterface.

Built-in:

Creating Custom Context Providers

Implement StaticContextProviderInterface or DynamicContextProviderInterface:

namespace App\Context\Providers;

use Faustoff\Contextify\Context\Contracts\StaticContextProviderInterface;

class CustomContextProvider implements StaticContextProviderInterface
{
    public function getContext(): array
    {
        return [
            // implement ...
        ];
    }
}

Registering Custom Providers

Add custom providers to config/contextify.php:

use App\Context\Providers\CustomContextProvider;

return [
    'logs' => [
        'providers' => [
            // Built-in providers...
            
            // Custom providers
            CustomContextProvider::class,
        ],
    ],

    'notifications' => [
        'providers' => [
            // Built-in providers...
            
            // Custom providers
            CustomContextProvider::class,
        ],
    ],
];

Group-Based Context

Define separate Context Providers for logs and notifications. If a provider appears in both sets, the same context data is used for both.

Configure in config/contextify.php:

  • logs.providers — providers for log entries
  • notifications.providers — providers for notifications

Example:

use Faustoff\Contextify\Context\Providers\CallContextProvider;
use Faustoff\Contextify\Context\Providers\EnvironmentContextProvider;
use Faustoff\Contextify\Context\Providers\TraceIdContextProvider;

return [
    'logs' => [
        'providers' => [
            TraceIdContextProvider::class,     // Shared
            CallContextProvider::class,        // Logs only
        ],
    ],

    'notifications' => [
        'providers' => [
            TraceIdContextProvider::class,     // Shared
            EnvironmentContextProvider::class, // Notifications only
        ],
    ],
];

Notifications

Supports mail and telegram channels out of the box. Mail works immediately; Telegram requires the laravel-notification-channels/telegram package.

Configuration

Configure channels in config/contextify.php:

'notifications' => [
    /*
     * Use associative array format ['channel' => 'queue'] to specify
     * queue per channel. Simple array ['channel'] uses 'default' queue.
     */
    'channels' => [
        'mail' => 'mail-queue',
        'telegram' => 'telegram-queue',
    ],
    
    'mail_addresses' => explode(',', env('CONTEXTIFY_MAIL_ADDRESSES', '')),
    
    'telegram_chat_id' => env('CONTEXTIFY_TELEGRAM_CHAT_ID'),
],

Custom Notification Channels

For example, to add Slack notifications, you need to:

  1. Create a custom notification class with a toSlack() method implemented:
namespace App\Notifications;

use Faustoff\Contextify\Notifications\LogNotification;
use Illuminate\Notifications\Messages\SlackMessage;

class CustomLogNotification extends LogNotification
{
    public function toSlack($notifiable): SlackMessage
    {
        // See https://laravel.com/docs/12.x/notifications#formatting-slack-notifications
        
        return (new SlackMessage())
            ->content(ucfirst($this->level) . ': ' . $this->message);
    }
}
  1. Create a custom notifiable class with a routeNotificationForSlack() method implemented:
namespace App\Notifications;

use Faustoff\Contextify\Notifications\Notifiable;

class CustomNotifiable extends Notifiable
{
    public function routeNotificationForSlack($notification): string
    {
        // See https://laravel.com/docs/12.x/notifications#routing-slack-notifications
    
        return config('services.slack.notifications.channel');
    }
}
  1. Configure Slack in config/services.php.

  2. Update config/contextify.php:

'notifications' => [
    'class' => \App\Notifications\CustomLogNotification::class,
    
    'notifiable' => \App\Notifications\CustomNotifiable::class,
    
    'channels' => [
        'mail',
        'telegram',
        'slack'
    ],
],

Note: For exception notifications, extend ExceptionNotification and add the toSlack() method similarly.

Want more notification channels? You are welcome to Laravel Notifications Channels.

Console Commands

Tracking

Use Faustoff\Contextify\Console\Trackable trait to log command start, finish, and execution time:

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Faustoff\Contextify\Console\Trackable;
use Faustoff\Contextify\Facades\Contextify;

class SyncData extends Command
{
    use Trackable;

    protected $signature = 'data:sync';

    public function handle(): int
    {
        // Your business logic here
        
        Contextify::notice('Data was synced');

        return self::SUCCESS;
    }
}

Log:

[2025-01-01 12:00:00] local.DEBUG: Run with arguments {"command":"data:sync"} {"pid":12345,"trace_id":"4f9c2a1bd3e7a8f0","file":"app/Console/Commands/SyncData.php:42","class":"App\\Console\\Commands\\SyncData"}
[2025-01-01 12:00:00] local.NOTICE: Data was synced {"pid":12345,"trace_id":"4f9c2a1bd3e7a8f0","file":"app/Console/Commands/SyncData.php:42","class":"App\\Console\\Commands\\SyncData"}
[2025-01-01 12:00:00] local.DEBUG: Execution time: 1 second {"pid":12345,"trace_id":"4f9c2a1bd3e7a8f0","file":"app/Console/Commands/SyncData.php:42","class":"App\\Console\\Commands\\SyncData"}

Output Capturing

Use Faustoff\Contextify\Console\Outputable trait to capture Laravel console output from info()-like methods and store it in logs:

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Faustoff\Contextify\Console\Outputable;

class SyncData extends Command
{
    use Outputable;

    protected $signature = 'data:sync';

    public function handle(): int
    {
        // You business logic here
        
        $this->info('Data was synced');

        return self::SUCCESS;
    }
}

Log:

[2025-01-01 12:00:00] local.NOTICE: Data was synced {"pid":12345,"trace_id":"4f9c2a1bd3e7a8f0","file":"app/Console/Commands/SyncData.php:42","class":"App\\Console\\Commands\\SyncData"}

Handling Shutdown Signals

Handle shutdown signals (SIGQUIT, SIGINT, SIGTERM by default) for graceful shutdown. Use the appropriate trait with SignalableCommandInterface:

  • TerminatableV62 for symfony/console:<6.3 (Laravel 9, 10)
  • TerminatableV63 for symfony/console:^6.3 (Laravel 9, 10)
  • TerminatableV70 for symfony/console:^7.0 (Laravel 11+)
namespace App\Console\Commands;

use Faustoff\Contextify\Console\TerminatableV62;
use Illuminate\Console\Command;
use Symfony\Component\Console\Command\SignalableCommandInterface;

class ConsumeStats extends Command implements SignalableCommandInterface
{
    use TerminatableV62;

    protected $signature = 'stats:consume';

    public function handle(): void
    {
        while (true) {
            // ...

            if ($this->shouldTerminate) {
                // Execution terminated by handle shutdown signal
                break;
            }
        }
    }
}

Log:

[2025-01-01 12:00:00] local.WARNING: Received SIGTERM (15) shutdown signal {"pid":12345,"trace_id":"4f9c2a1bd3e7a8f0","file":"app/Console/Commands/ConsumeStats.php:42","class":"App\\Console\\Commands\\ConsumeStats"}

License

This package is open-sourced software licensed under the MIT license.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues, questions, or contributions, please visit the GitHub repository.