Steph
Steph

Reputation: 91

Cron job stderr to email AND log file?

I have a cron job:

$SP_s/StartDailyS1.sh >$LP_s/MirrorLogS1.txt

Where SP_s is the path to the script and LP_s is the path for the log file. This sends stdout to the log file and stderr to my email.

How do I?:
1) send both stdout AND stderr to the logfile,
2) AND send stderr to email

or to put it another way: stderr to both the logfile and the email, and stdout only to the logfile.

UPDATE: None of the answers I've gotten so far either follow the criteria I set out or seem suited to a CRON job.

I saw this, which is intended to "send the STDOUT and STDERR from a command to one file, and then just STDERR to another file" (posted by zazzybob on unix.com), which seems close to what I want to do and I was wondering if it would inspire someone more clever than I:

(( my_command 3>&1 1>&2 2>&3 ) | tee error_only.log ) > all.log 2>&1 

I want cron to send STDERR to email rather than 'another file'.

Upvotes: 5

Views: 15196

Answers (9)

Sylvain Lesage
Sylvain Lesage

Reputation: 961

I think the solution would be:

$SP_s/StartDailyS1.sh 2>&1 >> $LP_s/MirrorLogS1.txt | tee -a $LP_s/MirrorLogS1.txt

This will:

  • append standard output to $LP_s/MirrorLogS1.txt
  • append standard error to $LP_s/MirrorLogS1.txt
  • print standard error, so that cron will send a mail in case of error

Upvotes: 0

Steph
Steph

Reputation: 91

My experience (ubuntu) is that 'crontab' only emails 'stderr' (I have the output directed to a log file which is then archived). That is useful, but I wanted a confirmation that the script ran (even when no errors to 'stderr'), and some details about how long it took, which I find is a good way to spot potential trouble.

I found the way I could most easily wrap my head around this problem was to write the script with some duplicate 'echo's in it. The extensive regular 'echo's end up in the log file. For the important non-error bits I want in my crontab 'MAILTO' email, I used an 'echo' that is directed to stderr with '1>&2'.

Thus this:

    Frmt_s="+>>%y%m%d %H%M%S($HOSTNAME): " # =Format of timestamp: "<YYMMDD HHMMSS>(<machine name>): "
    echo `date "$Frmt_s"`"'$0' started." # '$0' is path & filename
    echo `date "$Frmt_s"`"'$0' started."  1>&2 # message to stderr

# REPORT:
    echo ""
    echo "================================================"
    echo "================================================" 1>&2 # message to stderr
    TotalMins_i=$(( TotalSecs_i / 60 )) # calculate elapsed mins
    RemainderSecs_i=$(( TotalSecs_i-(TotalMins_i*60) ))
    Title_s="TOTAL run time"
    Summary_s=$Summary_s$'\n'$(printf "%-20s%3s:%02d" "$Title_s" $TotalMins_i $RemainderSecs_i)
    echo "$Summary_s"
    echo "$Summary_s" 1>&2 # message to stderr
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" 1>&2 # message to stderr
    echo ""
    echo `date "$Frmt_s"`"TotalSecs_i: $TotalSecs_i"
    echo `date "$Frmt_s"`"'$0' concluded." # '$0' is path & filename
    echo `date "$Frmt_s"`"'$0' concluded."  1>&2 # message to stderr

Sends me an email containing this (when there are no errors, the lines beginning 'ssh:' and 'rsync:' do not appear):

170408 030001(sb03): '/mnt/data1/LoSR/backup_losr_to_elwd.sh' started.
ssh: connect to host 000.000.000.000 port 0000: Connection timed out
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: unexplained error (code 255) at io.c(226) [Receiver=3.1.0]
ssh: connect to host 000.000.000.000 port 0000: Connection timed out
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: unexplained error (code 255) at io.c(226) [Receiver=3.1.0]
================================================
S6 SUMMARY (mins:secs):
'Templates'           2:07
'Clients'             2:08
'Backups'             0:10
'Homes'               0:02
'NetAppsS6'          10:19
'TemplatesNew'        0:01
'S6Www'               0:02
'Aabak'               4:44
'Aaldf'               0:01
'ateam.ldf'           0:01
'Aa50Ini'             0:02
'Aadmin50Ini'         0:01
'GenerateTemplates'   0:01
'BackupScripts'       0:01
TOTAL run time       19:40
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
170408 031941(sb03): '/mnt/data1/LoSR/backup_losr_to_elwd.sh' concluded.

This doesn't satisfy my initial desire to "send both stdout AND stderr to the logfile" (stderr, and only the 'echo'ed lines with '1>&2' go to the email; stdout goes to the log), but I find this is better than my initially imagined solution, as the email finds me and I don't have to go looking for problems in the log file.

Upvotes: 0

sherbang
sherbang

Reputation: 16195

Since I was just looking at the info page for tee (trying to figure out how to do the same thing), I can answer the last bit of this for you.

This is most of the way there:

(( my_command 3>&1 1>&2 2>&3 ) | tee error_only.log ) > all.log 2>&1

but replace "error_only.log" with ">(email_command)"

(( my_command 3>&1 1>&2 2>&3 ) | tee >(/bin/mail -s "SUBJECT" "EMAIL") ) > all.log 2>&1

Note: according to tee's docs this will work in bash, but not in /bin/sh. If you're putting this in a cron script (like in /etc/cron.daily/) then you can just but #!/bin/bash at the top. However if you're putting it as a one-liner in a crontab then you may need to wrap it in bash -c ""

Upvotes: 3

&#193;lex
&#193;lex

Reputation: 1703

Unless I'm missing something:

command 2>&1 >> file.log | tee -a file.log

2>&1 redirects stderr to stdout

>> redirects regular command stdout to logfile

| tee duplicates stderr (from 2>&1) to logfile and passes it through to stdout be mailed by cron to MAILTO

I tested it with

(echo Hello & echo 1>&2 World) 2>&1 >> x | tee -a x

Which indeed shows World in the console and both texts within x

The ugly thing is the duplicate file name. And the different buffering from stdout/stderr might make text in file.log a bit messy I guess.

Upvotes: 2

codemann8
codemann8

Reputation: 390

You could try writing another cronjob to read the log file and "display" the log (really just let cron email it to you)

Upvotes: -1

basher
basher

Reputation: 31

Not sure why nobody mentioned this.

With CRON if you specify MAILTO= in the users crontab, STDOUT is already sent via mail.

Example

[temp]$ sudo crontab -u user1 -l
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=user1
# transfer order shipping file every 3 minutes past the quarter hour
3,19,33,48 * * * * /home/user1/.bin/trans.sh

Upvotes: 3

pilcrow
pilcrow

Reputation: 58524

A bit tricky if you want stdout and stderr combined in one file, with stderr yet tee'd into its own stream.

This ought to do it (error-checking, clean-up and generalized robustness omitted):

#! /bin/sh

CMD=..../StartDailyS1.sh
LOGFILE=..../MirrorLogS1.txt
FIFO=/tmp/fifo

>$LOGFILE

mkfifo $FIFO 2>/dev/null || :

tee < $FIFO -a $LOGFILE >&2 &

$CMD 2>$FIFO >>$LOGFILE

stderr is sent to a named pipe, picked up by tee(1) where it is appended to the logfile (wherein is also appended your command's stdout) and tee'd back to regular stderr.

Upvotes: 0

nos
nos

Reputation: 229058

If you can do with having stdout/err in separate files, this should do:

($SP_s/StartDailyS1.sh 2>&1 >$LP_s/MirrorLogS1.txt.stdout) | tee $LP_s/MirrorLogS1.txt.stderr

Upvotes: 2

Alberto Zaccagni
Alberto Zaccagni

Reputation: 31560

I assume you are using bash, you redirect stdout and stderr like so

1> LOG_FILE
2> LOG_FILE

to send a mail containing the stderr in the body something like this

2> MESSAGE_FILE
/bin/mail -s "SUBJECT" "EMAIL_ADDRESS" < MESSAGE_FILE

I'm not sure if you can do the above in only one passage as this

/bin/mail -s "SUBJECT" "EMAIL_ADDRESS" <2

Upvotes: -2

Related Questions