MDCLogger is a PHP library that provides logging capabilities based on Mapped Diagnostic Context (MDC). It extends the PSR-3 LoggerInterface and allows you to add global context to your log messages.
Article about MDC Logger in PHP
To install MDCLogger, use Composer:
composer require gabrielanhaia/mdc-logger
PHP 7.4
or higherpsr/log
^3.0
MDC (Mapped Diagnostic Context) allows you to add context information to your log messages. This is useful for tracking information across different parts of your application, such as user sessions, request IDs, or any other data that you need to correlate across log entries.
-
Autowiring with Symfony
If you are using Symfony with autowiring, the
MDCLoggerInterface
will be automatically injected into your services. Ensure you have configured your service container correctly. -
Manual Setup
If you are not using a framework that supports autowiring, you need to set up the
MDCLogger
manually. You must also configure theLoggerInterface
with your actual logger, such as Monolog.
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use MDCLogger\MDCLogger;
use MDCLogger\MDCLoggerInterface;
// Create a Monolog logger instance
$monolog = new Logger('app');
$monolog->pushHandler(new StreamHandler('path/to/your.log', Logger::DEBUG));
// Wrap the Monolog logger with MDCLogger
$mdcLogger = new MDCLogger($monolog);
$mdcLogger->addGlobalContext('user_id', '12345');
$mdcLogger->addGlobalContext('request_id', 'abcde');
$mdcLogger->info('User logged in.');
$mdcLogger->error('An error occurred.', ['exception' => $exception]);
$mdcLogger->clearGlobalContext();
<?php
require 'vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use MDCLogger\MDCLogger;
use MDCLogger\MDCLoggerInterface;
// Create a Monolog logger instance
$monolog = new Logger('app');
$monolog->pushHandler(new StreamHandler('path/to/your.log', Logger::DEBUG));
// Wrap the Monolog logger with MDCLogger
$mdcLogger = new MDCLogger($monolog);
// Adding global context
$mdcLogger->addGlobalContext('user_id', '12345');
$mdcLogger->addGlobalContext('request_id', 'abcde');
// Logging messages
$mdcLogger->info('User logged in.');
$mdcLogger->error('An error occurred.', ['exception' => $exception]);
// Clearing global context
$mdcLogger->clearGlobalContext();
To ensure the MDC context is available across different layers of your application, you should use a singleton instance of MDCLogger
. This instance can be auto-injected throughout your application, allowing you to add context and log messages from various points.
<?php
namespace App\Controller;
use MDCLogger\MDCLoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
class SampleController extends AbstractController
{
private MDCLoggerInterface $mdcLogger;
public function __construct(MDCLoggerInterface $mdcLogger)
{
$this->mdcLogger = $mdcLogger;
}
public function sampleAction(): Response
{
// Add context at the controller level
$this->mdcLogger->addGlobalContext('entity_id', 'entity123');
// Log in the controller
$this->mdcLogger->info('Controller action started.');
// Call service layer
$this->serviceLayerMethod();
// Clear context at the end
$this->mdcLogger->clearGlobalContext();
return new Response('Action completed');
}
private function serviceLayerMethod(): void
{
// Log in the service layer
$this->mdcLogger->info('Service layer processing.');
// Call repository layer
$this->repositoryLayerMethod();
}
private function repositoryLayerMethod(): void
{
// Log in the repository layer
$this->mdcLogger->info('Repository layer accessed.');
}
}
When using MDCLogger with multiple global contexts, the log output will include the global context and any additional context provided at the log message level. Here is an example of how a log entry might look in JSON format:
{
"message": "An error occurred.",
"context": {
"mdc_context": {
"user_id": "12345",
"request_id": "abcde",
"entity_id": "entity123"
},
"local_context": {
"additional_info": "Some local context data"
}
},
"level": "error",
"channel": "app",
"datetime": "2024-07-13T12:00:00+00:00",
"extra": []
}
This output shows the following:
- message: The log message.
- context: The combined context information.
- mdc_context: The global context, including
user_id
,request_id
, andentity_id
(you can add multiple global contexts that are shared across log entries) - local_context: Additional context specific to this log entry.
- mdc_context: The global context, including
- level: The log level (e.g., error, info).
- channel: The logging channel (e.g., app).
- datetime: The timestamp of the log entry.
- extra: Any additional metadata (empty in this case).
By default, the MDC context key is mdc_context
. You can customize this key by configuring your DI container to pass the desired key when creating the MDCLogger
instance.
In case you care creating the MDCLogger
instance manually, you can pass the desired key as the second parameter of the constructor.
$mdcLogger = new MDCLogger($monolog, 'custom_mdc_context');
The output of the logs will now include the custom key:
{
"message": "An error occurred.",
"context": {
"custom_mdc_context": {
"user_id": "12345",
"request_id": "abcde",
"entity_id": "entity123"
},
"local_context": {
"additional_info": "Some local context data"
}
},
"level": "error",
"channel": "app",
"datetime": "2024-07-13T12:00:00+00:00",
"extra": []
}
Below is a diagram showing the flow of adding global context in a controller, logging in different layers, and clearing the context at the end.
sequenceDiagram
participant Controller
participant Service
participant Repository
participant MDCLogger
participant Logger as LoggerInterface (PSR-3, Monolog, etc.)
Controller ->> MDCLogger: addGlobalContext('entity_id', 'entity123')
note over MDCLogger: Global context: [entity_id: entity123]
Controller ->> MDCLogger: info('Controller action started.')
MDCLogger ->> Logger: info('Controller action started.')
note over Logger: Log message with global context included.
Controller ->> Service: call serviceLayerMethod()
Service ->> MDCLogger: info('Service layer processing.')
MDCLogger ->> Logger: info('Service layer processing.')
note over Logger: Log message with global context included.
Service ->> Repository: call repositoryLayerMethod()
Repository ->> MDCLogger: info('Repository layer accessed.')
MDCLogger ->> Logger: info('Repository layer accessed.')
note over Logger: Log message with global context included.
Controller ->> MDCLogger: clearGlobalContext()
note over MDCLogger: Global context cleared: []
The MDCLoggerInterface
extends the LoggerInterface
from PSR-3 and adds methods to manage the global context.
namespace MDCLogger;
use Psr\Log\LoggerInterface;
interface MDCLoggerInterface extends LoggerInterface
{
public function __construct(LoggerInterface $logger, string $mdcContextKey = 'mdc_context');
public function addGlobalContext(string $key, string $value): void;
public function getGlobalContext(): array;
public function clearGlobalContext(): void;
}
This project is licensed under the MIT License. See the LICENSE file for details.
Gabriel Anhaia
Email