17: Trace
Phlo's trace mode is a runtime instrumentation layer that logs every call to a generated method, prop getter, static or native function with timing and arguments. Per request Phlo writes a JSON dump to data/trace/<id>.json. Meant for debugging and profiling, not for production.
17.1: What trace does
With trace on, Phlo's compiler injects one line at the top of every generated method:
trace('class->method', compact('arg1', 'arg2'))
And Phlo loads functions.trace.php (a generated variant of functions.php) so that native helpers, esc(), arr(), loop(), view(), apply(), everything, get a trace call too. The result: a complete chronological log of what happened during a request.
17.2: Enabling
In your dev entrypoint:
phlo_app (
id: 'Example',
host: 'dev.example.nl',
build: true,
debug: true,
trace: true,
)
Then run build::run so the generated PHP contains the trace() injections. From the next request on, every call is logged.
Trace output goes to data/trace/. The directory is created automatically.
17.3: What a trace contains
One JSON file per request with:
| Field | Contents |
|---|---|
id |
Date-time + random suffix, also the file name |
path, method, route |
Request context |
ts, ms, count |
Start timestamp, total duration, number of events |
active |
Map per file → kind → call count (quick overview of "what got touched a lot") |
sequence |
Order of first touch per file (visualizes the request path through your app) |
events |
Complete log: see below |
Each event in events:
{
"t": 12.345,
"k": "call",
"c": "user",
"n": "byEmail",
"node": "user->byEmail",
"f": "user.phlo",
"args": {"email": "jordi@example.nl"}
}
| Field | Meaning |
|---|---|
t |
Offset in ms since request start |
k |
Kind: call, static, get (prop get), set (prop set), function |
c, n |
Class and name |
f |
Source file (resolved via classmap + sourcemap) |
args |
Snipped arguments, see X.4 |
17.4: Argument snipping
Full argument values would make the trace unreadable. Phlo snips:
| Type | Becomes |
|---|---|
null, bool, int, float |
unchanged |
string > 200 characters |
... truncated at 200 |
array |
'[N items]' (length only) |
object with an id property |
{class: ..., id: ...} |
Other object |
{class: ...} |
That is enough to see which records were touched without dumping the entire payload.
17.5: Reading via the Phlo Control Center
The Phlo Control Center has a Trace tab that reads data/trace/index.json. The most recent trace is at the top; a selectbox opens older ones. Per trace you see active, sequence and the event stream.
index.json keeps the last 100 traces. Older ones are auto-pruned along with their JSON file.
17.6: Maintenance: `build::traceShadow`
functions.trace.php is a generated file. Whenever you add something to or change something in functions.php, regenerate it:
php www/app.php build::traceShadow
This parses functions.php and injects, in every function foo($a, $b), as the first statement:
trace('foo', compact('a', 'b'))
Then you align the contents of functions.trace.php with the source. The command is only relevant for those working on the Phlo runtime itself, not for app code.
17.7: When to use it, when not
Do:
- A request is slow "somewhere" and you want to know where the time goes
- Exploring an unfamiliar app:
sequenceshows you the actual path through the code - Debugging a race condition or side effect:
eventsshows exact order + timing - For teaching: showing how a Phlo request actually unfolds
Don't:
- Production, every method call costs a log entry and a disk write per request
thread: trueworkers, trace writes per request and is therefore, just likebuild, not worker-safe- Performance measurement down to the microsecond, the instrumentation itself adds overhead
Turn trace: true on when you need it; turn it off when you're done. The Control Center keeps showing the historical traces until the auto-prune cleans them up.