How can I build a Bash logger with timestamps, colors, and printf-style arguments?

0
3
Asked By MellowCedar42 On

I want to write Bash logging helpers that add a timestamp and a colored severity label while sending diagnostic output to stderr. Standard output should remain available for functions that return values. Ideally, the helpers should support normal printf-style format strings and arguments, for example `log_info "What a %s %d" "day" 100` and `log_error "Error: %s %o" "something went wrong" "{1: "enter"}"`. What would a clean implementation of the underlying `log` function look like, and how should terminal color support be handled?

3 Answers

Answered By CopperLynx53 On

You can map severity levels to ANSI escape sequences and print the timestamp and level in color while leaving the actual message formatting to `printf`. For example: `declare -A colors=([INFO]=$'e[0;34m' [WARNING]=$'e[1;33m' [ERROR]=$'e[0;31m'); reset=$'e[0m'; log(){ local level=$1; shift; printf '%s%(%FT%T%z)T%s [%s] ' "${colors[$level]}" -1 "$reset" "$level" >&2; printf "$@" >&2; printf 'n' >&2; }; log_info(){ log INFO "$@"; }; log_warn(){ log WARNING "$@"; }; log_error(){ log ERROR "$@"; }`. ANSI colors are common in terminal emulators, but they should usually be disabled when stderr is redirected to a file or when the environment does not support color. You can add a `NO_COLOR` check and test `[[ -t 2 ]]` before emitting escape sequences.

SilverMaple26 -

Color escape sequences are not universal across every shell or output destination. They are terminal control codes, so checking whether file descriptor 2 is a terminal and providing a way to disable colors makes the logger safer.

Answered By OrbitingPine7 On

With Bash 4.2 or newer, `printf` can format the current time directly. A simple version is `printf '%(%d-%m-%Y-%T:Z)T %sn' -1 "Some information"`. The `-1` argument means the current time. For printf-style log messages, keep the format string and its arguments together and pass them through with `printf` rather than joining them into one string: `log(){ local level=$1; shift; printf '%(%FT%T%z)T [%s] ' -1 "$level" >&2; printf "$@" >&2; printf 'n' >&2; }`. Then `log_info(){ log INFO "$@"; }`, and the call `log_info 'What a %s %d' day 100` works as expected.

QuietHarbor18 -

That is the important distinction: the first remaining argument is the format string, and the rest are its substitutions. Passing `"$@"` preserves that structure, whereas using `"$*"` turns everything into one combined string and loses printf's argument handling.

Answered By NorthwindPebble9 On

For log files and tools that parse logs, an ISO 8601-style timestamp such as `%Y-%m-%dT%H:%M:%S%z` is generally easier to sort and interpret than day-month-year formatting. Also, keep the timestamp and level on stderr, but avoid adding extra formatting that could interfere with the caller's printf string.

Related Questions

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.