Take the 2-minute tour ×
Server Fault is a question and answer site for system and network administrators. It's 100% free, no registration required.

I am trying to add rudimentary logging to a script which will be run daily by a cron job. Basically it goes along these lines:

LOGFILE=mylog.txt
exec 3<>$LOGFILE
2>&1
commands
...
exec 3>&-

Basically all I need to know is how to write the third line correctly so that STDERR and STDOUT both get written to FD 3, which is the log file. I've been fumbling around with it and sadly TLDP's explanation isn't the greatest - and most of the other tutorials on redirection and file descriptors don't go into this kind of depth. Any help would really be appreciated.

share|improve this question

2 Answers 2

up vote 1 down vote accepted

exec >&3 2>&3

And SF requires me to type some more characters, but that's all there is to it.

share|improve this answer
    
Works like a charm, thank you! I suck with redirects :) Much appreciated!!! –  Matthew Dec 2 '11 at 20:27

I am using this for simple logging:

exec > >(tee -a $LOGFILE)
exec 2> >(tee -a >(sed 's/^/STDERR: /' >>$LOGFILE) 1>&2)

For complex stuff I have a function in log-function.sh:

log() { #{{{
    local DAYS_TO_KEEP_THE_LOG=30
    local LOG_DATE_FORMAT="+%Y-%m-%d %H:%M:%S"
    local FILESUFIX_DATE_FORMAT="+%Y-%m-%d_%H%M%S"
    case $1 in
        --disable-output-redirection)
            OUTPUT_NOT_LOGGED=1
            log Output redirection is disabled. Only explicit \"log\" calls will be logged.
            return
            ;;
        --enable-timestamps)
            if [[ $OUTPUT_NOT_LOGGED == 1 ]];then
                log The option --enable-timestamps is ignored if --disable-output-redirection is used.
            fi
            TIMESTAMP_ON_OUTPUT=1
            log Time stamps enabled for output.
            return
            ;;
        --enable-syslog)
            ENABLE_SYSLOG=1
            return
            ;;
        --log-file)
            if [ $# -eq 2 ];then
                local MY_LOG_FILE_TEMP=$2
                local MY_LOG_DIR_TEMP=$(dirname $MY_LOG_FILE_TEMP)
                if [ ! -d $MY_LOG_DIR_TEMP ];then
                    log Folder $MY_LOG_DIR_TEMP does not exist. It will be created.
                    log "$(mkdir -p $MY_LOG_DIR_TEMP)"
                fi
                log Current log file is: $MY_LOG_FILE_TEMP
                log Rotating the logs
                # We make sure that the current log file is not deleted. We change the mtime to be current time.
                log "$(touch $MY_LOG_FILE_TEMP)"
                # Find all log files that have a date appended, older than 14 days and remove them.
                #log "$(find $(dirname $MY_LOG_FILE_TEMP) -name $(basename $MY_LOG_FILE_TEMP)-\* -mtime +$DAYS_TO_KEEP_THE_LOG -print0|xargs -0r rm -v)"
                log "$(find $(dirname $MY_LOG_FILE_TEMP) -name $(basename $MY_LOG_FILE_TEMP)-\* -mtime +$DAYS_TO_KEEP_THE_LOG|xargs rm 2>/dev/null)"
                # rename the log file and append the current date and time to the filename.
                #log "$(mv -v  $MY_LOG_FILE_TEMP $MY_LOG_FILE_TEMP-$(date "$FILESUFIX_DATE_FORMAT"))"
                log "$(echo $MY_LOG_FILE_TEMP '->' $MY_LOG_FILE_TEMP-$(date "$FILESUFIX_DATE_FORMAT");mv $MY_LOG_FILE_TEMP $MY_LOG_FILE_TEMP-$(date "$FILESUFIX_DATE_FORMAT"))"

                # Dump the log memory buffer into the log file.
                if [ "x$MY_LOG" != "x" ];then
                    echo -e "$MY_LOG" >>$MY_LOG_FILE_TEMP
                    unset MY_LOG
                fi
                if [[ $MY_LOG_FILE_TEMP =~ '^[^/]' ]]; then
                    log Full path of the log is $PWD/$MY_LOG_FILE_TEMP
                fi
                MY_LOG_FILE=$MY_LOG_FILE_TEMP
                if [[ $OUTPUT_NOT_LOGGED != 1 ]];then
                    if [[ $TIMESTAMP_ON_OUTPUT == 1 ]];then
                        exec > >(while read LINE; do echo $(date "$LOG_DATE_FORMAT") ">" $LINE; done|tee -a $MY_LOG_FILE) 2>&1
                    else
                        exec > >(tee -a $MY_LOG_FILE)
                        exec 2> >(tee -a >(sed 's/^/ERR: /' >>$MY_LOG_FILE) 1>&2)
                    fi
                fi
                return
            else
                log "Invalid number of paramaters for the log function"
            fi
            ;;
        --help|--*)
            echo 'This function allows to log inside a script. It can be used to log the entire output to a log file.'
            echo 'Examples:'
            echo '       log "Message to log"'
            echo '       log --log-file FILENAME.LOG'
            echo '       log --enable-timestamps;log --log-file FILENAME.LOG'
            echo '       log --disable-output-redirection;log --log-file FILENAME.LOG'
            echo '       log --enable-syslog'
            return
            ;;
        "")     # If the message is empty, then do not log anything.
            return
            ;;
    esac

    # We log to the console to STDERR then to syslog.
    if [[ $ENABLE_SYSLOG == 1 ]];then
        logger -p local1.notice -t $(basename $0) -i -- "$@"
    fi

    if [ "x$MY_LOG_FILE" = "x" ]; then
        echo $(date "$LOG_DATE_FORMAT") "$@" >&2
        # If we do not have a log file configured, we will buffer the log in memory.
        if [ "x$MY_LOG" = "x" ];then
            MY_LOG="$(date "$LOG_DATE_FORMAT") . $@"
        else
            MY_LOG="$MY_LOG\n$(date "$LOG_DATE_FORMAT") . $@"
        fi
    else
        if [[ $OUTPUT_NOT_LOGGED != 1 ]];then
            if [[ $TIMESTAMP_ON_OUTPUT == 1 ]];then
                echo "$@" >&2
            else
                echo $(date "$LOG_DATE_FORMAT") "$@" >&2
            fi
        else
            echo $(date "$LOG_DATE_FORMAT") "$@" >&2
            echo $(date "$LOG_DATE_FORMAT") "$@" >> $MY_LOG_FILE
        fi
    fi
} #}}}
#vim:ts=4:sw=4:et:fdm=marker:

Here is an usage of it:

SCRIPT_HOME=$(cd $(dirname $0);pwd -P)
. $SCRIPT_HOME/log-function.sh
log --enable-syslog
log --log-file $SCRIPT_HOME/logs/$(basename $0).log
log Starting $0 script with params=$@ on $(hostname) host as $USER user.
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.