[🐛 BUG]: mode: raw ignored for temporal plugin
No duplicates 🥲.
- [x] I have searched for a similar issue in our bug tracker and didn't find any solutions.
What happened?
When using the Temporal plugin, RoadRunner still prefixes every log line with a timestamp, level, and channel — even when encoding: json and mode: raw are configured. This breaks JSON parsing in log processors like Elasticsearch, where the prefixed metadata prevents proper decoding of Laravel’s JSON log content.
As a result, log messages appear as raw strings instead of structured JSON objects, making it impossible to extract or filter context fields correctly.
"2025-10-24T08:26:24+0000\tINFO\tserver \t{\"message\":\"PDOException: SQLSTATE[42P01]: Undefined table: 7 ERROR: relation \\\"events\\\" does not exist\\nLINE 1: insert into \\\"events\\\" (\\\"audit_event_id\\\", \\\"env_name\\\", \\\"audit_s...\\n ^ in /var/www/gms/current/vendor/laravel/framework/src/Illuminate/Database/Connection.php:501\\nStack trace:\\n#0 Illuminate\\\\Foundation\\\\Console\\\\Kernel->handle()\\n#42 {main}\",\"context\":{},\"level\":400,\"level_name\":\"ERROR\",\"channel\":\"gms_audit_log\",\"datetime\":\"2025-10-24T08:26:24.853203+00:00\",\"extra\":{}}\n"
Version (rr --version)
- RoadRunner version: v2025.1.1
- Temporal plugin: latest
- Temporal PHP SDK: 2.14.1
How to reproduce the issue?
- Configure
.rr.yaml:
logs:
encoding: json
level: "${TEMPORAL_LOG_LEVEL:-debug}"
mode: "${TEMPORAL_LOG_MODE:-production}"
channels:
temporal:
level: info
mode: raw
rpc.level: error # Only RPC errors
server.level: info # Worker lifecycle
app.level: info # Laravel logs
lock.level: warn # Logs RoadRunner lock events
- Run a Temporal PHP worker (using temporalio/roadrunner-temporal).
- Trigger a workflow that calls Workflow::getLogger()->info().
Relevant log output
2025-10-24T10:35:51+0000 INFO server [2025-10-24 10:35:51] info: Workflow ===> Workflow started {"task_queue":"default","activity":"sendNotification","payload":{"message_key":"MESSAGE - TEST","source":"pev_vrednosti","title_key":"TITLE - TEST","type":"error"}}
2025-10-24T10:35:51+0000 INFO server {"message":"LOG ===> Workflow started","context":{"activity":"sendNotification","payload":{"message_key":"MESSAGE - TEST","source":"pev_vrednosti","title_key":"TITLE - TEST","type":"error"}},"level":200,"level_name":"INFO","channel":"laravel","datetime":"2025-10-24T10:35:51.528914+00:00","extra":{}}
2025-10-24T10:35:51+0000 INFO server [2025-10-24 10:35:51] info: Bridge workflow started {"task_queue":"default","activity":"sendNotification","startedAt":"2025-10-24 10:35:51"}
2025-10-24T10:35:51+0000 INFO server {"message":"Bridge workflow started","context":{"activity":"sendNotification","startedAt":"2025-10-24 10:35:51"},"level":200,"level_name":"INFO","channel":"laravel","datetime":"2025-10-24T10:35:51.529886+00:00","extra":{}}
Hey @koren88 👋🏻
If you want structured logs, you may use mode: json for the temporal logs as well. I'll not break any JSON parser, since it'd be a valid JSON.
But anyway, I'll double-check that, in raw mode, you should have a raw message from the worker. But in that case - you're responsible for producing a valid JSON. This log will still break JSON parsing:
[2025-10-24 10:35:51] info: Bridge workflow started {"task_queue":"default","activity":"sendNotification","startedAt":"2025-10-24 10:35:51"}
Thanks @rustatian — makes sense.
Just to clarify what we’re trying to achieve: we don’t need RR to validate or format JSON, we only need mode: raw to pass through the message exactly as produced by the worker — without the timestamp, level, or channel prefix.
Our worker already emits valid JSON — for example:
{"message":"Workflow started","context":{"activity":"sendNotification"}}
But currently, it’s still prefixed like this:
2025-10-24T10:35:51+0000 INFO server {"message":"Workflow started","context":{"activity":"sendNotification"}}
That prefix breaks structured ingestion in Elasticsearch, because the parser expects the line to start with {.
If mode: raw just streamed the payload untouched, that would solve the problem completely.
Would this config behave that way once the change is applied?
logs:
encoding: json
level: "${TEMPORAL_LOG_LEVEL:-debug}"
mode: "${TEMPORAL_LOG_MODE:-production}"
channels:
temporal:
level: info
mode: raw
Just to clarify what we’re trying to achieve: we don’t need RR to validate or format JSON, we only need mode: raw to pass through the message exactly as produced by the worker — without the timestamp, level, or channel prefix.
Yep, this looks like a bug. That info should not be included with raw log. I'll check that.