I have a long (~2,000 lines) script that I'm trying to log for future debugging. Right now I have:
function log_with_time()
{
while read a; do
echo `date +'%H:%M:%S.%4N '` " $a" >> $LOGFILE
done
}
exec 7> >(log_with_time)
BASH_XTRACEFD=7
PS4=' exit($?)ln:$LINENO: '
set -x
echo "helloWorld 1"
which gives me very nice logging for any and all commands that are run:
15:18:03.6359 exit(0)ln:28: echo 'helloWorld 1'
The issue that I'm running into is that xtrace seems to be asynchronous. With longer scripts, the log times fall behind the actual time the commands are called, and the exit code doesn't match the logged command.
There has to be a better way to do this but I'd be happy if I could just synchronize xtrace.
...
tldr: How can I generally log the time, command and exit code for all commands in a script?
...
(First time posting, feedback appreciated)
UPDATE:
exec {BASH_XTRACEFD}>>$LOGFILE
PS4=' time:$(date +%H:%M:%S.%4N) ln:$LINENO: '
set -x
fail()
{
echo "fail" >> $LOGFILE
return 1
}
trap 'echo exit:$? >> $LOGFILE' DEBUG
fail
solves all of my synchronization issues. exit codes and timestamps are working beautifully. My only issue now is one of formatting: the trap itself is getting reported by xtrace.
time:18:30:07.6080 ln:27: fail
time:18:30:07.6089 ln:12: echo fail
fail
time:18:30:07.6126 ln:13: return 1
time:18:30:07.6134 ln:28: echo exit:1
exit:1
I've tried setting +x in the trap but then set +x gets logged. If I could find a way to omit one line from xtrace, this log would be perfect.
>(...)is on the other side of a FIFO, running in its own process. The actual write toBASH_XTRACEFDis itself fully synchronous.PS4that inserts the date itself.function funcname() {is a mix of POSIX sh and ancient-ksh declaration conventions (the former is justfuncname() {, the second isfunction funcname {), and compatible with neither. It's best practice to pick one or the other -- I strongly suggest the POSIX form, as bash's ksh-esque function declaration syntax doesn't actually have the same semantics that format had in ksh (where it made variables defined in functions local-by-default).printf %(%H:%M:%S)T -1to emit the current time without all the overhead of forking off a subshell and then using anexec-family syscall to replace that subshell with a copy of/usr/bin/date(not to mention setting up a pair of FIFOs to read that subshell's output, &c).