Logs
Forward your Symfony application logs to Traceway via the OpenTelemetry Logs OTLP endpoint. The recommended path is a Monolog handler that bridges Symfony's standard PSR-3 logger to the OTel Logs SDK — all of your existing $logger->info(...) / $logger->error(...) calls keep working, and each log is automatically linked to the active trace and span.
Install the Logs Packages
composer require \
open-telemetry/opentelemetry-logger-monolog \
open-telemetry/sdk \
open-telemetry/exporter-otlpopentelemetry-logger-monolog— Monolog handler that emits records to the OTel Logs SDK.sdkandexporter-otlp— already installed if you completed the Quick Start, listed here for completeness.
Configure Environment Variables
Extend the env config from the Quick Start to also enable the logs exporter:
# Existing traces + metrics config from the Quick Start
OTEL_PHP_AUTOLOAD_ENABLED=true
OTEL_SERVICE_NAME=my-symfony-app
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
# New: enable OTLP logs
OTEL_LOGS_EXPORTER=otlp
OTEL_EXPORTER_OTLP_PROTOCOL=http/json
OTEL_EXPORTER_OTLP_ENDPOINT=https://your-traceway-instance.com/api/otel
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer your-project-token"OTEL_EXPORTER_OTLP_ENDPOINT is the base URL — the logs exporter appends /v1/logs automatically. No extra endpoint config is needed.
Wire the Monolog Handler
Register the OTel Monolog handler in config/packages/monolog.yaml so every channel (app, request, security, doctrine, …) is forwarded to Traceway:
# config/packages/monolog.yaml
monolog:
handlers:
traceway:
type: service
id: OpenTelemetry\Contrib\Logs\Monolog\Handler
level: info
channels: ~ # all channels
services:
OpenTelemetry\Contrib\Logs\Monolog\Handler:
arguments:
$loggerProvider: '@OpenTelemetry\API\Logs\LoggerProviderInterface'
$level: !php/const Monolog\Logger::INFO
$bubble: trueThe $loggerProvider argument is resolved by the OTel PHP autoloader — it's available as a service as soon as OTEL_PHP_AUTOLOAD_ENABLED=true is set.
Set level to the Monolog level threshold you want forwarded. Use debug during early development to verify the pipeline, then raise it to info or warning in production to keep volume under control.
Emit Logs With Trace Context
Inject Symfony's standard Psr\Log\LoggerInterface as usual — the Monolog handler takes care of bridging to OTel, and trace context attaches automatically:
// src/Controller/OrderController.php
namespace App\Controller;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class OrderController
{
public function __construct(
private readonly LoggerInterface $logger,
) {}
#[Route('/orders', methods: ['POST'])]
public function create(): Response
{
$this->logger->info('order received', ['order.id' => 'ord_123']);
try {
// ... business logic ...
$this->logger->info('order processed', ['order.id' => 'ord_123']);
return new JsonResponse(['status' => 'ok']);
} catch (\Throwable $e) {
$this->logger->error('order failed', [
'order.id' => 'ord_123',
'exception' => $e->getMessage(),
]);
throw $e;
}
}
}Because the controller runs inside the OTel-instrumented request span, every log emitted here carries that span's trace_id and span_id automatically. Open the endpoint's trace in the Traceway dashboard and the Logs tab will show these records attached.
The same is true for logs emitted inside Messenger handlers, Console commands, and any other code that runs under an active span.
Severity Mapping
Monolog levels are mapped to OpenTelemetry severity numbers (and therefore Traceway's TRACE → FATAL labels) as follows:
| Monolog Level | OTLP Severity Number | Traceway Severity |
|---|---|---|
DEBUG | 5 | DEBUG |
INFO | 9 | INFO |
NOTICE | 10 | INFO |
WARNING | 13 | WARN |
ERROR | 17 | ERROR |
CRITICAL | 18 | ERROR |
ALERT | 19 | ERROR |
EMERGENCY | 21 | FATAL |
Only records at or above the handler's configured level threshold are forwarded.
Test Your Integration
Add a route that logs at multiple levels, hit it once, and check the Logs page in the Traceway dashboard:
// src/Controller/LogTestController.php
namespace App\Controller;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class LogTestController
{
public function __construct(
private readonly LoggerInterface $logger,
) {}
#[Route('/log-test', name: 'log_test')]
public function test(): Response
{
$this->logger->debug('debug sample');
$this->logger->info('info sample', ['request.id' => 'req_1']);
$this->logger->warning('warning sample');
$this->logger->error('error sample', ['order.id' => 'ord_1']);
return new JsonResponse(['emitted' => 4]);
}
}Visit /log-test, then open Logs in the dashboard. You should see all four records within a few seconds, each linked to the trace for that request.
Next Steps
- Exceptions — record caught exceptions with context
- Metrics — custom counters, histograms, and gauges
- OTel Logs reference — full OTLP logs mapping and severity details