2 # Copyright (C) 2019 Ian Kelling
3 # SPDX-License-Identifier: AGPL-3.0-or-later
9 local cbase c log line i out
file o tmp mvout mverr
10 cbase
=/var
/local
/cron-errors
11 [[ $EUID == 0 ]] || cbase
=$HOME/cron-errors
12 local help="Usage: some-command-that-outputs-on-error |& log-once [OPTION]... LOG_NAME
14 Main use case: in cronjobs where STDIN represents an error,
15 but we only want to output that to STDOUT if we've seen this type of
16 error ERRORS(default 3) number of times in a row, then we don't
17 want to output anything again until we've seen a success: no standard input.
19 Logs STDIN to /var/local/cron-errors/LOG_NAME\$error_count or
20 $HOME/cron-errors if not root, and keeps state in the same directory.
22 -ERRORS: ERRORS is the number of errors to accumulate before outputing the error
25 You can emulate how cronjobs work by doing this for example:
27 cmdname |& log-once | ifne mail -s 'cmdname failed' root@localhost
29 I could imagine a similar command that considers its non-option args to
30 be an error, but I haven't written it yet.
35 if [[ $1 == --help ]]; then
38 elif [[ $1 == -[0-9]* ]]; then
41 elif [[ $1 == -- ]]; then
49 # todo, make option & make them overridable based on command line or env variable
50 [[ -d $cbase ]] || mkdir
-p $cbase
53 while read -r line
; do
55 # If we find something that is not just a newline:
62 # file is error file indicating previous error
63 tmp
=($glob); file="${tmp[0]}"
64 if [[ $file == "$glob" ]]; then
71 if (( i
< errors
)); then
73 mvout
=$
(mv $file $new_file 2>&1) || mverr
=$?
75 if [[ $mvout == *": No such file or directory" ]]; then
76 # We've very likely hit a race condition, where another
77 # log-once did the mv before us.
80 echo "log-once: error on mv $file $new_file"
85 if [[ $file == $c$errors ]]; then
91 if (( errors
== 1 )); then
95 $out $file <<<"log-once: $(date -R)"
99 for o in "${output[@]}"; do
108 if [[ $file == $c$errors ]]; then
109 echo "log-once: $(date -R): success after failure for $c"