PHPackages                             aubes/shadow-logger-bundle - 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. aubes/shadow-logger-bundle

ActiveSymfony-bundle[Logging &amp; Monitoring](/categories/logging)

aubes/shadow-logger-bundle
==========================

Monolog processor for anonymization

v1.3.1(2mo ago)030[1 PRs](https://github.com/aubes/shadow-logger-bundle/pulls)MITPHPPHP &gt;=8.1CI passing

Since Apr 11Pushed 2mo ago1 watchersCompare

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

READMEChangelog (5)Dependencies (20)Versions (9)Used By (0)

Shadow Logger Bundle
====================

[](#shadow-logger-bundle)

[![CI](https://github.com/aubes/shadow-logger-bundle/actions/workflows/php.yml/badge.svg)](https://github.com/aubes/shadow-logger-bundle/actions/workflows/php.yml/badge.svg)

This Symfony bundle provides a Monolog processor to transform log data in order to respect GDPR or anonymize sensitive data.

It allows IP anonymization, hashing, encryption, or removal of sensitive fields in logs.

Requirements
------------

[](#requirements)

- PHP &gt;= 8.1
- Symfony 6, 7 or 8
- Monolog 2 or 3

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

[](#installation)

```
composer require aubes/shadow-logger-bundle
```

Configuration
-------------

[](#configuration)

```
# config/packages/shadow-logger.yaml
shadow_logger:
    # Add "shadow-debug" in "extra" when a transformer throws an exception.
    # Recommended to use '%kernel.debug%' so it is only active in development.
    debug: '%kernel.debug%'

    # When a transformer throws an exception:
    #   true  → the field value is set to null
    #   false → the original (untransformed) value is kept
    strict: true

    # Register ShadowProcessor on handlers OR channels (not both).
    # Scoping to specific handlers/channels is recommended for performance.
    handlers: ['app']
    #channels: ['app']

    # Salt used by the "hash" transformer (see Hash transformer section).
    encoder:
        salt: '%env(SHADOW_LOGGER_ENCODER_SALT)%'

    mapping:
        # Fields to transform in the log "context"
        context:
            user_ip:        ['ip']
            user_name:      ['hash']
            user_birthdate: ['remove']

        # Fields to transform in the log "extra"
        extra:
            custom_field: ['remove']
```

Choosing between hashing and encryption
---------------------------------------

[](#choosing-between-hashing-and-encryption)

Both the `hash` and `encrypt` transformers protect sensitive data in logs, but they serve different purposes. Choosing the right one depends on what you need to do with the data after it is logged.

### Hashing (`hash`)

[](#hashing-hash)

Hashing is a **one-way, irreversible** operation. The original value cannot be recovered from the hash.

**Use it when:**

- You do not need to read back the original value
- You want to correlate log entries belonging to the same user (e.g. trace a user across multiple requests) without storing their identity — the same input always produces the same hash
- You want to pseudonymize data in compliance with GDPR

**Limitations:**

- If the space of possible values is small (e.g. an IP address), an attacker with access to the logs could reconstruct the original values through brute force
- The salt must be kept secret; rotating it means previously hashed values can no longer be correlated with new ones
- You cannot fulfill a GDPR "right of access" request using only the hashed value

**Configuration:**

```
shadow_logger:
    encoder:
        algo:   'sha256'  # any algorithm from hash_algos()
        salt:   '%env(SHADOW_LOGGER_ENCODER_SALT)%'
        binary: false
```

> Always configure a `salt` to prevent rainbow table attacks. Store it as a secret environment variable.

### Encryption (`encrypt`)

[](#encryption-encrypt)

Encryption is a **reversible** operation. The original value can be recovered using the key and the IV stored alongside it.

**Use it when:**

- You may need to read back the original value later (e.g. to respond to a GDPR right of access or right of erasure request, or to debug a production issue with proper authorization)
- You want to store sensitive data in logs in a protected form without losing it permanently

**Limitations:**

- Requires secure key management: if the key is compromised, all encrypted log entries are exposed
- Key rotation is complex: old entries encrypted with a previous key can no longer be decrypted with the new one
- The presence of the IV in the log reveals that the original field was non-empty, which may itself be sensitive
- More computationally expensive than hashing

### Quick comparison

[](#quick-comparison)

`hash``encrypt``remove`ReversibleNoYes (with key)NoCorrelate entriesYes (same input → same output)No (IV is random)NoProtect against brute forceWith a strong saltYesYesGDPR right of accessNoYesNoKey management requiredSalt onlyKey + IVNoneTransformers
------------

[](#transformers)

The following transformers are available out of the box:

AliasDescription`ip`Anonymizes an IPv4 or IPv6 address`hash`Hashes the value using the configured algorithm`string`Casts a scalar or `Stringable` object to string`remove`Replaces the value with `--obfuscated--``encrypt`Encrypts the value (requires encryptor configuration)`truncate`Masks the middle of a value, keeping start and/or end visible (requires truncator configuration)### Chaining transformers

[](#chaining-transformers)

Transformers can be chained. They are applied in order, the output of one becoming the input of the next. For example, to hash a `Stringable` object:

```
shadow_logger:
    mapping:
        context:
            custom_field: ['string', 'hash']
```

### Hash transformer

[](#hash-transformer)

The `hash` transformer uses the configured encoder. See the [Hashing section](#hashing-hash) above for configuration options.

### Encrypt transformer

[](#encrypt-transformer)

The `encrypt` transformer supports two modes: a built-in encryptor based on OpenSSL, or a custom implementation.

#### Built-in encryptor

[](#built-in-encryptor)

```
shadow_logger:
    encryptor:
        key:    '%env(SHADOW_LOGGER_ENCRYPTOR_KEY)%'
        cipher: 'aes-256-cbc' # optional, default: aes-256-cbc
```

The key must be kept secret and should be provided via an environment variable. The cipher can be any algorithm supported by OpenSSL (`openssl_get_cipher_methods()`).

#### Custom encryptor

[](#custom-encryptor)

If you need a different encryption strategy, implement [`EncryptorInterface`](src/Encryptor/EncryptorInterface.php):

```
// src/Encryptor/EncryptorAdapter.php
namespace App\Encryptor;

use Aubes\ShadowLoggerBundle\Encryptor\EncryptorInterface;

class EncryptorAdapter implements EncryptorInterface
{
    public function encrypt(string $data, string $iv): string
    {
        // your encryption logic
        return $encryptedValue;
    }

    public function generateIv(): string
    {
        // generate a random IV
        return $iv;
    }
}
```

Register it as a service (if not using autoconfiguration):

```
# config/services.yaml
services:
    App\Encryptor\EncryptorAdapter: ~
```

Then reference it by its service ID:

```
shadow_logger:
    encryptor: 'App\Encryptor\EncryptorAdapter'
```

#### Output format

[](#output-format)

In both cases, the transformer replaces the field value with:

```
[
    'iv'    => 'abc123', // base64-encoded IV used during encryption
    'value' => '...',    // encrypted value
]
```

### Truncate transformer

[](#truncate-transformer)

The `truncate` transformer masks the middle of a value while keeping a configurable number of characters visible at the start and/or end. It is useful for partially revealing values like card numbers, email addresses, or tokens.

Named variants are declared under `truncators`. Each variant becomes available as a transformer alias: `default` → `truncate`, others → `truncate_{name}`.

```
shadow_logger:
    truncators:
        default:            # alias: "truncate"
            keep_start: 2
            keep_end:   2
            mask:       '***'
        card:               # alias: "truncate_card"
            keep_start: 4
            keep_end:   4
            mask:       '****'
        email:              # alias: "truncate_email"
            keep_start: 1
            keep_end:   0
            mask:       '***'

    mapping:
        context:
            card_number: ['truncate_card']    # 4242424242424242 → 4242****4242
            email:       ['truncate_email']   # john@example.com → j***
            token:       ['truncate']         # abcdef1234 → ab***34
```

OptionDescriptionDefault`keep_start`Number of characters to keep at the beginning`2``keep_end`Number of characters to keep at the end`2``mask`String used to replace the hidden part`***`> If the value is shorter than or equal to `keep_start + keep_end`, it is replaced entirely by the mask.

Mapping
-------

[](#mapping)

### Nested fields

[](#nested-fields)

Field names can use dot notation to target nested array keys.

Given the following `extra` structure:

```
'user' => [
    'id'   => 42,
    'name' => [
        'first' => 'John',
        'last'  => 'Doe',
    ],
    'ip'   => '1.2.3.4',
]
```

You can map nested fields like this:

```
shadow_logger:
    mapping:
        extra:
            user.ip:         ['ip']
            user.name.first: ['hash']
            user.name.last:  ['remove']
```

> **Note:** Dot notation uses the Symfony PropertyAccessor internally, which is slower than direct key access. Prefer flat field names when possible.

Custom transformer
------------------

[](#custom-transformer)

Implement [`TransformerInterface`](src/Transformer/TransformerInterface.php):

```
// src/Transformer/CustomTransformer.php
namespace App\Transformer;

use Aubes\ShadowLoggerBundle\Transformer\TransformerInterface;

class CustomTransformer implements TransformerInterface
{
    public function transform(mixed $data): mixed
    {
        // transform and return the value
        return $data;
    }
}
```

Register it as a service with the `shadow_logger.transformer` tag and an `alias`:

```
# config/services.yaml
services:
    App\Transformer\CustomTransformer:
        tags:
            - { name: 'shadow_logger.transformer', alias: 'custom' }
```

The `alias` is the name used in the `mapping` configuration:

```
shadow_logger:
    mapping:
        context:
            some_field: ['custom']
```

###  Health Score

43

—

FairBetter than 91% of packages

Maintenance88

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity59

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 ~267 days

Total

5

Last Release

60d ago

PHP version history (2 changes)v1.0.0PHP &gt;=7.4

v1.3.0PHP &gt;=8.1

### Community

Maintainers

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

---

Top Contributors

[![aubes](https://avatars.githubusercontent.com/u/3941035?v=4)](https://github.com/aubes "aubes (9 commits)")

---

Tags

anonymizationbundlegdprloggingmonolog-processorphpsymfonysymfony-bundlelogsymfonybundlemonologgdpranonymise

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/aubes-shadow-logger-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/aubes-shadow-logger-bundle/health.svg)](https://phpackages.com/packages/aubes-shadow-logger-bundle)
```

###  Alternatives

[egeniq/monolog-gdpr

Some Monolog processors that will help in relation to the security requirements under GDPR.

528.7k](/packages/egeniq-monolog-gdpr)

PHPackages © 2026

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