Remove as much trailing whitespace as possible.
[asterisk/asterisk.git] / contrib / scripts / ast_loggrabber
1 #!/usr/bin/env bash
2 # Turn on extended globbing
3 shopt -s extglob
4 # Bail on any error
5 set -e
6
7 prog=$(basename $0)
8
9 print_help() {
10 cat <<EOF
11 NAME
12 $prog - Gather asterisk log files
13
14 SYNOPSIS
15         $prog [ --help ] [ --dateformat="<dateformat>" ]
16                 [ --timezone="<timezone>" ] [ --append-logfiles ]
17                 [ --tarball-uniqueid="<uniqueid>" ]
18                 [ <logfiles> | <pattern> ... ]
19
20 DESCRIPTION
21
22         Gathers log files, optionally converts POSIX timestamps
23         to readable format. and creates a tarball.
24
25         Options:
26
27         --help
28                 Print this help.
29
30         --dateformat="<dateformat>"
31                 A Python strftime format string to be used when converting
32                 POSIX timestamps in log files to readable format.  If not
33                 specified as an argument or in the config file, no conversion
34                 is done.
35
36         --timezone="<timezone>"
37                 The timezone to use when converting POSIX timestamps to
38                 readable format.  It can be specified in "<continent>/<city>"
39                 format or in abbreviation format such as "CST6CDT".  If not
40                 specified as an argument or in the config file, the "local"
41                 timezone is used.
42
43         --append-logfiles
44                 Append any log files specified on the command line to the
45                 config file specified ones instead of overriding them.
46
47         --tarball-uniqueid="<uniqueid>"
48                 Normally DATEFORMAT is used to make the tarballs unique
49                 but you can use your own unique id in the tarball names
50                 such as a Jira issue id.
51
52         <logfiles> | <pattern>
53                 A list of log files or log file search patterns.  Unless
54                 --append-logfiles was specified, these entries will override
55                 those specified in the config files.
56
57                 If no files are specified on the command line the, value of
58                 LOGFILES from ast_debug_tools.conf will be used.  Failing
59                 that, the following patterns will be used:
60                 /var/log/asterisk/messages*
61                 /var/log/asterisk/queue*
62                 /var/log/asterisk/debug*
63                 /var/log/asterisk/security*
64
65 NOTES
66         Any files output will have ':' characters changed to '-'.  This is
67         to facilitate uploading those files to Jira which doesn't like the
68         colons.
69
70 FILES
71         /etc/asterisk/ast_debug_tools.conf
72         ~/ast_debug_tools.conf
73         ./ast_debug_tools.conf
74
75         # Readable Local time for the tarball names
76         DATEFORMAT='date +%FT%H-%M-%S%z'
77
78         # A list of log files and/or log file search patterns using the
79         # same syntax as COREDUMPS.
80         #
81         LOGFILES=(/var/log/asterisk/messages* /var/log/asterisk/queue* \\
82                 /var/log/asterisk/debug* /var/log/asterisk/security*)
83
84         # $prog converts POSIX timestamps to readable format
85         # using this Python strftime format string.  If not specified
86         # or an empty string, no format covnersion is done.
87         LOG_DATEFORMAT="%m/%d %H:%M:%S.%f"
88
89         # The timezone to use when converting POSIX timestamps to
90         # readable format.  It can be specified in "<continent>/<city>"
91         # format or in abbreviation format such as "CST6CDT".  If not
92         # specified, the "local" timezone is used.
93         # LOG_TIMEZONE=
94
95 EOF
96         exit 1
97 }
98
99 append_logfiles=false
100
101 declare -a LOGFILES
102 declare -a ARGS_LOGFILES
103
104 # Read config files from least important to most important
105 [ -f /etc/asterisk/ast_debug_tools.conf ] && source /etc/asterisk/ast_debug_tools.conf
106 [ -f ~/ast_debug_tools.conf ] && source ~/ast_debug_tools.conf
107 [ -f ./ast_debug_tools.conf ] && source ./ast_debug_tools.conf
108
109 if [ ${#LOGFILES[@]} -eq 0 ] ; then
110         LOGFILES+=(/var/log/asterisk/messages* /var/log/asterisk/queue* \
111         /var/log/asterisk/debug* /var/log/asterisk/security*)
112 fi
113
114 DATEFORMAT=${DATEFORMAT:-'date +%FT%H-%M-%S%z'}
115
116 # Use "$@" (with the quotes) so spaces in patterns or
117 # file names are preserved.
118 # Later on when we have to iterate over LOGFILES, we always
119 # use the indexes rather than trying to expand the values of LOGFILES
120 # just in case.
121
122 for a in "$@" ; do
123         case "$a" in
124         --dateformat=*)
125                 LOG_DATEFORMAT=${a#*=}
126                 ;;
127         --timezone=*)
128                 LOG_TIMEZONE=${a#*=}
129                 ;;
130         --append-logfiles)
131                 append_logfiles=true
132                 ;;
133         --tarball-uniqueid=*)
134                 tarball_uniqueid=${a#*=}
135                 ;;
136         --help|-*)
137                 print_help
138                 ;;
139         *)
140                 ARGS_LOGFILES+=("$a")
141                 # If any files are specified on the command line, ignore those
142                 # specified in the config files unless append-logfiles was specified.
143                 if ! $append_logfiles ; then
144                         LOGFILES=()
145                 fi
146         esac
147 done
148
149 # append logfiles/patterns specified as command line arguments to LOGFILES.
150 for i in ${!ARGS_LOGFILES[@]} ; do
151         LOGFILES+=("${ARGS_LOGFILES[$i]}")
152 done
153
154 # At this point, all glob entries that match files should be expanded.
155 # Any entries that don't exist are probably globs that didn't match anything
156 # and need to be pruned.
157
158 for i in ${!LOGFILES[@]} ; do
159         if [ ! -f "${LOGFILES[$i]}" ] ; then
160                 unset LOGFILES[$i]
161                 continue
162         fi
163 done
164
165 # Sort and weed out any dups
166 IFS=$'\x0a'
167 readarray -t LOGFILES < <(echo -n "${LOGFILES[*]}" | sort -u )
168 unset IFS
169
170 if [ "${#LOGFILES[@]}" -eq 0 ] ; then
171         echo "No log files found"
172         print_help
173 fi
174
175 # Timestamp to use for output files
176 df=${tarball_uniqueid:-$(${DATEFORMAT})}
177
178 # Extract the Python timestamp conver script from the end of this
179 # script and save it to /tmp/.ast_tsconvert.py
180
181 ss=`egrep -n "^#@@@SCRIPTSTART@@@" $0 |cut -f1 -d:`
182 tail -n +${ss} $0 >/tmp/.ast_tsconvert.py
183
184 tmpdir=$(mktemp -d)
185 if [ -z "$tmpdir" ] ; then
186         echo "${prog}: Unable to create temporary directory."
187         exit 1
188 fi
189 trap "rm -rf $tmpdir" EXIT
190 tardir=asterisk-${df}.logfiles
191
192 # Now iterate over the logfiles
193 for i in ${!LOGFILES[@]} ; do
194         lf=${LOGFILES[$i]}
195         destdir="$tmpdir/$tardir/$(dirname $lf)"
196         destfile="$tmpdir/$tardir/$lf"
197         mkdir -p "$destdir" 2>/dev/null || :
198         if [ -n "$LOG_DATEFORMAT" ] ; then
199                 echo "Converting $lf"
200                 cat "$lf" | python /tmp/.ast_tsconvert.py --format="$LOG_DATEFORMAT" --timezone="$LOG_TIMEZONE" > "${destfile}"
201         else
202                 echo "Copying $lf"
203                 cp "$lf" "${destfile}"
204         fi
205 done
206
207 echo "Creating /tmp/$tardir.tar.gz"
208 tar -czvf /tmp/$tardir.tar.gz -C $tmpdir $tardir 2>/dev/null
209
210 exit
211
212 # Be careful editng the inline scripts.
213 # They're space-indented.
214
215 # We need the python bit because lock_infos isn't
216 # a valid symbol in asterisk unless DEBUG_THREADS was
217 # used during the compile.  Also, interrupt and continue
218 # are only valid for a running program.
219
220 #@@@SCRIPTSTART@@@
221 import argparse
222 import datetime as dt
223 import dateutil.tz as tz
224 import re
225 import sys
226 import time
227
228 parser = argparse.ArgumentParser(description="Make POSIX timestamps readable")
229 parser.add_argument('--format', action='store', required=True)
230 parser.add_argument('--timezone', action='store', required=False)
231 args=parser.parse_args()
232
233 # We only convert timestamps that are at the beginning of a line
234 # or are preceeded by a whilespace character or a '['
235 rets = re.compile(r'(^|(?<=\s|\[))\d+(\.\d+)?', flags=re.M)
236 if args.timezone and len(args.timezone) > 0:
237    tzf = tz.tzfile('/usr/share/zoneinfo/' + args.timezone)
238 else:
239    tzf = tz.tzfile('/etc/localtime')
240
241 now = time.time()
242 a_year_ago = now - (86400.0 * 365)
243
244 def convert(match):
245    ts = float(match.group(0))
246    if ts <= now and ts > a_year_ago and len(args.format) > 0:
247       return dt.datetime.fromtimestamp(ts, tzf).strftime(args.format)
248    else:
249       return match.group(0)
250
251 while 1:
252    line = sys.stdin.readline()
253    if not line:
254       break
255    print(rets.sub(convert, line))