|
#!/bin/bash |
|
#------------------------------------------------------------------------------ |
|
# Azure Web App Log Streaming Utility |
|
# |
|
# PURPOSE: Stream logs from an Azure Web App, with optional logging to file |
|
# and automatic reconnection if the stream disconnects. |
|
# |
|
# USAGE: ./script.sh <app_name> <resource_group> [slot] [--logfile path/to/logfile] |
|
#------------------------------------------------------------------------------ |
|
|
|
# ===== Command-line argument handling ===== |
|
# First positional arguments are critical for Azure resource identification |
|
APP_NAME="$1" |
|
RESOURCE_GROUP="$2" |
|
SLOT="$3" # Optional deployment slot (defaults to production if not specified) |
|
LOGFILE="" # Will be set if --logfile flag is provided |
|
MAX_SIZE=$((5 * 1024 * 1024)) # 5MB maximum log file size |
|
|
|
# Parse optional flags that can appear anywhere in the argument list |
|
# How: Loop through all arguments searching for the --logfile flag |
|
# Why: Allows for more flexible command-line syntax |
|
while [[ $# -gt 0 ]]; do |
|
case $1 in |
|
--logfile) |
|
LOGFILE="$2" # Capture the logfile path provided after the --logfile flag |
|
shift 2 # Skip past both the flag and its value |
|
;; |
|
*) |
|
shift # Skip any non-recognized arguments |
|
;; |
|
esac |
|
done |
|
|
|
# ===== Input validation ===== |
|
# What: Ensure required parameters are provided |
|
# Why: Fail fast with helpful usage message if mandatory fields are missing |
|
if [ -z "$APP_NAME" ] || [ -z "$RESOURCE_GROUP" ]; then |
|
echo "Usage: $0 <app_name> <resource_group> [slot] [--logfile path/to/logfile]" |
|
exit 1 |
|
fi |
|
|
|
# ===== Signal handling ===== |
|
# What: Set up clean exit on Ctrl+C |
|
# Why: Provide user feedback and graceful termination |
|
trap "echo -e '\n[+] Stopping log tail...'; exit 0" SIGINT |
|
|
|
# ===== Initial status output ===== |
|
echo "[+] Starting log tail for app: $APP_NAME (slot: ${SLOT:-production})" |
|
# Only show logfile info if a logfile was specified |
|
[ -n "$LOGFILE" ] && echo "[+] Logging to: $LOGFILE (max: $((MAX_SIZE / 1024 / 1024))MB)" |
|
|
|
# ===== Log processing function ===== |
|
# What: Process each line of the log stream |
|
# How: Read from stdin, display to console, and optionally write to logfile |
|
# Why: Encapsulates log processing logic for reuse in the main loop |
|
log_and_check_size() { |
|
while read -r line; do |
|
# Always display the log line to stdout |
|
echo "$line" |
|
|
|
# If a logfile was specified, write to it and manage its size |
|
if [ -n "$LOGFILE" ]; then |
|
echo "$line" >>"$LOGFILE" |
|
|
|
# Check file size to prevent unbounded growth |
|
# Why: Prevent filling disk space with too many logs |
|
size=$(stat -c %s "$LOGFILE") |
|
if ((size > MAX_SIZE)); then |
|
echo "[!] Logfile exceeded $((MAX_SIZE / 1024 / 1024))MB, truncating..." |
|
: >"$LOGFILE" # Truncate file by writing empty string to it |
|
fi |
|
fi |
|
|
|
# Detect disconnection in Azure CLI log stream |
|
# Why: Azure CLI sometimes disconnects with an "aborted" message |
|
if echo "$line" | grep -q "^aborted$"; then |
|
echo "[!] Aborted detected in log stream. Reconnecting in 3s..." |
|
break # Exit the loop to trigger reconnection |
|
fi |
|
done |
|
} |
|
|
|
# ===== Main log streaming loop ===== |
|
# What: Continuously connect to Azure log stream, handling disconnections |
|
# How: Loop forever, executing the Azure CLI command and piping to our processor |
|
# Why: Maintain persistent connection to logs despite intermittent failures |
|
while true; do |
|
echo "[*] Connecting to Azure log stream..." |
|
|
|
# Connect to the appropriate log stream based on whether a slot was specified |
|
if [ -n "$SLOT" ]; then |
|
# Stream logs for the specified deployment slot |
|
az webapp log tail --name "$APP_NAME" --resource-group "$RESOURCE_GROUP" --slot "$SLOT" 2>&1 | log_and_check_size |
|
else |
|
# Stream logs for the production slot |
|
az webapp log tail --name "$APP_NAME" --resource-group "$RESOURCE_GROUP" 2>&1 | log_and_check_size |
|
fi |
|
|
|
# Brief pause before reconnecting |
|
# Why: Prevent rapid connection attempts if there's a persistent issue |
|
sleep 3 |
|
done |