PHPackages                             phphleb/maskolog - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Logging &amp; Monitoring](/categories/logging)
4. /
5. phphleb/maskolog

ActiveLibrary[Logging &amp; Monitoring](/categories/logging)

phphleb/maskolog
================

Universal PSR-3 logger based on Monolog with masking

v2.1.5(1mo ago)171MITPHPPHP &gt;=8.1CI passing

Since Oct 5Pushed 1mo agoCompare

[ Source](https://github.com/phphleb/maskolog)[ Packagist](https://packagist.org/packages/phphleb/maskolog)[ RSS](/packages/phphleb-maskolog/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (10)Versions (38)Used By (0)

Maskolog
========

[](#maskolog)

[![version](https://camo.githubusercontent.com/8ecc95941763c685829d31de44e7105e6b9dc793f5c4e203b375308adb2b56da/68747470733a2f2f706f7365722e707567782e6f72672f706870686c65622f6d61736b6f6c6f672f76)](https://packagist.org/packages/phphleb/maskolog)[![License: MIT](https://camo.githubusercontent.com/6a971da90180ef445bacc481bafedda8784051a6eb20d93284bfc4c53d748aa9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542532302846726565292d3331433735342e737667)](https://github.com/phphleb/maskolog/blob/master/LICENSE)[![tests](https://github.com/phphleb/maskolog/actions/workflows/tests.yaml/badge.svg?event=push)](https://github.com/phphleb/maskolog/actions/workflows/tests.yaml)

**PSR-3 decorator for the Monolog logger with log-masking capabilities.**

 [![maskolog](https://github.com/phphleb/maskolog/raw/7d5c2c45556013d53b7a4cdd28213dbfcd8267ab/example.png)](https://github.com/phphleb/maskolog/blob/7d5c2c45556013d53b7a4cdd28213dbfcd8267ab/example.png)

Supports PHP versions [v7.3](https://github.com/phphleb/maskolog/tree/0.x), [v7.4 - 8.0](https://github.com/phphleb/maskolog/tree/1.x) and **v8.1+**

*From the author:*

When I joined a second relatively large company and faced the same requirement to mask logs on production servers, it became clear that this application security issue was broader and warranted a public library. This repository implements the concrete rules we needed to roll out across projects.

- **Masking sensitive data in logs**. Masking is flexible and can be applied globally or selectively. You can also designate specific Monolog handlers on the same logger to bypass masking.
- **Masking configuration data in exceptions**. Logged errors may include environment settings, tokens, and other secrets. This logger locates such values in the context and masks them by value.

This library is intended for corporate environments; you should be familiar enough to handle installation and usage details not covered in this brief guide.

Installation
------------

[](#installation)

Installation is performed using Composer:

```
composer require phphleb/maskolog --no-dev
```

Logger Assembly
---------------

[](#logger-assembly)

The logger is built from two parts — a factory that creates Monolog instances and an initializer that uses that factory to construct the masking logger itself.

```
$factory = new \Maskolog\ExampleManagedLoggerFactory();
$logger = new \Maskolog\Logger($factory);
```

Since this library only provides the abstract AbstractManagedLoggerFactory for creating and configuring a factory, the repository includes an ExampleManagedLoggerFactory demonstrating its use. The repository also includes an ExampleLogger showing how to extend the logger with additional masking capabilities.

### Log Masking Methods

[](#log-masking-methods)

Whether an added item participates in the masking strategy depends on which logger method is used

- `withMaskingProcessor` and `withMaskingProcessors` are used to add masking processors that implement MaskingProcessorInterface. For the factory, this corresponds to the global `pushMaskingProcessor` method in the context of the logger instance.
- `withUnmaskingHandler` lets you register a separate Monolog handler that does not participate in masking, even when masking is active. For the factory, this corresponds to the global `pushUnmaskingHandler` method in the context of the logger instance.

Masking logger methods return a new instance of the logger with the added properties.

```
$logger = $logger->withMaskingProcessors([PasswordMaskingProcessor::class => 'password']);

$logger->info('Text using password: {password}', ['password' => 'secret_password']);

// 'Text using password: *REDACTED.PASSWORD*' if masking is enabled, otherwise 'Text using password: secret_password'.

$logger->withMaskingProcessors([
           StringMaskingProcessor::class => 'token',
           UrlMaskingProcessor::class => 'url',
     ])
    ->info('Text using sensitive data: {token}, {password}, {url}', ['password' => 'secret_password', 'token' => 'secret_token', 'url' => 'domain.ru?hash=secret_hash']);

// 'Text using sensitive data: sec*REDACTED*en, *REDACTED.PASSWORD*, domain.ru?hash=REDACTED' if masking is enabled.
```

You can find examples of these processors in the */src/Processors/Masking/* folder, and also create your own. If such a record seems long to you and the names of the placeholder fields in the project will be standardized, create a shortened method in the class inherited from the logger and use it. For example:

```
public function withPasswordMasking(array $value = ['password', 'pass']): Logger
{
    return $this->withMaskingProcessors([PasswordMaskingProcessor::class => $value]);
}
```

Now you can use password masking like this:

```
$logger->withPasswordMasking()
       ->info('Text using password: {password}', ['password' => 'secret_password']);
```

This example is in the ExampleLogger class.

### Masking Data According to the Project Configuration

[](#masking-data-according-to-the-project-configuration)

For masking according to the values from the transferred list, a separate masking processor has been created, which is best assigned globally in the factory:

```
$configValues = array_values(['cdn_url' => 'cdn.site.domain', 'cdn_token' => 'secret_token']);

$this->pushMaskingProcessor(function($record) use ($configValues) {
    return (new ConfigMaskingProcessor($configValues))($record);
});
```

This processor is intended to prevent raw configuration data from appearing in error messages and contexts; therefore, by default it is limited to error levels only. Enabling informational levels in the processor constructor can cause performance issues when logging large volumes, because each message will require substring searches.

The project is expected to catch exceptions and log them via the masking logger.

### Specifying Exact Fields to Mask

[](#specifying-exact-fields-to-mask)

In the examples above, masking processors were configured with field names that were searched throughout the entire context, regardless of nesting. But what if the log context contains a nested array (for example, an API response) and you need to mask a specific field while leaving another field with the same name unmasked? This is a rare case, but you can specify a field for masking more precisely:

```
$logger->withMaskingProcessors(
    [StringMaskingProcessor::class => ['internal' => ['token']]]
    )->info('List of tokens', [
      'internal' => ['token' => 'secret_token'], // The token will be masked
      'external' => ['token' => 'public_token']  // The token will not be masked
    ]);
```

If you need to target a field at the top level, wrap it in array brackets — `['key']`. To mask all nested values under a given key, provide an empty array for that key — `['list' => []]`. You can also use numeric target keys (for example, `['list' => [0]]`), although this is not recommended.

### Masking for Objects in the Context

[](#masking-for-objects-in-the-context)

By default, the logger converts objects to arrays, so an object will be masked like a regular associative array of data. However, this conversion changes the nesting structure, and you must take that into account when specifying masks directly for fields. Global replacements at the logger level work the same as before (the field name becomes a key in the resulting array).

```
class ExampleObject {
    public string $examplePassword = 'secret_password';
}
```

```
  $logger->withMaskingProcessors(
            [PasswordMaskingProcessor::class => 'examplePassword']
        )->info('Masked object', [new ExampleObject()]);
```

Direct indication of the target:

```
  $logger->withMaskingProcessors(
            [PasswordMaskingProcessor::class => [
                [ExampleObject::class => ['examplePassword']],
            ]]
        )->info('Masked object', [new ExampleObject()]);
```

If automatic conversion is disabled, target objects will be passed to masking processors in their original form, so you will need to provide individual masks for each object. As an example for repository usage, there are examples for converting a specific object type to the required array format - these are the `Psr7RequestProcessor` and `Psr7ResponseProcessor` classes.

### Masking Object Fields According to Attributes

[](#masking-object-fields-according-to-attributes)

To automatically mask an object's fields in log context, add the special `Mask` attribute.

```
use Maskolog\Attributes\Mask;
class ExampleDto {
    public function __construct(
         #[\SensitiveParameter]
         #[Mask]
         readonly public string $secret,
         #[\SensitiveParameter]
         #[Mask(PasswordMaskingProcessor::class)]
         readonly public string $password;
    ) {}
}
```

If a specific masking processor is not specified in the attribute, the default one will be used.

### Simplified Example of Integration in Symfony

[](#simplified-example-of-integration-in-symfony)

```
services:
   Maskolog\ExampleLogger:
     arguments:
       - 'debug' # Maximum logging level
       - 'php://stdout' # Main log handler
       - '%kernel.environment%'

   Psr\Log\LoggerInterface:
     alias: Maskolog\ExampleLogger
```

```
public function index(ExampleLogger $exampleLogger, LoggerInterface $psrLogger): void
{
    $psrLogger->info('Global masks applied only');

    $exampleLogger->withPasswordMasking()
        ->info('Local password masking on top of global masks: {password}', ['password' => 'secret_password']);
}
```

### Initializing the Standard Monolog Logger

[](#initializing-the-standard-monolog-logger)

From this wrapper logger you can retrieve the currently initialized Monolog instance at any time, including all associated processors and handlers.

- The `getMaskingLogger` method returns the logger with all resources, including masking processors, but excluding handlers that are not involved in masking.
- The `getUnmaskingLogger` method returns the logger without masking processors, and includes only those handlers that are not involved in masking, if such handlers were assigned.

### Notes

[](#notes)

- If you use a Monolog logger instance obtained from the masking logger, be aware of limitations on how it can be used. First, you cannot add new masking processors to it. Second, context objects will be converted to arrays only after any processors already attached to the returned Monolog instance have run.
- Examples show using the global processor `Monolog\Processor\PsrLogMessageProcessor` with the `removeUsedContextFields` option enabled to substitute context values into the log message.

### Conclusion

[](#conclusion)

If you have suggestions to improve or extend the logger, contact the author or the Telegram [community](https://t.me/phphleb).

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance89

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~5 days

Total

37

Last Release

53d ago

Major Versions

v1.1.3 → v2.1.32026-03-17

v0.1.4 → v1.1.42026-03-19

v1.1.4 → v2.1.42026-03-19

v0.1.5 → v1.1.52026-03-26

1.x-dev → v2.1.52026-03-26

PHP version history (3 changes)v2.0.0PHP &gt;=8.1

v1.0.1PHP &gt;=7.4.0, &lt;8.1.0

v0.1.0PHP ^7.3.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/add493abc37e54c44eabe920dd9facf7aae69955c84bb8245b561f3fc51708da?d=identicon)[phphleb](/maintainers/phphleb)

---

Top Contributors

[![phphleb](https://avatars.githubusercontent.com/u/48386098?v=4)](https://github.com/phphleb "phphleb (32 commits)")

---

Tags

phploggermonologMasking

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/phphleb-maskolog/health.svg)

```
[![Health](https://phpackages.com/badges/phphleb-maskolog/health.svg)](https://phpackages.com/packages/phphleb-maskolog)
```

###  Alternatives

[logtail/monolog-logtail

Logtail handler for Monolog

233.2M3](/packages/logtail-monolog-logtail)[justbetter/magento2-sentry

Magento 2 Logger for Sentry

1851.5M3](/packages/justbetter-magento2-sentry)[theorchard/monolog-cascade

Monolog extension to configure multiple loggers in the blink of an eye and access them from anywhere

1482.2M9](/packages/theorchard-monolog-cascade)[tomatophp/filament-logger

Log all user activity to file or log driver and preview it on your FilamentPHP panel

162.2k](/packages/tomatophp-filament-logger)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
