Recipe: Decision And DLQ Logs
Configure decision output, bad-record handling, and dashboard log inspection.
Decision logs are compact records for downstream routing. Dead-letter logs keep bad input out of the hot path while preserving samples for debugging.
Python Engine Logs
import blazerules
cfg = blazerules.EngineConfig()
cfg.output_detail = blazerules.OutputDetail.DECISIONS
cfg.decision_log_path = "decisions.ndjson"
cfg.ingest_error_mode = blazerules.IngestErrorMode.SKIP_TO_DEAD_LETTER
cfg.dead_letter_path = "dead_letters.ndjson"
engine = blazerules.RuleEngine(cfg)
engine.load_rules("rules.yaml")
result = engine.evaluate_ndjson(payload)CLI equivalent: write compact decisions with the agent
blazerules_agent \
--rules rules.yaml \
--input stdin \
--output ndjson \
--output-path decisions.ndjsonThe agent writes compact decisions. Engine-level dead-letter policy is configured through EngineConfig in Python/C++.
Agent Output
blazerules_agent \
--rules rules.yaml \
--input stdin \
--output ndjson \
--output-path decisions.ndjsonPython equivalent: evaluate a batch and write decisions yourself
import json
import time
import blazerules
engine = blazerules.RuleEngine()
engine.load_rules("rules.yaml")
result = engine.evaluate_ndjson(payload)
with open("decisions.ndjson", "a", encoding="utf-8") as out:
ts_ms = int(time.time() * 1000)
for row, decision in enumerate(result.decisions):
out.write(json.dumps({
"ts_ms": ts_ms,
"batch_row": row,
"decision": decision,
"score": result.scores[row],
"risk_band": result.risk_bands[row],
"winning_rule_id": result.winning_rule_ids[row],
}) + "\n")The agent writes one decision per evaluated row, tagged with the instance name and ruleset version:
{"ts_ms":1782150000000,"instance":"checkout","batch_row":12,"ruleset_version":"1.0.0","matched":true,"decision":"BLOCK","score":91.0,"risk_band":"HIGH","winning_rule_id":"blocked_device"}When the instance runs one or more ONNX models (model_score rules), each model's raw per-record prediction is appended too — as a model_scores object in NDJSON, or a model.<name> float column in Arrow output:
{"ts_ms":1782150000000,"instance":"fraud","decision":"REVIEW","score":72.0,"risk_band":"HIGH","winning_rule_id":"ml_high_risk","model_scores":{"model.risk_logistic":0.83,"model.loss_regression":118.4}}The agent writes a dead-letter file when you pass --dead-letter-path PATH (or set output.dead_letter_path on an instance); dead-letter output is always NDJSON, even when the decision log is Arrow. The Python/C++ paths use EngineConfig.dead_letter_path, and the CLI uses blazerules eval --dead-letter-log PATH.
Dead-Letter Mode
Use dead-letter mode when bad records should be inspectable instead of silently counted:
cfg.ingest_error_mode = blazerules.IngestErrorMode.SKIP_TO_DEAD_LETTER
cfg.dead_letter_path = "dead_letters.ndjson"CLI equivalent: inspect dead-letter output with the dashboard
blazerules_dashboard \
--decision-log decisions.ndjson \
--dead-letter-log dead_letters.ndjson \
--host 127.0.0.1 \
--port 9470The result still reports counters:
print(result.messages_processed)
print(result.messages_skipped)
print(result.error_counts)
print(result.error_samples)Dashboard
blazerules_dashboard \
--decision-log decisions.ndjson \
--dead-letter-log dead_letters.ndjson \
--rules rules.yaml \
--host 127.0.0.1 \
--port 9470Open http://127.0.0.1:9470.
Updated 4 days ago