Script Valley
Bash Scripting for Developers
Functions and Script ArchitectureLesson 3.4

How to write a Bash logging library

log levels, color output with tput, stderr vs stdout logging, log to file and terminal simultaneously, timestamps in logs, log verbosity flag, disabling color in CI

Every Script Needs Structured Logging

Bash logging function flow

Ad-hoc echo statements don't scale. A small logging library pays for itself on the first production incident.

#!/usr/bin/env bash
# lib/logging.sh

LOG_LEVEL="${LOG_LEVEL:-INFO}"  # override via env var
LOG_FILE="${LOG_FILE:-/dev/null}"

# Use tput for colors; fall back gracefully in CI
if [[ -t 2 ]] && tput colors &>/dev/null; then
  RED=$(tput setaf 1)
  YELLOW=$(tput setaf 3)
  GREEN=$(tput setaf 2)
  RESET=$(tput sgr0)
else
  RED="" YELLOW="" GREEN="" RESET=""
fi

log() {
  local level="$1"
  shift
  local message="$*"
  local ts
  ts=$(date "+%Y-%m-%d %H:%M:%S")

  local color=""
  case "$level" in
    ERROR) color="$RED" ;;
    WARN)  color="$YELLOW" ;;
    INFO)  color="$GREEN" ;;
  esac

  # Always write plain text to log file
  echo "[$ts] [$level] $message" >> "$LOG_FILE"
  # Write colored output to stderr
  echo -e "${color}[$ts] [$level]${RESET} $message" >&2
}

log_info()  { log INFO "$@"; }
log_warn()  { log WARN "$@"; }
log_error() { log ERROR "$@"; }

Check [[ -t 2 ]] (is stderr a terminal?) before emitting colors. CI systems like GitHub Actions set TERM=dumb or pipe stderr — colors become garbled escape codes in logs.

Up next

Bash argument parsing with getopts

Sign in to track progress