- Posted on
- • Artificial Intelligence
Artificial Intelligence Bash Logging and Error Handling
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
AI‑Powered Bash Logging and Error Handling: Turn 2 A.M. Incidents into Actionable Insights
Ever been paged for a broken Bash job and found a single line of cryptic output to work with? Traditional logs tell you what failed, not why. By combining robust Bash patterns with a lightweight dose of AI, you can capture better context, summarize root causes, and propose fixes automatically—before you even open your laptop.
This article shows you how to:
Build rock‑solid logging and error handling in Bash
Emit structured, machine‑readable logs
Automatically summarize failures with an AI endpoint
Add retries and send lightweight alerts
You’ll get copy‑pasteable snippets and distro‑specific install commands.
Why this matters
Bash is everywhere: CI pipelines, cron jobs, container entrypoints, maintenance tasks.
But defaults are brittle: silent errors, swallowed failures in pipelines, and unstructured logs.
Adding structure (JSON logs, traps, error context) makes problems observable.
Adding AI on top turns “noise” into “next steps,” proposing likely root causes and fixes, and cutting time-to-resolution.
Prerequisites and installation
We’ll use:
curl for HTTP calls (AI, webhooks)
jq for JSON building/parsing
logger (from util-linux) for syslog/journald integration
Install with your package manager:
Debian/Ubuntu (apt):
sudo apt update sudo apt install -y curl jq util-linuxFedora/RHEL/CentOS (dnf):
sudo dnf install -y curl jq util-linuxopenSUSE (zypper):
sudo zypper refresh sudo zypper install -y curl jq util-linux
Note: journald/systemd are typically present by default on modern distros.
1) Build a solid Bash foundation: strict mode, traps, and a single log function
Start every serious script with strict error handling and a consistent logger. This alone catches a large class of “works on my machine” issues.
#!/usr/bin/env bash
set -Eeuo pipefail
# Configuration (override via environment variables)
LOG_FILE=${LOG_FILE:-"$PWD/myjob.log"}
LOG_TAG=${LOG_TAG:-myjob}
# Optional AI + alerting
AI_ENDPOINT=${AI_ENDPOINT:-https://api.openai.com/v1/responses}
AI_MODEL=${AI_MODEL:-gpt-4.1-mini}
AI_API_KEY=${AI_API_KEY:-""}
SLACK_WEBHOOK_URL=${SLACK_WEBHOOK_URL:-""}
mkdir -p "$(dirname "$LOG_FILE")"
ts() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
log() {
local level=${1:-INFO}; shift || true
local msg="$*"
# Console + file + syslog (best-effort)
printf "%s [%s] %s\n" "$(ts)" "$level" "$msg" \
| tee -a "$LOG_FILE" \
| logger -t "$LOG_TAG" -p "user.${level,,}" 2>/dev/null || true
}
set -Eeuo pipefail enables early, explicit failure (including pipeline errors).
logger ships with util-linux; it forwards to syslog/journald so operations teams can use journalctl/centralized logging.
LOG_FILE defaults to the current directory to avoid permission headaches.
2) Emit structured, machine‑readable logs (JSON)
Structured logs make it easy to query, alert, and feed AI. Add a JSON logger that captures command, cwd, pid, and exit codes.
log_json() {
local level=${1:-INFO}; shift || true
local message="$1"; shift || true
local exit_code="${1:-0}"
jq -c -n \
--arg t "$(ts)" \
--arg level "$level" \
--arg msg "$message" \
--arg cmd "${BASH_COMMAND:-}" \
--arg cwd "$PWD" \
--arg script "${BASH_SOURCE[0]}" \
--arg pid "$$" \
--arg exit "$exit_code" \
'{time:$t, level:$level, message:$msg,
context:{cmd:$cmd, cwd:$cwd, script:$script, pid:($pid|tonumber), exit:($exit|tonumber)}}' \
| tee -a "$LOG_FILE" \
| logger -t "$LOG_TAG" 2>/dev/null || true
}
Query examples:
Recent errors in file logs:
jq -r 'select(.level=="ERROR") | .time + " " + .message' myjob.logFrom journald by tag:
journalctl -t myjob -o json-pretty | jq -r '.MESSAGE'
3) Wire in AI summaries on failure (with redaction and retries)
On error, trap the failure, capture context, and ask an AI endpoint to propose likely root causes and next steps. Keep it opt‑in via an API key and redact sensitive values.
redact() {
# Best-effort scrubbing; extend with your org’s patterns
sed -E \
-e 's/(API[_-]?KEY|TOKEN|PASSWORD)=\S+/\1=***REDACTED***/g' \
-e 's/(Authorization: Bearer) [A-Za-z0-9._-]+/\1 ***REDACTED***/g'
}
retry() {
local max=${1:-3}; shift
local delay=${1:-1}; shift
local n=1
until "$@"; do
local rc=$?
if (( n >= max )); then
log ERROR "Command failed after $n attempts (rc=$rc): $*"
return $rc
fi
log WARN "Attempt $n failed (rc=$rc). Retrying in ${delay}s: $*"
sleep "$delay"
delay=$(( delay * 2 ))
n=$(( n + 1 ))
done
}
ai_summarize() {
local err_msg="$(printf "%s" "$1" | redact)"
local context="$(printf "%s" "$2" | redact)"
[[ -n "$AI_API_KEY" ]] || { log WARN "AI_API_KEY not set; skipping AI summary."; return 0; }
local prompt="You are a Linux/Bash SRE assistant. Summarize a probable root cause and propose 1–3 concrete next actions.
Error:
$err_msg
Recent log tail:
$context"
local payload
payload=$(jq -n --arg model "$AI_MODEL" --arg input "$prompt" '{model:$model, input:$input, temperature:0.2}')
local resp summary
resp=$(curl -sS -H "Authorization: Bearer $AI_API_KEY" -H "Content-Type: application/json" \
-d "$payload" "$AI_ENDPOINT" || true)
summary=$(jq -r 'try .output_text // .choices[0].message.content // empty' <<<"$resp")
if [[ -n "$summary" ]]; then
log ERROR "AI summary: $summary"
printf "%s\n" "$summary" >> "${LOG_FILE%.log}.ai.log" || true
if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
jq -n --arg text "*$LOG_TAG* AI summary:\n$summary" '{text:$text}' \
| curl -sS -X POST -H "Content-type: application/json" --data @- "$SLACK_WEBHOOK_URL" >/dev/null || true
fi
else
log WARN "No AI summary parsed. Raw response: $resp"
fi
}
on_err() {
local rc=$?
local cmd=${BASH_COMMAND:-}
local err="Command: $cmd | Exit: $rc"
local taillog
taillog=$(tail -n 50 "$LOG_FILE" 2>/dev/null || true)
log_json ERROR "Trap ERR" "$rc"
ai_summarize "$err" "$taillog"
exit "$rc"
}
trap on_err ERR
trap 'log INFO "Completed with exit code $?";' EXIT
How it works:
Any uncaught error triggers on_err (thanks to set -Eeuo pipefail).
We append structured context and tail the log.
We call an AI endpoint with a minimal, focused prompt.
If available, we also post the summary to a Slack Incoming Webhook.
Security note:
- Do not send secrets to external services. The redact function is a guardrail; adapt it to your environment and prefer private/self‑hosted models for sensitive workloads.
4) A realistic main() with retries and a forced failure to test
Drop this in the same script to see the flow end-to-end.
main() {
log INFO "Starting job..."
log_json INFO "Structured block begins"
# Example of a flaky call with retries
retry 3 1 curl -fsS https://example.invalid/healthz
log INFO "Doing a thing that might fail..."
false # Intentionally fail to test traps, summaries, and alerts
}
main "$@"
Run it:
bash ./myjob.sh
You should see:
Human‑readable logs in myjob.log
Structured JSON entries piped to journald (journalctl -t myjob)
An AI summary appended to myjob.ai.log (if AI_API_KEY is set)
Configure environment for AI and Slack:
export AI_API_KEY="your_api_key_here"
export AI_ENDPOINT="https://api.openai.com/v1/responses" # or your self-hosted compatible endpoint
export AI_MODEL="gpt-4.1-mini" # or another available model
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/XXX/YYY/ZZZ"
Operational tips and real‑world patterns
Make logs discoverable:
- File logs: predictable paths (e.g., /var/log/myteam/myjob.log)
- Syslog/journald: consistent LOG_TAG and use journalctl -t myjob -o short-iso
Test your traps:
- Simulate errors with false or a failing curl. Confirm AI summaries appear.
Keep prompts short and specific:
- Feed only the last N lines of context. Long prompts are slower and cost more.
Prefer self‑hosted endpoints for sensitive data:
- Point AI_ENDPOINT to an internal, OpenAI‑compatible gateway if needed.
CI/CD integration:
- Export AI_API_KEY as a secret, run the same script in pipelines, and surface AI summaries as job annotations.
Troubleshooting
logger not found:
- Install util-linux (see package commands above).
jq not found:
- Install jq (see package commands above).
Permission denied writing logs:
- Change LOG_FILE to a writable path (default uses $PWD).
No AI summary:
- Check AI_API_KEY, AI_ENDPOINT reachability, and model name.
- Inspect raw response in logs when parsing fails.
Conclusion and next steps
With a few functions and sane defaults, you’ve upgraded Bash from “best effort” to “observable and explainable.” The combination of strict mode, structured logs, and AI summaries makes root cause analysis faster and on‑call far less painful.
Your next steps: 1) Copy the template into your most brittle scripts. 2) Install curl, jq, and util-linux with your package manager. 3) Set AI_API_KEY and test the failure path. 4) Wire the script into cron or systemd and watch journalctl -t myjob. 5) Iterate on redaction rules and prompts to fit your environment.
If this helped, share the template with your team and standardize it as your org’s Bash skeleton.