Unomi 3 · Debugging
Request tracing with explain=true
See condition evaluations, rule actions, and timings on /cxs/context.json and /cxs/eventcollector.
When a rule “should have fired” or a session update looks wrong, guessing from logs is slow.
Unomi 3 adds query-parameter tracing (
UNOMI-873
/
#775):
append explain=true and the response includes a requestTracing tree.
Where it works
POST/GET/cxs/context.jsonand/cxs/context.jsPOST/GET/cxs/eventcollector
Capabilities
- Opt-in per request —
?explain=trueenables tracing for that call only - Response field —
requestTracingonContextResponse/EventCollectorResponse - Hierarchical tree — nested
TraceNodechildren for nested work - Per-node fields —
operationType,description,context,result,startTime,endTime,traces[],children[] - What gets traced — request processing, event validation, rule evaluation, condition evaluation (property/boolean/…), action execution (visit counters, segment eval, age, …), personalization / content-filter evaluation on context requests
- Timings — wall-clock start/end on every node for latency hunting
- Auth gate —
ROLE_UNOMI_ADMIN/ROLE_UNOMI_TENANT_ADMINonly; public API key alone returns HTTP 403 - Low overhead when off — tracer disabled unless
explain=true; reset after the request - Programmatic API —
TracerService/RequestTracerfor services that start nested operations - IT coverage — authorized explain success + unauthorized 403 paths
Auth: tenant admin, not the public key
Tracing is restricted to ADMINISTRATOR / TENANT_ADMINISTRATOR.
In practice that means authenticate with the tenant private API key (Basic tenantId:privateApiKey), not the public track key.
We verified the following against a local Unomi 3.1.0-SNAPSHOT (HTTPS). Public-key + explain=true returned HTTP 403 with:
{"errorMessage":"Insufficient privileges to access tracing information"}
Integration coverage: EventsCollectorIT#testEventsCollectorWithExplain and #testEventsCollectorWithExplainUnauthorized.
Prerequisite: create the scope
JSON Schema validation rejects events whose scope is unknown for the tenant
(Unknown scope value … for value myscope). Create the scope first:
curl -X POST http://localhost:8181/cxs/scopes \
-u 'YOUR_TENANT_ID:YOUR_PRIVATE_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"itemId": "myscope",
"itemType": "scope",
"metadata": { "id": "myscope", "name": "My scope" }
}'
Example: event collector
Include target.properties.pageInfo (at least referringURL / pagePath).
Built-in rules such as sessionReferrer read those fields; omitting them still works but logs noisy WARNs.
curl -X POST 'http://localhost:8181/cxs/eventcollector?explain=true' \
-u 'YOUR_TENANT_ID:YOUR_PRIVATE_API_KEY' \
-H 'Content-Type: application/json' \
-d @- <<'EOF'
{
"sessionId": "trace-demo-1",
"events": [
{
"eventType": "view",
"scope": "myscope",
"source": {
"itemType": "site",
"scope": "myscope",
"itemId": "mysite"
},
"target": {
"itemType": "page",
"scope": "myscope",
"itemId": "homepage",
"properties": {
"pageInfo": {
"pageID": "homepage",
"pagePath": "/home",
"pageName": "Home",
"destinationURL": "https://example.com/home",
"referringURL": "https://example.com/",
"language": "en"
}
}
}
}
]
}
EOF
Verified excerpt (trimmed) from a live 3.1 response — session create, condition checks, visit counters:
{
"updated": 6,
"requestTracing": {
"operationType": "event-collection",
"description": "Event collection request processed successfully",
"startTime": 1784099217601,
"endTime": 1784099217629,
"traces": [
"Sending event: sessionCreated - Context: …",
"Processing event for profile: …"
],
"children": [
{
"operationType": "condition-evaluation",
"description": "Condition evaluation completed",
"result": "true",
"children": [
{
"operationType": "boolean",
"description": "OR condition succeeded on subcondition",
"result": "true"
}
]
},
{
"operationType": "action-execution",
"description": "Action execution completed",
"children": [
{
"operationType": "increment-property",
"description": "Property incremented successfully",
"traces": [
"Processing property increment - Context: {propertyName=nbOfVisits, …}",
"Property increment result - Context: {newValue=1, isUpdated=true}"
]
}
]
}
]
}
}
Example: context.json
curl -X POST \
'http://localhost:8181/cxs/context.json?sessionId=trace-demo-1&explain=true' \
-u 'YOUR_TENANT_ID:YOUR_PRIVATE_API_KEY' \
-H 'Content-Type: application/json' \
-d @- <<'EOF'
{
"source": {
"itemId": "homepage",
"itemType": "page",
"scope": "myscope"
},
"requiredProfileProperties": ["*"],
"requireSegments": true
}
EOF
Live trace root on the same stack:
"requestTracing": {
"operationType": "context-request",
"description": "Context request processed successfully",
"children": [
{ "operationType": "condition-evaluation", … },
{ "operationType": "action-execution",
"children": [
{ "operationType": "evaluate-age", "description": "No changes needed" },
{ "operationType": "evaluate-segments", "description": "No changes needed" }
]
}
]
}
How to read the tree
- Timings — subtract
endTime - startTimeon hot nodes when hunting latency - Conditions — look for
result: falseon rules you expected to match - Actions —
increment-property,evaluate-segments, visit helpers show whether profile state actually changed
For save-time condition checks (before runtime), use condition validation in the manual; tracing is the runtime companion.
Manual: explain / tracing Manual improvements Task scheduler