- Posted on
- • Artificial Intelligence
Artificial Intelligence SLA Reporting
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence SLA Reporting with Bash: From Logs to Guarantees
Your AI features are fast—until they aren’t. A spike in 429s, a provider hiccup, or a 2x latency regression can turn “wow” into “whoa” for your users. If you ship AI to production, you need Service Level Agreements (SLAs) you can actually measure, report, and act on—without waiting for a vendor dashboard.
This post shows how to build practical, vendor-agnostic AI SLA reporting using nothing but Linux, Bash, and a few standard CLI tools. You’ll define AI-specific SLIs/SLOs, instrument logs, generate daily reports, and automate them via cron or systemd. Everything runs locally and integrates with your existing ops toolchain.
What problem are we solving?
AI systems introduce new failure modes: rate limits, model saturation, token-based timeouts, and vendor-side safety blocks.
Traditional web SLAs (availability, error rate, latency) still apply, but need AI-aware metrics (token usage, rate-limit pressure, policy violation flags).
Vendor dashboards are useful, but you still need your own ground truth tied to your traffic, regions, and models.
This approach gives you:
A reproducible, version-controlled reporting pipeline
Independence from any single provider’s UI or definitions
Extensible metrics that track what matters to your users
Validity: Why measure AI SLAs this way?
It’s the same proven practice SRE teams use for web services (SLIs/SLOs/error budgets), adapted for AI-specific signals.
Logs are your most portable, long-term artifact. They let you reconstruct incidents and drill down by model, region, or customer.
Bash + jq pipelines are transparent and reviewable. No black boxes—just text processing you can audit and improve.
Prerequisites (Linux)
We’ll use these tools:
- curl (HTTP), jq (JSON), gawk (awk), bc (math), mailx (optional email)
Install them with your package manager:
Debian/Ubuntu (apt):
sudo apt update sudo apt install -y curl jq gawk bc mailutilsNote: mailutils provides the mail command. Optional; only needed if you’ll email reports.
Fedora/RHEL/CentOS (dnf):
sudo dnf install -y curl jq gawk bc mailxopenSUSE (zypper):
sudo zypper install -y curl jq gawk bc mailx
Step 1: Define AI SLIs and SLOs
Start with a small, high-signal set:
Availability
- Experience availability: 2xx / total requests
- Provider availability: 2xx / (total - 4xx excluding 429). This removes clear client errors but still counts vendor rate-limiting.
- Example SLO: provider availability ≥ 99.9%
Latency
- P95 latency for successful requests (ms)
- Example SLO: P95 ≤ 1000 ms
Rate limits
- Percentage of 429 responses
- Target: keep under 0.2% or what your plan supports
Token usage
- Total tokens in/out as a leading indicator for capacity and cost
- Not an SLO by itself, but crucial for planning
Policy/safety blocks (optional)
- Share of responses flagged by your policy layer (policy_violation == true)
- Target derived from your risk tolerance
Write down your SLOs explicitly; you’ll evaluate PASS/FAIL in the script.
Step 2: Instrument logs for AI traffic
Emit one JSON line per request, capturing enough to reconstruct SLIs:
Example JSONL (one line per request):
{"ts":"2026-07-08T14:31:22Z","service":"ai-gw","provider":"openai","model":"gpt-4o-mini","status":200,"latency_ms":423,"tokens_in":118,"tokens_out":201,"policy_violation":false,"rate_limited":false,"region":"us-east-1"}
{"ts":"2026-07-08T14:31:23Z","service":"ai-gw","provider":"anthropic","model":"claude-3-haiku","status":429,"latency_ms":0,"tokens_in":41,"tokens_out":0,"policy_violation":false,"rate_limited":true,"region":"us-west-2"}
Store them at:
- /var/log/ai_gateway/requests.jsonl
Optional logrotate snippet (/etc/logrotate.d/ai_gateway):
/var/log/ai_gateway/requests.jsonl {
daily
rotate 14
compress
missingok
notifempty
copytruncate
}
Tip: Keep timestamps in UTC ISO-8601 with Z (lexicographic comparisons in jq then “just work”).
Step 3: Generate a daily SLA report with Bash
Save the script below as ai-sla-report.sh, make it executable, and run it. It computes availability, latency percentiles, token totals, and flags SLO PASS/FAIL for a time window (defaults to “yesterday UTC”).
#!/usr/bin/env bash
set -euo pipefail
# Configuration (overrides via environment)
LOG_FILE="${LOG_FILE:-/var/log/ai_gateway/requests.jsonl}"
SINCE="${SINCE:-$(date -u -d 'yesterday 00:00:00' +%FT%TZ)}"
UNTIL="${UNTIL:-$(date -u -d 'yesterday 23:59:59' +%FT%TZ)}"
SLO_AVAILABILITY="${SLO_AVAILABILITY:-99.9}" # Provider availability target
SLO_P95_LATENCY_MS="${SLO_P95_LATENCY_MS:-1000}" # Target in ms
# Dependencies check
for cmd in jq awk bc date; do
command -v "$cmd" >/dev/null 2>&1 || { echo "Missing dependency: $cmd"; exit 1; }
done
TMPDIR="$(mktemp -d)"
trap 'rm -rf "$TMPDIR"' EXIT
FILTER='.ts >= env(SINCE) and .ts <= env(UNTIL)'
# Filter once
if [ ! -f "$LOG_FILE" ]; then
echo "Log file not found: $LOG_FILE" >&2
exit 1
fi
jq -r "select($FILTER)" "$LOG_FILE" > "$TMPDIR/subset.jsonl"
total=$(wc -l < "$TMPDIR/subset.jsonl" | awk '{print $1}')
if [ "$total" -eq 0 ]; then
echo "AI SLA Report ($SINCE to $UNTIL, UTC)"
echo "No records found in the selected time window."
exit 0
fi
# Status buckets
succ=$(jq -r 'select(.status>=200 and .status<300) | 1' "$TMPDIR/subset.jsonl" | wc -l | awk '{print $1}')
fourxx_no429=$(jq -r 'select(.status>=400 and .status<500 and .status!=429) | 1' "$TMPDIR/subset.jsonl" | wc -l | awk '{print $1}')
rate429=$(jq -r 'select(.status==429) | 1' "$TMPDIR/subset.jsonl" | wc -l | awk '{print $1}')
fivexx=$(jq -r 'select(.status>=500) | 1' "$TMPDIR/subset.jsonl" | wc -l | awk '{print $1}')
# Availabilities
avail_all=$(printf 'scale=4; 100*%s/%s\n' "$succ" "$total" | bc)
denom_no4xx=$(( total - fourxx_no429 ))
if [ "$denom_no4xx" -gt 0 ]; then
avail_no4xx=$(printf 'scale=4; 100*%s/%s\n' "$succ" "$denom_no4xx" | bc)
else
avail_no4xx="100"
fi
# Latencies for successful responses
jq -r 'select(.status>=200 and .status<300 and .latency_ms!=null) | .latency_ms' "$TMPDIR/subset.jsonl" | sort -n > "$TMPDIR/latencies.txt"
nlat=$(wc -l < "$TMPDIR/latencies.txt" | awk '{print $1}')
if [ "$nlat" -gt 0 ]; then
p_index() { # nearest-rank percentile
local p=$1
local rank
rank=$(awk -v p="$p" -v n="$nlat" 'BEGIN{r=int((p/100.0)*n+0.999999); if(r<1) r=1; print r}')
sed -n "${rank}p" "$TMPDIR/latencies.txt"
}
p50=$(p_index 50)
p95=$(p_index 95)
p99=$(p_index 99)
else
p50=0; p95=0; p99=0
fi
# Tokens
tin=$(jq -r 'select(.tokens_in!=null) | .tokens_in' "$TMPDIR/subset.jsonl" | awk '{s+=$1} END{print s+0}')
tout=$(jq -r 'select(.tokens_out!=null) | .tokens_out' "$TMPDIR/subset.jsonl" | awk '{s+=$1} END{print s+0}')
ttok=$(( tin + tout ))
# Policy violations (optional field)
pviol=$(jq -r 'select(.policy_violation==true) | 1' "$TMPDIR/subset.jsonl" | wc -l | awk '{print $1}')
# Throughput
start_epoch=$(date -u -d "$SINCE" +%s)
end_epoch=$(date -u -d "$UNTIL" +%s)
duration_sec=$(( end_epoch - start_epoch + 1 ))
rps=$(printf 'scale=3; %s/%s\n' "$total" "$duration_sec" | bc)
# SLO checks
status_slo_avail=$(awk -v a="$avail_no4xx" -v s="$SLO_AVAILABILITY" 'BEGIN{if (a+0 >= s+0) print "PASS"; else print "FAIL"}')
status_slo_p95=$(awk -v p="$p95" -v s="$SLO_P95_LATENCY_MS" 'BEGIN{if (p+0 <= s+0) print "PASS"; else print "FAIL"}')
# Output (Markdown-friendly)
echo "AI SLA Report ($SINCE to $UNTIL, UTC)"
echo ""
echo "- Requests total: $total | Success (2xx): $succ"
echo "- 4xx (excl. 429): $fourxx_no429 | 429s: $rate429 | 5xx: $fivexx"
echo "- Experience availability (2xx/total): ${avail_all}%"
echo "- Provider availability (2xx/(total-4xx_except_429)): ${avail_no4xx}% => SLO ${SLO_AVAILABILITY}%: ${status_slo_avail}"
echo "- Latency ms (success only): P50=${p50} P95=${p95} P99=${p99} => SLO P95 ≤ ${SLO_P95_LATENCY_MS}ms: ${status_slo_p95}"
echo "- Tokens: in=${tin} out=${tout} total=${ttok}"
echo "- Policy violations (if logged): ${pviol}"
echo "- Avg throughput: ${rps} req/s over ${duration_sec}s"
Usage examples:
Run for yesterday (default):
./ai-sla-report.shRun for a custom window:
SINCE="2026-07-08T00:00:00Z" UNTIL="2026-07-08T23:59:59Z" ./ai-sla-report.shTighten the latency SLO to 800 ms:
SLO_P95_LATENCY_MS=800 ./ai-sla-report.sh
Security tip: Logs may include user content. Scrub PII upstream or store only hashes/metadata.
Step 4: Automate reports (cron or systemd) and email
Cron (user-level, runs at 01:05 UTC daily):
crontab -eAdd:
5 1 * * * LOG_FILE=/var/log/ai_gateway/requests.jsonl /path/to/ai-sla-report.sh | tee -a ~/ai-sla/daily.logOptional email delivery
- Ensure mailx is installed (see package manager commands above).
- Send the report:
/path/to/ai-sla-report.sh | mail -s "AI SLA Report $(date -u +\%F)" ops@example.comsystemd timer (root or user units)
- /etc/systemd/system/ai-sla-report.service
[Unit] Description=AI SLA daily report [Service] Type=oneshot Environment=LOG_FILE=/var/log/ai_gateway/requests.jsonl Environment=SLO_AVAILABILITY=99.9 Environment=SLO_P95_LATENCY_MS=1000 ExecStart=/path/to/ai-sla-report.sh- /etc/systemd/system/ai-sla-report.timer
[Unit] Description=Run AI SLA report daily at 01:05 UTC [Timer] OnCalendar=*-*-* 01:05:00 UTC Persistent=true [Install] WantedBy=timers.target- Enable and start:
sudo systemctl daemon-reload sudo systemctl enable --now ai-sla-report.timer
Step 5: Real-world usage examples
New model rollout check
- Slice by model in the script (add select(.model=="gpt-4o-mini")) to compare P95 latencies and 429 rates pre/post rollout.
- Abort rollout if P95 jumps 30% or provider availability dips below 99.9%.
Rate-limit tuning
- Monitor 429% daily. If > 0.2%, increase concurrency backoff or request batching. Re-run report to verify improvement.
Capacity and cost planning
- Track tokens_in/out month-over-month to anticipate spend and negotiate provider quotas before traffic spikes.
Incident review
- During a 5xx spike, the report gives you exact percentages and windows, separated from client-side 4xx noise.
Troubleshooting and extensions
No records found: verify timestamps are in UTC ISO-8601 with Z and match the SINCE/UNTIL window.
Performance: for very large logs, consider splitting per day or pre-aggregating with a streaming job.
Add dimensions: group by provider, region, or customer tier using jq’s group_by and Bash loops.
Export metrics: emit Prometheus text format and scrape locally, or push to your TSDB after each run.
Charts: you can optionally install gnuplot for quick PNG histograms. Install with:
- apt:
sudo apt update && sudo apt install -y gnuplot- dnf:
sudo dnf install -y gnuplot- zypper:
sudo zypper install -y gnuplot
Conclusion and next steps
SLA reporting for AI doesn’t have to start with a SaaS subscription. With Bash, jq, and disciplined logging, you can establish rigorous, auditable SLAs today:
Define clear AI SLIs/SLOs
Log minimal, structured metadata per request
Automate a daily report and wire it into your ops loops
Call to action:
1) Drop the script into your repo and point it at your AI gateway logs.
2) Set initial SLOs, schedule the job, and share the first report with your team.
3) Iterate: add grouping, alerts, or export to your metrics stack.
If you’d like, share your modified version or questions—happy to help you tailor this to your stack.