#!/bin/sh # Example of duplicating STDOUT+STDERR into a log file # Copyright (C) 2016 Assaf Gordon # License: MIT # # This script demonstate how to setup logging of all STDOUT and STDERR # outputs into log files (in addition to also outputing it to the # original STDOUT/STDERR (e.g. the terminal). # # The function 'setup_logging2' creates two separate log files, one for # stderr and one for stdout. This makes it easy to detect errors # (if there's lots of non-error noise on STDOUT), but it might make it # hard to know when/where the error was printed during the program's life time. # # NOTE: # when STDERR and STDOUT are different streams, STDERR is usually not buffered # and printed quickly, while STDOUT is buffered and printed slowly, thus the # order of the messages might seem 'incorrect'. # # See 'log-to-file1.sh' for an alternative (saving STDOUT and STDERR # into one merged file). # die() { BASE=$(basename "$0") echo "$BASE: error: $@" >&2 exit 1 } setup_logging2() { logprefix="$1" test -z "$logprefix" \ && die "internal error: missing log filename parameter" # Create a tmp directory, to store the fifo __pipedir=$(mktemp -d -t log.XXXXXX) \ || die "failed to create tmp directory" # Create the fifo file mkfifo "${__pipedir}/log-fifo-out" \ || die "failed to create fifo" mkfifo "${__pipedir}/log-fifo-err" \ || die "failed to create fifo" # Run 'tee' in the background, duplicating # the input (from the fifo) to both STDOUT and the log file tee "${logprefix}.stdout.log" < "${__pipedir}/log-fifo-out" & # duplicate the input to both file and STDERR tee "${logprefix}.stderr.log" < "${__pipedir}/log-fifo-err" >&2 & # Re-exec this shell instance, merging STDERR into STDOUT # and redirecting STDOUT into the fifo exec >"${__pipedir}/log-fifo-out" 2>"${__pipedir}/log-fifo-err" # Delete the FIFO and the temp dir - # no harm since the the fifo's file-descriptior is # held open by 'tee'. Saves the trouble of cleanup on exit. rm "${__pipedir}/log-fifo-out" "${__pipedir}/log-fifo-err"\ || die "failed to cleanup fifo files" rmdir "${__pipedir}" \ || die "failed to cleanup tmp dir" } # Setup logging into a file with a time stamp log=program-$(date +%F-%H%M%S) setup_logging2 "$log" # Simulate some output both to STDOUT and STDERR echo "Logging Example - anything you see printed to the terminal" echo " is also saved in log files:" echo " stdout: $log.stdout.log" echo " stderr: $log.stderr.log" echo "Hello (Stdout)" echo "Hello (stderr)" >&2 # Simulate potential error messages to stdout/err. # NOTE: # stdout (the 'echo') will be saved in one file, # while the error (from 'cp') will be saved in another. # this makes it easy to spot errors, but might make it harder # to understand the context (unless progress messages are also printed # to STDERR). echo "Trying to copy non-existing file (which will fail):" cp "/foo/bar" "/bar/baz"