log_analysis - Analyze various system logs log_analysis [-h] [-f config_file] [-o file] [-O] [-n nodename] [-U] [-u unknownsdir] [-d days_ago] [-a] [-m mail_address] [-M mail_prog] [-r] [-s] [-S] [required_files. . .] log_analysis analyzes and summarizes system logs files. It also runs some other commands (ie. w, df -k) to show the system state. It's intended to be run on a daily basis out of cron. Show all logs, not just the ones from yesterday. Show logs from days_ago days ago. Defaults to 1 (ie. show yesterday's logs.) In -a mode, this option only affects the heading, and it defaults to 0. You can also provide an absolute date in the form YYYY_MM_DD, ie. 2001_03_02. And you can provide the symbolic names today (equivalent to 0) and yesterday (equivalent to 1). And you can even provide a date range in the form YYYY_MM_DD-YYYY_MM_DD or ago1-ago2 to get output for a range of days. Each day is output individually, so if you use the -o option, you get a separate file for each day, and if you use the -m option, you get a separate mail for each day. Read config_file in addition to the internal config and the internal config files. See the section on "CONFIG FILE" for details. Show command summary and exit. This option is used for obtaining internal information about log_analysis. log_analysis exits immediately after outputting the information. If info is internal_config, the internal config is output. If info is evals, the evals built from the config (internal and local) are output. If info is categories, all categories (those mentioned in the various configs and implicit categories) will be listed. If info is config_versions, all config files will be listed with their config_version (if defined) and file_version (if defined). If info is log_files, the log files that would have been read are output. If info is log_types, the log types are output. If info is patterns, the various patterns defined for the log types are output. If info is nothing, log_analysis just exits. Useful for testing configs. If info is help, log_analysis outputs the list of things you can use for info. Mail output to mail_address. This can also be specified in the config; see mail_address in the section on "VARIABLES". Use mail_command to send the mail. This can also be specified in the config; see mail_command in the section on "VARIABLES" for more info, including the default. Use nodename as the nodename (AKA hostname) instead of the output of uname -n. This is more than just cosmetic: entries in syslogged files will be processed differently if they didn't come from this nodename. This can also be specified in the config file; see nodename in the section on "VARIABLES". If the logs contain entries for nodes other than nodename, (ie. if the host is a syslog server), analyze them anyway. Output to file instead of to standard output. Works with -m, so you can save to a file and send mail with one same command. With -o file, causes the output to go both to the file and to standard output. NB: this does not currently work with -m, so you can't output to a file, standard output, and to email. Encrypts the mail output. Uses pgp_type to determine the encryption command. For use with -m or mail_address. See pgp_type in the list of global variables for info on encryption types. "Real mode", ie. monitor log files continuously. Currently conflicts with many other modes and options, and will not show you files that need decompression or an open_command. Yes, has built-in support for log file rollover. See variables real_mode, real_mode_output_format, real_mode_sleep_interval, and real_mode_check_interval in the list of global variables for more configurables. Also see optional_log_files for an issue specific to real mode. Usually, log_analysis runs assorted commands that show system state (ie. w, df -k). This option doesn't run those commands. See commands_to_run in the section on "VARIABLES" for the list of extra commands. Usually, log_analysis will include its version number, the time it spent running, and its arguments at the end of the output. This option suppresses that output. Output logfile unknowns to stdout and exit. If unknownsdir exists, also wipe unknownsdir if it exists and then write out raw unknown lines to files in unknownsdir. This exists to make writing custom rules easier. Use unknownsdir as the unknownsdir. If unknownsdir already exists, and contains files, its files will be used as the input for log_analysis regardless of any other command line options. If -U is also specified, after all processing unknownsdir will be wiped out and its files rewritten with the current unknowns. This is useful for writing your own configs. Output version and exit. If files are specified on the command line, log_analysis ignores its built-in list of optional and required log files, and process the files on the command line. If one of the files doesn't exist, it's a fatal error. The script has an embedded config file. It will also read various external config files if they exist; see the section on "FILES" for a list. Later directives (from later in the file or from a file read later) override earlier directives. You can make comments with '#' at the beginning of a line. If you want a '#' or '=' at the beginning of a line, you usually need to quote it with backslash. Some directives take a "block" as argument. A block is a collection of lines that ends with a line that is empty or only contains whitespace. '#' at the beginning of a line still comments out the line. Leading whitespace on a line is ignored. Before the config is parsed, it is passed through a preprocessor inspired by the aide(1) preprocessor.

Pattern directives
These directives describe your logs, and are the main point of this program. The basic idea here is that you first declare what logtype you are working with, and then you specify a bunch of perl patterns that describe different kinds of log messages, and that save parts of the message. For each perl pattern, you specify one or more destinations that describe what you want done with it. Future patterns should be applied to this logtype (ie. sulog, syslog, wtmp.) Example: logtype: syslog pattern is a perl regex (see perlre(1)) that implictly starts with ^ (beginning of the line) and implicitly ends with \s*$ (optional whitespace and the end of the line.) This should only be issued after a logtype: has been issued in the same config file. Wildcard parts of the pattern should be surrounded with parentheses, to save these parts for later use in the format:. Note that there are some tokens with special meanings that can be used here, currently $host_pat, $file_pat, $ip_pat, $user_pat, $mail_user_pat, $word_pat, and $zone_pat. Examples: pattern: popper: Stats: ($mail_user_pat) (\d+) (\d+) (\d+) (\d+) pattern: login: LOGIN ON ($file_pat) BY ($user_pat) The order of precedence for patterns is undefined, except that user-defined patterns always have precedence over the patterns of the internal config. format is treated as a string that contains the useful information from a pattern. Note that it should not actually be quoted. A format is mandatory for category destinations, but should not be used with SKIP or LAST destinations. For example, if we had a pattern that was login: LOGIN ON ($file_pat) BY ($user_pat), we would probably just want $2, so we might say: format: $2 Similarly, if we had a patterns that was kernel: deny (\d+) packets from ($ip_pat) to ($ip_pat), we might want to say: format: $2 => $3 use_sprintf is optional. If this directive is present for a given format, than instead of the format being treated as a string, it is treated as the arguments for sprintf(3). For example, if you have a source IP address in $2 and a destination IP address in $3, you could just have dest as $2 => $3, but you would have things lining up better if you did this: format: "%-15s => $3", $2 use_sprintf count is optional. The default is that a log line that matches a pattern causes the category to increment by 1. But sometimes, a single log line corresponds to multiple events, ie. if you have a log message of the form "5 packets denied by firewall" or "last message repeated 3 times", you can extract the event count to count. For example, if you're using the pattern kernel: deny (\d+) packets from ($ip_pat) to ($ip_pat), you might say: count: $1 This describes what you want done with the data in a pattern. If dest is the special token SKIP the data is discarded. If dest is the special token LAST, the data is assumed to be of the form "last message repeated N times", and we pretend as though the last message we saw occurred, using count as a multiplier. If dest starts with the special token UNIQUE, we do special "unique" handling, which is covered in the section on "UNIQUE DESTINATION". If dest starts with the special token CATEGORY or is any other string, it is treated as a category that the pattern data should be saved to. Ie. if pattern was login: LOGIN ON ($file_pat) BY ($user_pat), and format was $2, then one might set dest to login: successful local login. You must have a format defined before the dest. You can have multiple dest directives for a single pattern, if all of the dests are category destinations. Each one needs its own format. Similarly, if you set count or use_sprintf, they are tied to the particular dest you set them with. Note that dest "closes" the description of a destination, so you need to have any other related directives (ie. format, count, use_sprintf) before the dest directive. This ordering is necessary to avoid ambiguity in the multiple-destination case.

Category directives
Several patterns can lead to the same category, so category-specific directives are associated with the category, not with a pattern. Here are the category directives: Specifies which category subsequent directives will define. Specifies how this category should be sorted in the output. Examples are "funky", "string", "value", "reverse value", etc. The default is "funky". See the section on "SORTING" for more info. By default, log_analysis will output all the data it finds in a category. Filters let you specify, say, that only the top 10 items should be output, or that only the items that occurred fewer than 5 times should be output. If a category has data, but none of the data meet the filter rules, then the category will be completely skipped. See the section on "FILTERS" for more info. The usual way to populate categories is via the pattern config. But sometimes, you want to combine two or more elemental categories to make a new category. Any categories derived in this manner may not be a destination for simple patterns. There are currently three subcommands for this (the quotes are literal): These do what you expect: take the values for the items in category2 and add or subtract them from the values for the items in category1. Any item defined in either category will be in the new category. Subtract can cause the values in the new category to be negative or 0. The new category will contain items in category2 that are not in category1. This is very different from subtract. Example: if category1 contains A with a value of 2 and B with a value of 2, while category2 contains A with a value of 1 and C with a value of 1, '"category1" subtract "category2"' will contain A with a value of 1, B with a value of 2, and C with a value of -1, while '"category1" remove "category2"' will only contain B with a value of 2.

Other directives
Declare that the config is compatible with version version-number. This is for version-control purposes. Every config file should have one of these. You can scan your config files' config versions with -I config_versions. Your own version control information. revision-information can be arbitrary text. You can scan your config files' config versions with -I config_versions. Read in configuration from file. Dies if file doesn't exist. file is subject to usual tag substitutions; see the section on "TAG SUBSTITUTION". Just like include, but doesn't die if the file doesn't exist. Read in all files in dir, and include them. Die if the directory doesn't exist, or if a file in the directory isn't readable. dir is subject to the usual tag substitutions; see the section on "TAG SUBSTITUTION". Any filenames that match a pattern in filename_ignore_patterns will be skipped. Just like include_dir, but doesn't die if the directory doesn't exist. Does still die if any of the files in dir isn't readable. Throws out the block immediately after it. Set scalar variable varname to value value. If the variable already exists, this will overwrite it. See the section on "VARIABLES" for the list of variables you can play with. If scalar variable varname already exists, append value to the end of its current value. If it doesn't yet exist, create it and set it to value. See the section on "VARIABLES" for the list of variables you can play with. Read in the block that follows this declaration, make the lines into an array, and set the array variable arrname to that array. See the section on "VARIABLES" for the list of variables you can play with. Read in the block that follows this declaration, make the lines into an array, and append that array to the array named arrname. See the section on "VARIABLES" for the list of variables you can play with. Read in the block that follows this declaration, and for each line, look for and delete that line from array arrname. If one of these lines cannot be found, the result is a warning, not death. See the section on "VARIABLES" for the list of variables you can play with. Some variables are scalar, which means they are strings or numbers. Some variables are arrays, which are lists of scalars. Some variables are mandatory, which means they must be defined somewhere in one of the config files, while some variables are optional. Some variables are global, while some are per-log-type extensions. Some example of per-log-type extensions are date_pattern and filenames. Extensions should actually appear in the format "TYPE_EXTENSION", ie. date_pattern would actually appear as syslog_date_pattern for the syslog log-type and sulog_date_pattern for sulog. To see examples of many of the possibilities, as well as the default values, run log_analysis -I internal_config.

PER-LOG-TYPE VARIABLE EXTENSIONS
This mandatory extension is an array of file basenames that apply to the log type. For example, if you wanted /var/adm/messages.1 to be processed by the syslog rules, you might add messages to syslog_filenames. Some log files (ie. wtmp log types) are in a binary format that needs to be interpreted by external commands. This optional scalar extension specifies a command to be run to interpret the file. The command is subject to the usual tag substitutions (see the section on "TAG SUBSTITUTIONS"), plus the %f tag maps to the file. For example, the wtmp log type defines wtmp_open_command as "last -f %f". If both decompression_rules and open_command apply to a given file, the intermediate data will be stored in a temp file unless pipe_decompress_to_open is used. See the section on "pipe_decompress_to_open" for more info. In real mode, files that need an open_command will be ignored. If both decompression_rules and open_command apply to a given file, the intermediate data will be stored in a temporary file by default to avoid problems with some commands that can't handle input from a pipe. If this optional scalar extension is set to 1 (or any "true") value, then instead, the output of the decompression rule will be piped to the open command, and the open command's %f tag will be mapped to "-". This optional extension is an array of arbitrary perl commands that are run for each log line, before the date processing (or any other processing) is done. This mandatory extension is a scalar that contains a pattern with at least one parenthesized subpattern. Before any rules are applied to a log line, the engine strips off the date pattern. If the engine is only looking at one day (ie. the default), it takes the part of the string that matched the parenthesized subpattern, and if it isn't equal to the right date, it skips the line. The date_format extension (next) describes what the date should look like. This mandatory extension is a scalar that describes the date using the same format as strftime(3). For example, syslog_date_format is "%b %e". This optional extension is a pattern with at least one parenthesized subpattern. If it exists, then after the date_pattern is stripped from the line, this pattern is stripped, and the part that matched the subpattern is compared to the nodename. If they're not equal, then the relevant counter for the category named by the other_host_message variable is incremented. Note that all nodenames are subject to having the local domain stripped from them; see domain and leave_FQDNs_alone for details. This optional extension is an array of perl commands to be run after the nodename check, just before the skip_list check. This optional extension is obsolete and deprecated, but still works for backwards compatibility. This optional extension is obsolete and deprecated, but still works for backwards compatibility.

GLOBAL VARIABLES
These variables are all globals. This variable is a mandatory global array that contains the list of all known log-types, ie. syslog, sulog, wtmpx, etc. This variable is a mandatory global scalar that describes a host. This variable is a mandatory global scalar that describes a file. This variable is a mandatory global scalar that describes an ip in dotted-quad format. This variable is a mandatory global scalar that describes a mail username. This variable is a mandatory global scalar that describes a Unix username. This variable is a mandatory global scalar that describes a typical computer identifier This variable is a mandatory global scalar that describes a zone. Assorted mandatory scalars that are used for human-readable output. other_host_message defaults to "Other hosts syslogging to us", output_message_one_day defaults to "Logs for %n on %d", output_message_all_days defaults to "All logs for %n as of %d". output_message_all_days_in_range defaults to "All logs for %n for %s through %e". This variable is a mandatory global scalar that describes how you want the date printed in the output. Uses the format of strftime(3). Note that you probably shouldn't use characters that you wouldn't want in a filename (ie. whitespace or '/') if you want to use the %d tag for output_file. Equivalent to -o file. This variable is an optional global scalar that lists a filename that will be output to instead of to standard output. Works with mail_address (if specified.) Note that this variable is subject to the usual tag substitutions (see the section on "TAG SUBSTITUTIONS", plus you can use the %d tag for the date, so you can set it to something like "/var/log_analysis/archive/%n-%d". See output_file_and_stdout. Equivalent to -O. This variable is an optional global scalar that changes the behavior of -o or output_file. By default, -o or output_file causes output to only to only go to the named file. With this variable, output also goes to standard output. Note: this does not currently work with -m. This variable is an optional global scalar that is used in a bunch of places: in checking to see whether a message from syslog (or other log type that defines nodename_pattern) originated on this host; in reading in various default config files; etc. If left unset in the config, its value is set from the output of uname -n. Its value is used to set the n tag. Note that unless leave_FQDNs_alone is set, log_analysis will try to strip the local domain name from nodename. These two optional global scalars default to the output of uname -s and uname -r, respectively. They are only used for reading in default config files. Their values set the s and r tags, respectively. This variable is an optional global scalar. If you don't set it, log_analysis will try to set it by looking for a domain line in /etc/resolv.conf. If log_analysis has domain set, it will attempt to strip away the local domain name from all nodenames it encounters, unless leave_FQDNs_alone is set. See leave_FQDNs_alone for details. This variable is an optional global scalar. By default, if log_analysis has domain set (either explicitly or implicitly), it will attempt to strip away the domain name in domain from all nodenames it encounters. If you set this to 1, or to some other true value, log_analysis will not attempt to strip the domain name in domain. This variable is an optional global scalar that sets the PATH environment variable. This doesn't help the initial setting of nodename, osname, or osrelease, which are set by running uname. This variable is an optional global scalar that sets the umask. See umask(2). This variable is an optional global scalar that sets the priority, or "niceness." See nice(1). Setting this to zero means run unchanged from the current niceness. Setting this negative is a bad idea unless you really know what you're doing, and is forbdidden to non-root users. This variable is an optional global array of rules to decompress compressed files, in the format: compression-extension, comma, space, command to decompress to stdout. The command is subject to the usual tag substitutions (see the section on "TAG SUBSTITUTIONS", plus %f stands for the filename. For example, the rule for gzipped files is: `gz, gzip -dc %f' If both decompression_rules and open_command apply to a given file, the default is to use a temp file for the intermediate results unless pipe_decompress_to_open is used. See the section on "pipe_decompress_to_open" for more info. In real mode, compressed files will be ignored. This variable is an optional global array of rules for PGP encrypting messages, in the format: PGP type (user defined), comma, space, command to PGP encrypt stdin to stdout. The command is subject to the usual tag substitutions, plus %m stands for the email address. For use with the "-p" and "-m" options. For example, the rule for gnupg is: `g, gpg -aer %m 2>&1' Internally defined rules are "g" for "gnupg", "2" for PGP 2.x, and "5" for PGP 5.x. WARNING: The user who runs log_analysis must have already imported the mail destination's key for this to work. Make sure to test this before you put it in a cronjob. This variable is an optional global array of patterns that describe filenames to be skipped in an include_dir/include_dir_if_exists context, such as emacs backup file (".*~") or vim backup files ("\..*\.swp"). Only the file component of the path is examined, not the directory component. Patterns implicitly begin with ^ and implicitly end with $. This variable is an optional global scalar that can consist of an email address. If set, the output of the script will be mailed to the address it is set to. The -m option does the same thing, and overrides this. This variable is an optional global scalar that is the command used to send mail if -m is user or mail_address is set. The -M option does the same thing, and overrides this. This variable is subject to the usual tag substitutions, plus %m stands for mail_address and %o stands for the relevant output message. The default is: `Mail -s '%o' %m' This variable is an optional array of file globs that are to be processed. Note that, unlike required_log_files, these are globs rather than literal filenames, although literal filenames will also work. [Globs are filenames with wildcards, ie. /var/adm/messages*.] WARNING: in real mode, only the most recent file per glob in optional_log_files is monitored. This means that you should set it to something like /var/log/messages* and /var/log/syslog* rather than /var/log/*. This variable is an optional array of commands that are also supposed to be run to give a snapshot of the system state. These are currently: w, df -k, and cat /etc/dumpdates. This variable is an optional array of categories that you don't want to see. Rather than try to remove all the rules for these categories, you can just list them here. This variable is an optional array of categories that will be listed first in the output. This variable is the config equivalent of the -N option. This variable is an optional array of nodenames that can log to this host. Usually, logs labelled as being from another host will not be anaylzed, and each such line will be listed in a special category; if you chose to allow some nodenames (or if you choose to process all nodenames by setting -N or setting process_all_nodenames) then these log messages will also be processed. This variable is the config equivalent of the -r option; see the -r option for more details. This variable is a required global scalar. It describes the per-output format for real mode. "\\" is replaced with "\", "\n" is replaced with newline, "\t" with tab, and then tag substitution occurs (see the section on "TAG SUBSTITUTION"); in addition to the normal tags, "%c" is replaced with the category, "%#" is replaced with the count, "%d" is replaced with the formatted data, and "%h" is replaced with the nodename of the message. WARNING: you usually want "%h" (nodename of the message), not "%n" (nodename of the host you're running on, which is one of the default tags substitutions.) Defaults to "%c: (loghost %n, from host %h)\n%-10# %d\n\n". This variable is an optional global scalar for use with real mode; see -r for more on real mode. In real mode, log_analysis reads log files for more data, sleeps for a little while, and then reads again. The sleep interval controls how long log_analysis sleeps (in seconds). It defaults to 1. This variable is an optional global scalar for use with real mode; see -r for more on real mode. In real mode, log_analysis sits in a loop reading from the logs files. Periodically, it wants to check if the log files have rolled over or if newer log files have appeared. If at least this long (in seconds) goes by since the last time we've checked, we check again. This variable is an optional global scalar that describes how certain things will be sorted. See the section on "SORTING" for info on what this can be set to. Defaults to funky. This variable is an optional global scalar that describes the default category filter. See the section on "FILTERS" for info on what this can be set to. NB: these get completely processed before all other directives, so they don't care about other syntax elements. Except as noted, these should appear at the beginning of the line after optional whitespace. End of config file. Define var as value val. var should contain only alphanumerics and underscores, and start with an alphanumeric. val may contain no whitespace. Undo any previous definition of var. If variable var is defined, even defined as a false value, the lines after the @@ifdef are used, otherwise the lines are effectively commented out. @@ifndef is the logical reverse. @@ifdef and @@ifndef must be terminated by an @@endif. They may contain an @@else section that works in the usual way. These are just like @@ifdef and @@ifndef above, except that they test if the variable nodename is equal to the value supplied for name. If this string appears anywhere on any line, then if var is a defined variable, its value is substituted. If var is not a defined variable, the string is left literally. Note that this behaviour is different from that of aide(1). Print out message as soon as the config is read. Print out message and exit as soon as the config is read. You can sort category items using several different criteria. You can set the default_sort, and then on a per-category basis, you can use the sort: keyword to control things even closer. If you don't override it, default_sort defaults to funky. Sorts stack, so you can use "reverse string" or "reverse value". In theory, you can stack all of them, ie. "reverse value reverse funky", but there is no guarantee that sorts are stable. The available sorts are: Simple string "lexicographical" sort. Does not handle numbers well. Sorts numbers, including decimal numbers, correctly, but cannot handle non-numeric characters, and cannot handle IPs correctly. Tries to do the right thing with mixed integers and strings. Handles IP addresses correctly. It does not handle decimal numbers correctly. Reverses the current order. Can be used in conjunction with another sort, ie. "reverse string". Sorts by count (ascending) instead of by item. Does no additional sorting. Sometimes, you don't want to see all the information in a category, just the top few items, or whatever. Filters let you do this. You can set a default filter using default_filter (defaults to "none") or you can set filters on a per-category basis using the filter: keyword. Some commands you can use: Only show items whose count is greater than or equal to N. These are analagous to >=. Only show those items who count is in the top N or top N%. The difference between top and top_strict is what happens when there's a tie to be in the top N. top will include all the items that tie, even if this means there will be more than N. top_strict always cuts off after N. Analagous to top. Lets you "and" or "or" two or more subfilters togther (ie. "top 10 and >= 4"). log_analysis has a relatively simple counting mechanism that is usually effective. One exception is when you want to track how often one value occurs in your log uniquely with another value. For example, suppose you're watching firewall logs, $1 is the source IP, $2 is the destination IP, and you want to know if you're being scanned. Tracking counts of "$1 $2" requires you to manually count how many times $1 occurs. Tracking just "$1" doesn't really tell you what you want, because you don't know if the source IP is really scanning a bunch of different hosts, or just has a renegade process that's banging away at a single destination. What you want to track is how many times $1 occurs with a unique $2. To do this sort of thing in a pattern config, set format: to value1, value2 and set dest: to "UNIQUE category-name". In our example, we might say: format: $1, $2 dest: UNIQUE scans The fields in format are not evaluated in a string context, and only the last comma acts as a separator. So, if $3 contains the protocol information, you might say this: format: sprintf("%-15s %s", $1, $3), $2 dest: UNIQUE scans A few items are subject to "tag substitutions". These are kind of like printf's "%" sequences: a sequence like "%n" gets replaced with the nodename. You can optionally specify field widths, which default to right-justified (ie. "%10n") or can be preceeded with a "-" to make them left-justified (ie. "%-10n"). Anything subject to tag substitutions will be listed as such. The default tag character is "%" unless otherwise listed. Here are the standard sequences: If you try to use a "%" followed by something else, you'll get an error. There are also other sequences that apply in special situations; these are listed where they apply. log_analysis -m root@whatever Analyze yesterday's logs and mail the results to root@whatever. You might want to put this in a cronjob. log_analysis -p5 -m root@whatever Same as the last one, but PGP encrypt the logs using PGP 5 before mailing. log_analysis -a Look at all the logs, not just yesterday's. log_analysis -sa /var/adm/sulog Analyze all the contents of sulog, don't bother with local state. log_analysis -san otherhost syslog-file Analyze all the contents of syslog-file, which was created on "otherhost". Don't run the local state commands. log_analysis -sd1 -f foo.conf -U This style of command is useful while developing local configs to handle log messages unknown to the internal config. Use foo.conf as a config file in addition to the internal config. Output only the unknowns. Written for Solaris and Linux. May work for other OSs. Written for perl 5.00503. May work with some earlier perl versions. You often need to be root to read interesting log files. It is customary to regularly "rollover" log files. Many log file formats don't include year infomation; among other benefits, rollover makes the dates in such logfiles unambiguous. log_analysis by default looks for log lines that match a particular day of the year, but does not even try to guess the year. If the OS you're using doesn't rollover some logfiles by default (ie. Solaris doesn't rollover /var/adm/wtmpx, /var/adm/wtmp, or /var/adm/sulog), you will need to rollover these files yourself to get valid output from this program. On some OSes, '%' (ie. the percent symbol) has a special meaning in crontabs, and needs to be commented. See crontab(1). When there are a lot of unknowns, log_analysis can take a lot longer to run. This is particularly a problem when you're first running it, before you customize for your site. To get around this problem, if you send log_analysis a SIGINT (ie. if you hit control-C), it will stop going through your logs and immediately output the results. Config files, in order of precedence. "%n", "%s", and "%r" have the usual tag substitution meanings; see the section on "TAG SUBSTITUTIONS". Plug-in directories. All files in these directories will be treated as config files and include'd. Mordechai T. Abzug <morty@sanctuary.arbutus.md.us> syslogd(8), last(1), perlre(1)