six demon bag
Wind, fire, all that kind of thing!
2020-02-24
Shell Patterns (1) - Logging
This is a short series describing some Bash constructs that I frequently use in my scripts.
What do you do when you run fully automated scripts in the background, but still want to keep track of what they're doing and, more importantly, when something goes wrong? The answer is, of course, you log what the script is doing (or is about to do).
There are two commonly used ways of implementing logging in Bash scripts:
- redirecting output to files
- invoking the
logger
command for logging to syslog
Personally, I prefer the latter, since it allows not only for managing log files independently of the process creating the log output, but also for filtering log data and/or forwarding it to a central loghost.
Both approaches can be used either inside the script for logging specific messages:
some_command >>/var/log/myoutput.log 2>>/var/log/myerror.log
some_command 2>&1 >>/var/log/mycombined.log
echo 'my message' >>/var/log/myoutput.log
some_command 2>&1 | logger -t 'mytag' -p 'local0.info'
logger -t 'mytag' -p 'local0.info' 'my message'
or outside the script for capturing the entire script output:
./myscript.sh >/var/log/myoutput.log 2>/var/log/myerror.log
./myscript.sh 2>&1 | logger -t 'mytag' -p 'local0.info'
There are downsides to either approach, though. Putting the logging statements inside the script tends to clutter the code, and may also miss some output you actually wanted logged. Putting the logging statements outside the script means that the script is no longer self-contained.
So what can you do to capture all script output without relying on something outside the script? For the output redirection approach you can use the exec
command to have all output written to files:
exec >/var/log/myoutput.log 2>/var/log/myerror.log
# rest of your code comes after this
exec 2>&1 >/var/log/mycombined.log
# rest of your code comes after this
For the logger
approach the I/O redirection can be combined with another useful Bash feature called process substitution, which allows redirecting the output of one or more processes into another process:
exec > >(logger -t 'mytag' -p 'local0.info') 2> >(logger -t 'mytag' -p 'local0.err')
# rest of your code comes after this
Usually I define the script name as the log tag, and also throw in the Bash process ID for good measure:
tag="$(basename "$0")"
exec > >(logger --id="$$" -t "$tag" -p 'local0.info') 2> >(logger --id="$$" -t "$tag" -p 'local0.err')
Posted 20:47 [permalink]