Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.1.0]

### Added

- **`CorrelationIdProcessor`** — new processor that reads a correlation ID from Monolog `extra` and writes it to `labels.correlation_id` or `trace.id`. Works with any library that populates `extra` (e.g. `aubes/correlation-bundle`). The source key is removed from `extra` after processing.
- **`TracingProcessor`** — new `opentelemetry` mode that reads flat `trace_id`/`span_id`/`trace_flags` keys injected by the OpenTelemetry Monolog processor and maps them to ECS fields. The flat keys are cleaned up from context automatically.

## [3.0.1]

### Fixed
Expand Down Expand Up @@ -89,7 +96,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Symfony 6.4, 7.x, and 8.x compatibility.
- PHP 8.1+ support.

[Unreleased]: https://github.com/aubes/ecs-logging-bundle/compare/v3.0.0...HEAD
[Unreleased]: https://github.com/aubes/ecs-logging-bundle/compare/v3.1.0...HEAD
[3.1.0]: https://github.com/aubes/ecs-logging-bundle/compare/v3.0.1...v3.1.0
[3.0.1]: https://github.com/aubes/ecs-logging-bundle/compare/v3.0.0...v3.0.1
[3.0.0]: https://github.com/aubes/ecs-logging-bundle/compare/v2.0.2...v3.0.0
[2.0.2]: https://github.com/aubes/ecs-logging-bundle/compare/v2.0.1...v2.0.2
[2.0.1]: https://github.com/aubes/ecs-logging-bundle/compare/v2.0.0...v2.0.1
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ Built on top of [elastic/ecs-logging](https://github.com/elastic/ecs-logging-php
|---|---|
| **`EcsFormatter`** | Produces ECS-compliant NDJSON (`log.level` lowercase, `ecs.version` and `tags` configurable) |
| **`ServiceProcessor`** | Injects static `service.*` metadata (name, version, id…) into every record |
| **`ErrorProcessor`** | Converts a `\Throwable` in context to ECS `error.*` fields |
| **`TracingProcessor`** | Maps a tracing array to ECS `trace.id`, `transaction.id`, `span.id` |
| **`ErrorProcessor`** | Converts a `\Throwable` in context to ECS `error.*` fields. `map_exception_key` also catches Symfony's native exceptions |
| **`TracingProcessor`** | Maps tracing data to ECS `trace.id`, `transaction.id`, `span.id` (supports manual arrays and OpenTelemetry flat keys) |
| **`CorrelationIdProcessor`** | Maps a correlation ID from Monolog `extra` to ECS `labels.correlation_id` or `trace.id` |
| **`UserProcessor`** | Injects the authenticated user as ECS `user.*` via a customisable provider |
| **`HttpRequestProcessor`** | Injects ECS `http.*`, `url.*`, and optionally `client.ip` from the current request |
| **`HostProcessor`** | Injects static ECS `host.*` fields resolved once at boot time |
Expand Down Expand Up @@ -121,7 +122,8 @@ ecs_logging:
- [ServiceProcessor](docs/processors/service.md)
- [ErrorProcessor](docs/processors/error.md)
- [TracingProcessor](docs/processors/tracing.md)
- [UserProcessor](docs/processors/user.md) — includes custom provider
- [CorrelationIdProcessor](docs/processors/correlation-id.md)
- [UserProcessor](docs/processors/user.md)
- [AutoLabelProcessor](docs/processors/auto-label.md)
- [HttpRequestProcessor](docs/processors/http-request.md)
- [HostProcessor](docs/processors/host.md)
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "aubes/ecs-logging-bundle",
"type": "symfony-bundle",
"description": "Symfony bundle providing the Ecs log format",
"keywords": ["symfony", "bundle", "ecs", "elastic", "elastic-common-schema", "monolog", "logging"],
"keywords": ["symfony", "symfony-bundle", "ecs", "elastic", "elastic-common-schema", "monolog", "logging"],
"license": "MIT",
"authors": [
{
Expand Down
19 changes: 19 additions & 0 deletions docs/configuration-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ ecs_logging:
channels: []
tracing:
enabled: false

# "default" reads a nested array from context[field_name]. "opentelemetry" reads flat trace_id/span_id keys injected by the OTel Monolog processor (field_name is ignored).
mode: default # One of "default"; "opentelemetry"

# Context key read by the processor in "default" mode. Ignored in "opentelemetry" mode.
field_name: tracing

# Logging handler list the processor should be pushed to
Expand Down Expand Up @@ -105,6 +110,20 @@ ecs_logging:
# Logging handler list the processor should be pushed to
handlers: []

# Logging channel list the processor should be pushed to
channels: []
correlation_id:
enabled: false

# Key to read from Monolog extra (must match the library that populates extra).
field_name: correlation_id

# Where to write the correlation ID: "labels" writes to labels.correlation_id, "trace" writes to trace.id.
target: labels # One of "labels"; "trace"

# Logging handler list the processor should be pushed to
handlers: []

# Logging channel list the processor should be pushed to
channels: []
http_request:
Expand Down
2 changes: 2 additions & 0 deletions docs/processors/auto-label.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# AutoLabelProcessor

Keep your custom context fields without polluting the ECS namespace. Non-ECS keys are moved to `labels` or dropped automatically.

Removes non-ECS context keys from log records to protect the ECS namespace. Optionally moves them into [`labels`](https://www.elastic.co/guide/en/ecs/current/ecs-base.html) instead of dropping them.

## Configuration
Expand Down
72 changes: 72 additions & 0 deletions docs/processors/correlation-id.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# CorrelationIdProcessor

Lightweight request correlation without the full OpenTelemetry stack. Your correlation ID flows into ECS logs automatically.

Reads a correlation ID from Monolog `extra` and writes it to an ECS-compliant field in the log context. Works with any library that populates `extra` with a correlation/request ID.

## Correlation ID vs Tracing OpenTelemetry Mode

If your application already uses OpenTelemetry for distributed tracing, you probably don't need this processor: the [Tracing processor with open opentelemetry mode](tracing.md#opentelemetry-mode) maps `trace_id` and `span_id` to ECS fields automatically.

## Configuration

```yaml
# config/packages/ecs_logging.yaml
ecs_logging:
processor:
correlation_id:
enabled: true

# Key to read from Monolog extra (default: "correlation_id").
# Must match the field name configured in the library that populates extra.
field_name: correlation_id

# Where to write the correlation ID:
# "labels" (default) -> labels.correlation_id
# "trace" -> trace.id
target: labels

#handlers: ['ecs']
#channels: ['app']
```

## Target options

| Value | ECS field | When to use |
|---|---|---|
| `labels` (default) | `labels.correlation_id` | When the correlation ID is a business/request-scoping value distinct from distributed tracing |
| `trace` | `trace.id` | When the correlation ID serves as the trace ID (no separate tracing system). ECS expects a 32-character lowercase hex string |

ECS output with `target: labels`:

```json
{
"labels": {
"correlation_id": "abc-123"
}
}
```

ECS output with `target: trace`:

```json
{
"trace": {
"id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"
}
}
```

The source key is removed from `extra` to prevent it from appearing at root level in the ECS output. If the field is missing, empty, or not a string, the record is left unchanged. Existing values in the target field are never overwritten.

## Custom field name

Any Monolog processor that writes a string to `extra` can be used. Configure `field_name` to match:

```yaml
ecs_logging:
processor:
correlation_id:
enabled: true
field_name: request_id # reads from extra.request_id
```
15 changes: 15 additions & 0 deletions docs/processors/error.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ErrorProcessor

Just pass a `\Throwable` in context, the processor handles the rest. With `map_exception_key`, Symfony's own exceptions (HttpKernel, security, form...) are also converted to ECS automatically.

Converts a `\Throwable` in the log context to [ECS `error.*`](https://www.elastic.co/guide/en/ecs/current/ecs-error.html) fields.

## Configuration
Expand Down Expand Up @@ -41,6 +43,19 @@ try {
}
```

ECS output:

```json
{
"error": {
"type": "RuntimeException",
"message": "Something failed",
"code": 0,
"stack_trace": "RuntimeException: Something failed in /app/src/Service.php:42\n..."
}
}
```

## Symfony exceptions (`map_exception_key`)

Symfony's internal log calls pass exceptions under the `exception` key (not `error`). Enable `map_exception_key` to automatically process `context['exception']` as `error.*`, without any code change:
Expand Down
13 changes: 13 additions & 0 deletions docs/processors/host.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# HostProcessor

Enrich every log record with host metadata (name, IP, architecture).

Injects static [ECS `host.*`](https://www.elastic.co/guide/en/ecs/current/ecs-host.html) fields into every log record. Values are resolved once at container build time and cached for the lifetime of the process.

## Configuration
Expand Down Expand Up @@ -33,3 +35,14 @@ ecs_logging:
| `host.ip` | Not set unless `ip` is provided or `resolve_ip: true` |
| `host.architecture` | `php_uname('m')` (e.g. `x86_64`, `aarch64`) |

ECS output:

```json
{
"host": {
"name": "example.com",
"architecture": "x86_64"
}
}
```

24 changes: 22 additions & 2 deletions docs/processors/http-request.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# HttpRequestProcessor

Every log record automatically includes HTTP method, URL and protocol version from the current request.

Injects [ECS `http.*`](https://www.elastic.co/guide/en/ecs/current/ecs-http.html), [`url.*`](https://www.elastic.co/guide/en/ecs/current/ecs-url.html), and optionally `client.ip` from the current HTTP request into every log record.

> No active request (e.g. in a Symfony command) means the processor has no effect.
Expand Down Expand Up @@ -43,5 +45,23 @@ ecs_logging:
| `url.port` | Port, if non-standard (80/443 are omitted) |

> **Note - `url.path`**
> The request path can contain personal identifiers embedded in the route (e.g. `/api/users/john@example.com/`, `/reset-password/TOKEN`).
> Consider normalising routes before logging (log `/api/users/{id}` rather than `/api/users/42`).
> The request path can contain personal identifiers embedded in the route.

ECS output:

```json
{
"http": {
"request": {
"method": "POST",
"mime_type": "application/json"
},
"version": "1.1"
},
"url": {
"path": "/api/checkout",
"scheme": "https",
"domain": "example.com"
}
}
```
13 changes: 13 additions & 0 deletions docs/processors/service.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ServiceProcessor

Configure once, every log record is automatically enriched with your service name, version and type.

Injects static [ECS `service.*`](https://www.elastic.co/guide/en/ecs/current/ecs-service.html) metadata into every log record. Values are defined in config and injected at container build time.

## Configuration
Expand Down Expand Up @@ -53,3 +55,14 @@ With the processor enabled, every log record receives the service fields automat
```php
$logger->info('message');
```

ECS output:

```json
{
"service": {
"name": "my-app",
"version": "1.0.0"
}
}
```
69 changes: 57 additions & 12 deletions docs/processors/tracing.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# TracingProcessor

Converts a tracing array in the log context to ECS `trace.id`, `transaction.id`, and `span.id`.
Maps your tracing data to ECS fields automatically, whether you use OpenTelemetry or pass tracing IDs manually.

Converts tracing data in the log context to ECS `trace.id`, `transaction.id`, and `span.id`.

## Configuration

Expand All @@ -10,13 +12,18 @@ ecs_logging:
processor:
tracing:
enabled: true
field_name: 'tracing' # context key to read from (default: 'tracing')
mode: 'default' # 'default' or 'opentelemetry'
field_name: 'tracing' # context key to read from (default mode only)

#handlers: ['ecs']
#channels: ['app']
```

## Usage
## Default mode

In `default` mode, the processor reads a nested array from `context[field_name]` and maps it to ECS tracing fields.

### Usage

Without the processor:

Expand All @@ -33,19 +40,57 @@ With the processor:
```php
$logger->info('message', [
'tracing' => [
'trace_id' => $traceId, // required → ECS trace.id
'transaction_id' => $transactionId, // optional → ECS transaction.id
'span_id' => $spanId, // optional → ECS span.id
'trace_id' => $traceId, // required
'transaction_id' => $transactionId, // optional
'span_id' => $spanId, // optional
],
]);
```

## ECS output
ECS output:

| Input key | ECS field | Format |
|---|---|---|
| `trace_id` | `trace.id` | 32-char hex string |
| `transaction_id` | `transaction.id` | 16-char hex string |
| `span_id` | `span.id` | 16-char hex string |
```json
{
"trace": { "id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" },
"transaction": { "id": "f6e5d4c3b2a1f6e5" },
"span": { "id": "f6e5d4c3b2a1f6e5" }
}
```

`trace_id` is required. `transaction_id` and `span_id` are optional.

## OpenTelemetry mode

In `opentelemetry` mode, the processor reads flat `trace_id`, `span_id`, and `trace_flags` keys from the log context (injected by the OpenTelemetry Monolog handler) and maps them to ECS fields. The `field_name` option is ignored.

This mode is designed to work with:
- [`open-telemetry/opentelemetry-auto-symfony`](https://github.com/opentelemetry-php/contrib-auto-symfony) with `OTEL_PHP_PSR3_MODE=inject`
- Any OpenTelemetry setup that injects flat tracing keys into Monolog context

### Configuration

```yaml
ecs_logging:
processor:
tracing:
enabled: true
mode: 'opentelemetry'
```

No additional dependency is required: the processor reads from keys already present in the log context. The flat OTel keys (`trace_id`, `span_id`, `trace_flags`) are cleaned up automatically.

### ECS output

| Context key | ECS field |
|---|---|
| `trace_id` | `trace.id` |
| `span_id` | `transaction.id`, `span.id` |
| `trace_flags` | removed (not an ECS field) |

```json
{
"trace": { "id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" },
"transaction": { "id": "f6e5d4c3b2a1f6e5" },
"span": { "id": "f6e5d4c3b2a1f6e5" }
}
```
12 changes: 12 additions & 0 deletions docs/processors/user.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# UserProcessor

Know who triggered every log record. The authenticated user is injected automatically.

Injects the currently authenticated user as [ECS `user.*`](https://www.elastic.co/guide/en/ecs/current/ecs-user.html) fields via a user provider.

## Configuration
Expand Down Expand Up @@ -85,3 +87,13 @@ ecs_logging:
# The built-in EcsUserProvider already implements it.
provider: 'App\Security\CustomEcsUserProvider'
```

ECS output:

```json
{
"user": {
"name": "john.doe"
}
}
```
Loading
Loading