Home Backend Development PHP Tutorial PHP Master | Logging with PSR-3 to Improve Reusability

PHP Master | Logging with PSR-3 to Improve Reusability

Feb 24, 2025 am 10:42 AM

PHP Master | Logging with PSR-3 to Improve Reusability

Core points

  • PSR-3, a common log object interface, allows developers to write reusable code without relying on any specific log implementation, thereby improving compatibility between different log libraries in PHP.
  • The
  • PSR-3 interface provides eight methods to handle messages of different severity levels, and a common log() method that can receive any severity levels. Its design is to solve the problem of log implementation incompatibility.
  • Although PSR-3 has many benefits, some log libraries do not support it natively. However, developers can create PSR-3 compliant adapters by leveraging the adapter mode and extending the AbstractLogger class provided in the Psr/Log library.
  • Many major PHP projects, including Monolog, Symfony, and Mustache.php, have added support for PSR-3. As it reduces the barriers to code reuse, more libraries and frameworks are expected to use logging correctly, providing useful information for developers.

In PHP development, logging is one of the most common tasks. We use logs to track error messages, log important events, and debug code issues. In any PHP project, the code may be filled with calls to log libraries that handle these operations for us. Unfortunately, calls to log libraries are scattered throughout the code, which makes the code depend on the availability of the library, which is clearly contrary to the principle of dependency inversion. Even if we use dependency injection to let our objects access the log library, the difference between log libraries means switching between them can be difficult and time-consuming, requiring a major refactoring of the entire code library. To improve compatibility between log libraries, the PHP-FIG team recently released PSR-3, a common log object interface. In this article, I will discuss how the PSR-3 defined log interface allows us to write reusable code that does not depend on any particular log implementation.

PSR-3 Quick Start

Before we understand how PSR-3 makes our code more reusable, it is necessary to understand what PSR-3 is. If you are already familiar with PSR-3, you can skip this section. The core of the specification is the interface to log objects. This interface discloses eight ways to handle messages of different severity levels, and a common log() method that can accept any severity levels. The eight severity levels supported by PSR-3 are based on RFC 5424, as described below:

  • emergency – The system cannot be used
  • alert – Action is required
  • critical – Serious situation
  • error – Errors that do not need immediate attention but should be monitored
  • warning – An unusual or undesirable event, but not an error
  • notice – Normal but important events
  • info – Interesting Events
  • debug – Details for debugging

Each log method accepts a message that must be a string or an object with a __toString() method. Additional parameters accept an array that can provide context information for log messages. A complete description of these methods and parameters can be found in the PSR-3 specification.

Get PSR-3 file

Getting the files you need to use PSR-3 is easy - you can find them in the Psr/Log GitHub repository. You can also use Composer to get these files from Packagist. Here is an example of a composer.json file for retrieving Psr/Log files:

{
    "require": {
        "psr/log": "dev-master"
    }
}
Copy after login
Copy after login
Copy after login

How to limit code reuse of logging

PHP has many different log libraries, each with its own way of collecting and recording data. While they have some commonalities, each library has its own unique set of logging methods. This means switching between logs can be challenging, and often requires changing the code where you use logging. This runs contrary to the SOLID principle of code reuse and object-oriented design. The situation we face is that either declare dependencies on specific log libraries or avoid logging altogether. To illustrate this issue more clearly, a specific example is needed. Suppose we are creating a simple Mailer object to handle sending emails. We want Mailer to log a message every time we send an email, and we decided to use the excellent Monolog library to handle our logging needs.

<?php namespace Email;

class Mailer
{
    private $logger;

    public function __construct($logger)
    {
        $this->logger = $logger;
    }

    public function sendEmail($emailAddress)
    {
        // 发送电子邮件的代码...

        // 记录消息
        $this->logger->addInfo("Email sent to $emailAddress");
    }
}
Copy after login
Copy after login
Copy after login

We can use this class with the following code:

<?php
// 创建一个Monolog对象
$logger = new Monolog\Logger("Mail");
$logger->pushHandler(new Monolog\Handler\StreamHandler("mail.log"));

// 创建邮件发送器并发送电子邮件
$mailer = new Email\Mailer($logger);
$mailer->sendEmail("email@example.com");
Copy after login
Copy after login

Running this code will create a new entry in the mail.log file, recording the email sent. At this point, we might think that we have written a reusable Mailer object. We use dependency injection to make the logger available for Mailer, so we can swap different logger configurations without touching our Mailer code. It looks like we have successfully followed the SOLID principle and avoided creating any hard dependencies. But suppose we want to reuse the Mailer class in different projects using Analog to handle logging interactions. Now we have a problem because Analog does not have a addInfo() method. To record information-level messages using Analog, we call Analog::log($message, Analog::INFO). We can modify the Mailer class to use the Analog method as shown below.

<?php namespace Email;

class Mailer
{
    public function sendEmail($emailAddress)
    {
        // 发送电子邮件的代码...

        // 记录消息
        Analog::log("Email sent to $emailAddress", Analog::INFO);
    }
}
Copy after login
Copy after login

We can use the updated Mailer class with the following code:

{
    "require": {
        "psr/log": "dev-master"
    }
}
Copy after login
Copy after login
Copy after login

While this will work, it is far from ideal. We encountered Mailer's dependency on a specific logging implementation, which requires changing the class when introducing a new logger. This makes the class less reusable and forces us to choose between relying on the availability of a particular logger or abandoning logging in the class altogether.

Use PSR-3 to avoid logger dependencies

As Alejandro Gervasio explains in his excellent article on the topic, the principle of dependency inversion tells us that we should rely on abstraction rather than concrete implementations. In the case of logging, our current problem has been the lack of a suitable abstraction that can be relied on. This is where PSR-3 comes into play. PSR-3 is designed to overcome the incompatibility of logging implementation by providing a common interface for the logger (properly named LoggerInterface). By providing an interface that is not bound to any specific implementation, PSR-3 allows us to avoid relying on a specific logger - we can instead type prompt for LoggerInterface to get a PSR-3-compliant logger. I've updated the following Mailer class to demonstrate this:

<?php namespace Email;

class Mailer
{
    private $logger;

    public function __construct($logger)
    {
        $this->logger = $logger;
    }

    public function sendEmail($emailAddress)
    {
        // 发送电子邮件的代码...

        // 记录消息
        $this->logger->addInfo("Email sent to $emailAddress");
    }
}
Copy after login
Copy after login
Copy after login
The

constructor has been modified to accept the LoggerInterface implementer, and the sendEmail() method now calls the info() method specified in PSR-3. Monolog is already PSR-3 compliant, and Analog provides a wrapper object that implements LoggerInterface, so we can now use these two loggers without modifying the Mailer class. Here is how to call this class using Monolog:

<?php
// 创建一个Monolog对象
$logger = new Monolog\Logger("Mail");
$logger->pushHandler(new Monolog\Handler\StreamHandler("mail.log"));

// 创建邮件发送器并发送电子邮件
$mailer = new Email\Mailer($logger);
$mailer->sendEmail("email@example.com");
Copy after login
Copy after login

and use Analog:

<?php namespace Email;

class Mailer
{
    public function sendEmail($emailAddress)
    {
        // 发送电子邮件的代码...

        // 记录消息
        Analog::log("Email sent to $emailAddress", Analog::INFO);
    }
}
Copy after login
Copy after login

Now we are able to use our Mailer object with any library without editing the Mailer class or changing the way we use it.

Use adapter mode for loggers that do not support PSR-3

So far, we have successfully decoupled the Mailer object from any specific logging implementation through the implementer requesting LoggerInterface. But what about those loggers that have not yet been added for PSR-3 support? For example, the popular KLogger library has not been updated for a while and is currently incompatible with PSR-3. Fortunately, we can easily map the methods exposed by KLogger to those defined in LoggerInterface by leveraging the adapter pattern. Supported files in the Psr/Log repository enable us to easily create adapter classes by providing a AbstractLogger class that we can extend. An abstract class simply forwards eight level-specific log methods defined in LoggerInterface to a common log() method. By extending the AbstractLogger class and defining our own log() method, we can easily create PSR-3-compliant adapters for loggers that do not natively support PSR-3. I'll demonstrate this below by creating a simple adapter for KLogger:

{
    "require": {
        "psr/log": "dev-master"
    }
}
Copy after login
Copy after login
Copy after login
The

log() method simply maps the LoggerInterface method to the respective KLogger method, and the KLogger handles the actual logging activity. By wrapping the KLogger class this way, we are able to use it without breaking the LoggerInterface contract. We can now use the KLogger adapter with the Mailer class:

<?php namespace Email;

class Mailer
{
    private $logger;

    public function __construct($logger)
    {
        $this->logger = $logger;
    }

    public function sendEmail($emailAddress)
    {
        // 发送电子邮件的代码...

        // 记录消息
        $this->logger->addInfo("Email sent to $emailAddress");
    }
}
Copy after login
Copy after login
Copy after login

With the adapter class, we are able to use KLogger without modifying the Mailer class and still adhere to LoggerInterface. KLogger does not accept the second parameter of debug level messages, so it does not fully comply with PSR-3 even with an adapter. Extending KLogger to make it fully compatible with PSR-3 would be a trivial task, but that's beyond the scope of this article. However, it is safe to say that using our adapter class makes us very close to being fully PSR-3 compliant and allows us to use LoggerInterface with the KLogger class.

Conclusion

In this article, we have learned how to use PSR-3 to help us write logger-free code that does not depend on a specific logging implementation. Many major PHP projects have added support for PSR-3, including Monolog, Symfony, and Mustache.php, as well as other well-known projects like Drupal are discussing how to best integrate it. Since PSR-3 reduces the barriers to code reuse, we should see more libraries and frameworks correctly use logging to provide useful information for developers. Will PSR-3 affect how you use logging in your application? Please let us know in the comments section below.

(Picture from Fotolia)

(The FAQ part of PSR-3 logging is omitted here due to space limitations. It can be added as needed.)

The above is the detailed content of PHP Master | Logging with PSR-3 to Improve Reusability. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Apr 05, 2025 am 12:04 AM

JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

How does session hijacking work and how can you mitigate it in PHP? How does session hijacking work and how can you mitigate it in PHP? Apr 06, 2025 am 12:02 AM

Session hijacking can be achieved through the following steps: 1. Obtain the session ID, 2. Use the session ID, 3. Keep the session active. The methods to prevent session hijacking in PHP include: 1. Use the session_regenerate_id() function to regenerate the session ID, 2. Store session data through the database, 3. Ensure that all session data is transmitted through HTTPS.

Describe the SOLID principles and how they apply to PHP development. Describe the SOLID principles and how they apply to PHP development. Apr 03, 2025 am 12:04 AM

The application of SOLID principle in PHP development includes: 1. Single responsibility principle (SRP): Each class is responsible for only one function. 2. Open and close principle (OCP): Changes are achieved through extension rather than modification. 3. Lisch's Substitution Principle (LSP): Subclasses can replace base classes without affecting program accuracy. 4. Interface isolation principle (ISP): Use fine-grained interfaces to avoid dependencies and unused methods. 5. Dependency inversion principle (DIP): High and low-level modules rely on abstraction and are implemented through dependency injection.

How to debug CLI mode in PHPStorm? How to debug CLI mode in PHPStorm? Apr 01, 2025 pm 02:57 PM

How to debug CLI mode in PHPStorm? When developing with PHPStorm, sometimes we need to debug PHP in command line interface (CLI) mode...

Framework Security Features: Protecting against vulnerabilities. Framework Security Features: Protecting against vulnerabilities. Mar 28, 2025 pm 05:11 PM

Article discusses essential security features in frameworks to protect against vulnerabilities, including input validation, authentication, and regular updates.

How to automatically set permissions of unixsocket after system restart? How to automatically set permissions of unixsocket after system restart? Mar 31, 2025 pm 11:54 PM

How to automatically set the permissions of unixsocket after the system restarts. Every time the system restarts, we need to execute the following command to modify the permissions of unixsocket: sudo...

What are Enumerations (Enums) in PHP 8.1? What are Enumerations (Enums) in PHP 8.1? Apr 03, 2025 am 12:05 AM

The enumeration function in PHP8.1 enhances the clarity and type safety of the code by defining named constants. 1) Enumerations can be integers, strings or objects, improving code readability and type safety. 2) Enumeration is based on class and supports object-oriented features such as traversal and reflection. 3) Enumeration can be used for comparison and assignment to ensure type safety. 4) Enumeration supports adding methods to implement complex logic. 5) Strict type checking and error handling can avoid common errors. 6) Enumeration reduces magic value and improves maintainability, but pay attention to performance optimization.

See all articles