faustoff / laravel-contextify
Contextual logging with inline notifications for Laravel.
Installs: 1 009
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/faustoff/laravel-contextify
Requires
- php: ^8.0
- illuminate/contracts: ^9.0|^10.0|^11.0|^12.0
- illuminate/log: ^9.0|^10.0|^11.0|^12.0
- illuminate/notifications: ^9.0|^10.0|^11.0|^12.0
- illuminate/support: ^9.0|^10.0|^11.0|^12.0
- monolog/monolog: ^2.0|^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- laravel-notification-channels/telegram: *
- orchestra/testbench: ^7.0 || ^8.0 || ^9.0 || ^10.0
- phpunit/phpunit: ^9.6 || ^10.0 || ^11.0
Suggests
- ext-pcntl: Allows to use Console Terminatable trait
- laravel-notification-channels/telegram: Allows sending notifications to Telegram
- dev-main
- v4.x-dev
- 4.0.0
- 3.6.0
- 3.5.0
- 3.4.0
- 3.3.0
- 3.2.0
- 3.1.4
- 3.1.3
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.4
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0.0
- 2.7.3
- 2.7.2
- 2.7.1
- 2.7.0
- 2.6.0
- 2.5.1
- 2.5.0
- 2.4.1
- 2.4.0
- 2.3.1
- 2.3.0
- 2.2.0
- 2.1.0
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- 1.1.1
- 1.1.0
- 1.0.0
- dev-notify-except-channels
- dev-auto-setup-exception-handler
- dev-notifiable
- dev-master
This package is auto-updated.
Last update: 2025-12-04 06:13:34 UTC
README
Laravel Contextify
Contextual logging with inline notifications for Laravel.
<?php 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:
- Inline Notifications — send notifications alongside logging without splitting code into multiple lines for logging and notifying.
- 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
- 📧 Notification Support: Send notifications via mail, telegram, or any Laravel notification channel
- 🔍 Automatic Context Enrichment: Logs and notifications automatically include extra contextual data from configured Context Providers
- 🔌 Custom Context Providers: Extend built-in providers with your own
- 🔄 Static & Dynamic Providers: Static (cached) and dynamic (refreshed on each call) providers
- 🎯 Group-Based Context: Separate Context Providers for logs and notifications
- 📊 PSR-3 Log Levels: All standard log levels (debug, info, notice, warning, error, critical, alert, emergency)
- 🎨 Custom Notifications: Extend notification classes and add custom channels
- 🔔 Channel Filtering: Filter channels with
onlyandexceptparameters - 🔄 Fluent API: Chain methods for readable code
- ⚡ Monolog Integration: Integrates with Laravel's logging via Monolog processors
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:
<?php 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:
<?php 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
If necessary, you can override the default implementation of the LogNotification:
<?php namespace App\Notifications; use Faustoff\Contextify\Notifications\LogNotification; class CustomLogNotification extends LogNotification { // Override methods as needed }
Update configuration:
'notifications' => [ 'class' => \App\Notifications\CustomLogNotification::class, // ... other notifications settings ],
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:
<?php 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, // ... other notifications settings ],
To disable automatic exception notifications, set reportable to null:
'notifications' => [ 'reportable' => null, // ... other notifications settings ],
Note:
ExceptionNotificationFailedExceptionprevents 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:
- ProcessIdContextProvider: Adds the current PHP process ID (
pid) - TraceIdContextProvider: Generates a unique 16-character hexadecimal trace ID (
trace_id) for distributed tracing - HostnameContextProvider: Adds the server hostname (
hostname) - EnvironmentContextProvider: Adds the application environment (
environment)
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:
<?php 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:
- CallContextProvider: Adds the file path and line number (
file) and class name (class) of the calling code - PeakMemoryUsageContextProvider: Adds the peak memory usage in bytes (
peak_memory_usage) - DateTimeContextProvider: Adds the current date and time in Laravel log format (
datetime)
Creating Custom Context Providers
Implement StaticContextProviderInterface or DynamicContextProviderInterface:
<?php 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:
<?php use App\Context\Providers\CustomContextProvider; use Faustoff\Contextify\Context\Providers\CallContextProvider; use Faustoff\Contextify\Context\Providers\EnvironmentContextProvider; use Faustoff\Contextify\Context\Providers\HostnameContextProvider; use Faustoff\Contextify\Context\Providers\ProcessIdContextProvider; use Faustoff\Contextify\Context\Providers\TraceIdContextProvider; return [ 'logs' => [ 'providers' => [ // Built-in providers ProcessIdContextProvider::class, TraceIdContextProvider::class, CallContextProvider::class, // Custom providers CustomContextProvider::class, ], // ... other logs settings ], 'notifications' => [ 'providers' => [ // Built-in providers HostnameContextProvider::class, ProcessIdContextProvider::class, TraceIdContextProvider::class, EnvironmentContextProvider::class, CallContextProvider::class, // Custom providers CustomContextProvider::class, ], // ... other notifications settings ], ];
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 entriesnotifications.providers— providers for notifications
Example:
<?php use Faustoff\Contextify\Context\Providers\CallContextProvider; use Faustoff\Contextify\Context\Providers\EnvironmentContextProvider; use Faustoff\Contextify\Context\Providers\HostnameContextProvider; use Faustoff\Contextify\Context\Providers\PeakMemoryUsageContextProvider; use Faustoff\Contextify\Context\Providers\ProcessIdContextProvider; use Faustoff\Contextify\Context\Providers\TraceIdContextProvider; return [ 'logs' => [ 'providers' => [ ProcessIdContextProvider::class, // Shared TraceIdContextProvider::class, // Shared CallContextProvider::class, // Logs only PeakMemoryUsageContextProvider::class, // Logs only ], // ... other logs settings ], 'notifications' => [ 'providers' => [ HostnameContextProvider::class, // Notifications only EnvironmentContextProvider::class, // Notifications only ProcessIdContextProvider::class, // Shared TraceIdContextProvider::class, // Shared ], // ... other notifications settings ], ];
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', '')), // ... other notifications settings ],
Custom Notification Channels
For example, to add Slack notifications, you need to:
- Create a custom notification class with a
toSlack()method implemented:
<?php 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); } }
- Create a custom notifiable class with a
routeNotificationForSlack()method implemented:
<?php 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'); } }
-
Configure Slack in
config/services.php. -
Update
config/contextify.php:
'notifications' => [ 'class' => \App\Notifications\CustomLogNotification::class, 'notifiable' => \App\Notifications\CustomNotifiable::class, 'channels' => [ 'mail', 'telegram', 'slack' ], // ... other notifications settings ],
Note: For exception notifications, extend
ExceptionNotificationand add thetoSlack()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:
<?php 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:
<?php 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:
TerminatableV62forsymfony/console:<6.3(Laravel 9, 10)TerminatableV63forsymfony/console:^6.3(Laravel 9, 10)TerminatableV70forsymfony/console:^7.0(Laravel 11+)
<?php 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.