How to Emit Structured Logs From Pipeline Steps
Structured logs are one JSON object per line, which lets aggregators filter builds by field instead of grepping free text.
Print one JSON object per log line with stable keys (level, stage, run, message). Aggregators like Loki and Elasticsearch index those fields so you can query level=error stage=build across all runs.
Steps
- Agree on a small schema: level, stage, run, message.
- Emit one JSON object per line from each step.
- Let the aggregator parse JSON so fields become queryable.
Emit a structured line
.github/workflows/ci.yml
- name: Structured log
run: |
log() {
printf '{"level":"%s","stage":"%s","run":"%s","msg":"%s"}\n' \
"$1" "build" "${{ github.run_id }}" "$2"
}
log info "starting build"
npm run build || { log error "build failed"; exit 1; }Gotchas
- Escape quotes and newlines in the message, or the line stops being valid JSON.
- Keep the key set stable; renaming fields breaks saved queries and dashboards.