Permalink
Cannot retrieve contributors at this time
#!/usr/bin/ruby | |
# == Synopsis | |
# | |
# Apache Logfile Scanning Script | |
# | |
# Copyright 2007-2019 Christian Folini (folini@netnea.com) | |
# | |
# This software has been released under the GPL 3.0. | |
# | |
# parse-apache-logs.rb [OPTIONS] file(s) | |
# <STDIN> | parse-apache-logs.rb [OPTIONS] | |
# | |
# Call with --help to get an overview of options. | |
# | |
require "tempfile" | |
require "getoptlong" | |
require 'zlib' | |
require 'pp' | |
include Zlib | |
LOGFORMAT_EXTENDED2019 = "Remote-IP Country Remote-User [Timestamp] \"Method Path Version\" Status Response-Size \"Referer\" \"User-Agent\" \"Content-Type\" Remote-Port ServerName Local-IP Local-Port RspHandler BalRoute ConnStatus \"Tracking-ID\" Request-ID SSLProtocol SSLCipher IO-In IO-Out Deflate-Ratio Duration ModSecPerfIn PerfAppl ModSecPerfOut ModSecInPL ModSecOutPL ModSecScoreIn ModSecScoreOut" | |
LOGFORMAT_EXTENDED2015 = "Remote-IP Country Remote-User [Timestamp] \"Method Path Version\" Status Response-Size \"Referer\" \"User-Agent\" ServerName Local-IP Local-Port RspHandler BalRoute ConnStatus \"Tracking-ID\" Request-ID SSLProtocol SSLCipher IO-In IO-Out Deflate-Ratio Duration ModSecPerfIn PerfAppl ModSecPerfOut ModSecScoreIn ModSecScoreOut" | |
LOGFORMAT_EXTENDED2014 = "Remote-IP Country Remote-User [Timestamp] \"Method Path Version\" Status Response-Size \"Referer\" \"User-Agent\" ServerName Local-IP Local-Port RspHandler BalRoute ConnStatus \"Tracking-ID\" Request-ID SSLProtocol Cipher IO-In IO-Out Deflate-Ratio Duration ModSecPerfIn PerfAppl ModSecPerfOut ModSecScoreIn ModSecScoreOut" | |
LOGFORMAT_EXTENDED2012 = "Remote-IP Remote-Logname Remote-User [Timestamp] \"Method Path Version\" Status Response-Size \"Referer\" \"User-Agent\" ServerName Local-IP Local-Port RspHandler BalRoute ConnStatus \"Tracking-ID\" Request-ID IO-In IO-Out Deflate-Ratio Duration ModSecPerfIn PerfAppl ModSecPerfOut ModSecScoreIn ModSecScoreOut" | |
LOGFORMAT_EXTENDED2011 = "Remote-IP Remote-Logname Remote-User [Timestamp] \"Method Path Version\" Status Response-Size \"Referer\" \"User-Agent\" ServerName Local-IP Local-Port RspHandler BalRoute ConnStatus \"Tracking-ID\" Request-ID IO-In IO-Out Deflate-Ratio Duration ModSecTime1 ModSecTime2 ModSecTime3 ModSecAnomaly" | |
LOGFORMAT_EXTENDED2007 = "Remote-IP Remote-Logname Remote-User [Timestamp] \"Method Path Version\" Status Response-Size \"Referer\" \"User-Agent\" ServerName Local-IP Local-Port \"Tracking-ID\" Request-ID IO-In IO-Out Deflate-Ratio Duration ModSecTime1 ModSecTime2 ModSecTime3" | |
LOGFORMAT_COMBINED = "Remote-IP Remote-Logname Remote-User [Timestamp] \"Method Path Version\" Status Response-Size \"Referer\" \"User-Agent\"" | |
params = Hash.new | |
params["logfiles"] = nil # space seperated list of logfiles | |
params["logformat"] = LOGFORMAT_EXTENDED2019 | |
params["outformat"] = nil # format to display the data after processing | |
params["filterstring"] = nil # format to display the data after processing | |
# ------------------------------------------ | |
# Subfunctions | |
# ------------------------------------------ | |
def vprint(text) | |
# | |
# Output text if global variable $verbose is set. | |
# | |
if $verbose | |
puts text + "\n" | |
end | |
end | |
def usage () | |
puts <<EOF | |
# | |
Synopsis | |
-------- | |
Apache Logfile Scanning Script | |
Copyright 2007-2019 Christian Folini (folini@netnea.com) | |
This software has been released under the GPL. | |
Usage | |
----- | |
parse-apache-logs.rb [OPTIONS] file(s) | |
<STDIN> | parse-apache-logs.rb [OPTIONS] | |
-h --help This text | |
-? --usage This text | |
-v --verbose Be verbose | |
-i --informat Specify the logfile format | |
Default is extended2012 | |
-o --outformat Specify an output format. This is a | |
required parameter | |
-f --filter Filter for specific conditions | |
Usage examples | |
-------------- | |
Take file access.log and print Status and Path for every request line: | |
parse-apache-logs.rb --outformat "Status Path" access.log | |
Find out which Browser (User-Agent) is being used by whom: | |
parse-apache-logs.rb -o "Remote-User User-Agent" access.log | sort | uniq | |
Find out which IP address accessed the / of the server: | |
parse-apache-logs.rb -o "Timestamp Remote-IP" --filter "Path == /" access.log | |
Select requests taking longer than 1s (1000000 microseconds) and HTTP Status 200 OK | |
parse-apache-logs.rb -o "Path" -f "Duration > 1000000 and Status == 200" access.log | |
Select requests with an Anomaly Score of 5 or above | |
parse-apache-logs.rb -o "Timestamp Remote-IP" -f "Anomaly-Score >= 5" | |
Filter options | |
-------------- | |
The Filter knows the following conditions | |
a == b for numbers and strings | |
a != b for numbers and strings | |
a > b for numbers | |
a >= b for numbers | |
a < b for numbers | |
a <= b for numbers | |
a =~ b regex evaluation for numbers and strings | |
Filters can be concatenated with "and". | |
"Or" is not supported. | |
EOF | |
puts "Predefined Formats" | |
puts "------------------" | |
puts | |
puts "extended / extended2019:" | |
puts LOGFORMAT_EXTENDED2019 | |
puts | |
puts "extended2015:" | |
puts LOGFORMAT_EXTENDED2015 | |
puts | |
puts "extended2014:" | |
puts LOGFORMAT_EXTENDED2014 | |
puts | |
puts "extended2012:" | |
puts LOGFORMAT_EXTENDED2012 | |
puts | |
puts "extended2007:" | |
puts LOGFORMAT_EXTENDED2007 | |
puts | |
puts "combined:" | |
puts LOGFORMAT_COMBINED | |
puts | |
puts | |
puts "combined:" | |
puts LOGFORMAT_COMBINED | |
puts | |
puts | |
puts "Construction of the output format" | |
puts "---------------------------------" | |
puts | |
puts "You can use the abbrevated names as parameter" | |
puts "for command line option --outformat.:" | |
puts "All variable names in the informat can be used to construct a new " | |
puts "outformat or a filter. A typical example for an outformat:" | |
puts | |
puts "parse-apache-logs.rb --informat extended2012 --outformat \"Timestamp Status\" ..." | |
puts "..." | |
puts "12/Nov/2007:21:50:49 +0100 200" | |
puts "12/Nov/2007:21:51:28 +0100 200" | |
puts "12/Nov/2007:21:51:39 +0100 200" | |
puts "..." | |
puts | |
puts "However, you can also do conversions like:" | |
puts "parse-apache-logs.rb --informat extended2012 --outformat combined ..." | |
puts | |
return | |
end | |
def check_stdin () | |
# check for existence of stdin | |
if STDIN.tty? | |
# no stdin | |
return false | |
else | |
# stdin | |
return true | |
end | |
end | |
def check_parameters(params) | |
# | |
# Check the validity of the command line parameters. | |
# Bail out if an error occurs. | |
# | |
params["logfiles"].each do |file| | |
unless FileTest::exists?(file) | |
puts "Logfile parameter #{file} does not exist. This is fatal. Aborting." | |
exit 1 | |
end | |
unless FileTest::file?(file) | |
puts "Logfile parameter #{file} is not a file. This is fatal. Aborting." | |
exit 1 | |
end | |
end | |
unless ( params["logformat"].downcase == "combined" || | |
params["logformat"].downcase == "extended" || | |
params["logformat"].downcase == "extended07" || | |
params["logformat"].downcase == "extended11" || | |
params["logformat"].downcase == "extended12" || | |
params["logformat"].downcase == "extended14" || | |
params["logformat"].downcase == "extended15" || | |
params["logformat"].downcase == "extended19" ) | |
error = parse_logformat(params["logformat"])[3] | |
if error | |
puts "Could not parse the logformat (see next line). This is fatal. Aborting." | |
puts params["logformat"] | |
exit 1 | |
end | |
end | |
if params["outformat"] == "" or params["outformat"].nil? | |
params["outformat"] = params["informat"] | |
end | |
end | |
def parse_filter(filterstring, informat_fields) | |
# example: filterstring = "Response-Size < 500 AND Response-Size > 200" | |
# returns an array with multiple hash-items. | |
# filters will be connected via "AND". | |
# "OR" is not supported. | |
filters = [] | |
fieldname, operator, parameter = nil | |
unless filterstring.nil? | |
myfilterstring = filterstring.gsub(" and ", " AND ") # capitalize AND | |
myfilterstring = myfilterstring.gsub(" && ", " AND ") # rewrite | |
myfilterstring = myfilterstring.gsub(" or ", " OR ") # capitalize OR (which is not supported. see below) | |
if (myfilterstring.split(" OR ").length > 1 || myfilterstring.split(" || ").length > 1 ) | |
$stderr.puts "Filter contains \"OR\" resp. \"or\" resp \"||\". This is not supported. Aborting." | |
exit 1 | |
end | |
begin | |
filterstringparts = myfilterstring.split("AND") | |
filterstringparts.each do |item| | |
fieldname, operator, parameter = item.split(" ") | |
if fieldname.nil? or operator.nil? or parameter.nil? | |
raise | |
end | |
if ["==", "!=", ">=", "<=", ">", "<", "=", "=~" ].index(operator).nil? | |
$stderr.puts "Filter operator #{operator} is not known. This is fatal. Aborting." | |
exit 1 | |
end | |
# start adjust operators and parameters | |
operator = "==" if operator == "=" | |
if ["==", "!="].index(operator) | |
parameter = parameter[1..parameter.length] if parameter[0..0] == "\"" # remove beginning " | |
parameter = parameter[0..parameter.length-2] if parameter[-1..-1] == "\"" # remove trailing " | |
end | |
if operator == "=~" | |
parameter = parameter[1..parameter.length] if parameter[0..0] == "/" # remove beginning slash | |
parameter = parameter[0..parameter.length-2] if parameter[-1..-1] == "/" # remove trainling slash | |
end | |
# end start adjust operators and parameters | |
filters << {"field" => informat_fields.index(fieldname), "operator" => operator , "parameter" => parameter} | |
end | |
rescue => detail | |
$stderr.puts "Could not parse filter (current subitem is fieldname/operator/paramter: #{fieldname}/#{operator}/#{parameter}. This is fatal. Aborting." | |
$stderr.puts detail | |
exit 1 | |
end | |
end | |
return filters | |
end | |
def extract_next_param(ignoreflag, linenum, line, sep=" ", sep2="") | |
item = "" | |
if sep == " " # space separated | |
n = line.index(sep) | |
if n.nil? # no space separator found (= last item of line) | |
n = line.length | |
item = line | |
else | |
item = line[0,n] | |
end | |
line = line[n + 1, line.length] | |
elsif (sep == "[" and sep2 == "]") or (sep == "\"" and sep2 == "\"") | |
# different separator | |
n = line.index(sep) | |
begin | |
line = line[n + 1, line.length] | |
n = line.index(sep2) | |
item = line[0,n] | |
line = line[n + 2, line.length] | |
rescue | |
$stderr.puts "Error parsing line #{linenum}. Ignoring Line." | |
ignoreflag = true | |
item = "" | |
line = "" | |
end | |
else | |
$stderr.puts "Separators #{sep} and #{sep2} unknown. This is fatal. Aborting." | |
exit 1 | |
end | |
return ignoreflag, line, item | |
end | |
def parse_display_logfile(file, informat_fields, displayfields_prefixes, displayfields, displayfields_suffixes, filters, logformat) | |
def process_handler(handler, informat_fields, displayfields_prefixes, displayfields, displayfields_suffixes, filters, logformat, filename=nil) | |
linenum = 0 | |
unless filename.nil? | |
if ( File.basename(filename, ".gz") != File.basename(filename) ) | |
handler = GzipReader.new(handler) | |
end | |
end | |
case logformat | |
when LOGFORMAT_COMBINED | |
pattern = /^(\S+) (\S+) (\S+) \[([^\]]+)\] "(\S+) (.+?) (\S+)" (\S+) (\S+) "([^"]+)" "([^"]+)"$/ | |
numfields = 11 | |
when LOGFORMAT_EXTENDED2007 | |
pattern = /^(\S+) (\S+) (\S+) \[([^\]]+)\] "(\S+) (.+?) (\S+)" (\S+) (\S+) "([^"]+)" "([^"]+)" (\S+) (\S+) (\S+) "(\S+)" (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+)$/ | |
numfields = 23 | |
when LOGFORMAT_EXTENDED2011 | |
pattern = /^(\S+) (\S+) (\S+) \[([^\]]+)\] "(\S+) (.+?) (\S+)" (\S+) (\S+) "([^"]+)" "([^"]+)" (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) "(\S+)" (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+)$/ | |
numfields = 27 | |
when LOGFORMAT_EXTENDED2012 | |
pattern = /^(\S+) (\S+) (\S+) \[([^\]]+)\] "(\S+) (.+?) (\S+)" (\S+) (\S+) "([^"]+)" "([^"]+)" (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) "(\S+)" (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+)$/ | |
numfields = 28 | |
when LOGFORMAT_EXTENDED2014 | |
pattern = /^(\S+) (\S\S?) (\S+) \[([^\]]+)\] "(\S+) (.+?) (\S+)" (\S+) (\S+) "([^"]+)" "([^"]+)" (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) "(\S+)" (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+)$/ | |
numfields = 30 | |
when LOGFORMAT_EXTENDED2015 | |
pattern = /^(\S+) (\S\S?) (\S+) \[([^\]]+)\] "(\S+) (.+?) (\S+)" (\S+) (\S+) "([^"]+)" "([^"]+)" (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) "([^"]+)" (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+)$/ | |
numfields = 30 | |
when LOGFORMAT_EXTENDED2019 | |
pattern = /^(\S+) (\S\S?) (\S+) \[([^\]]+)\] "(\S+) (.+?) (\S+)" (\S+) (\S+) "([^"]+)" "([^"]+)" "(.*)" (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+)$/ | |
numfields = 34 | |
else | |
raise | |
end | |
handler.each do |line| | |
begin | |
linenum += 1 | |
ignoreflag = false | |
linearray = Array.new | |
ret = pattern.match(line) | |
if ret != nil | |
linearray = ret[1,numfields] | |
else | |
# line does not match our pattern | |
# one reason could be, that we did not receive a request line | |
# or a broken request line. We will now expand the | |
# placeholder "-" to get a request line which we | |
# can parse. | |
line.gsub!(/" 40([08]) ([0-9-])/, ' -" 40\1 \2') | |
ret = pattern.match(line) | |
if ret != nil | |
linearray = ret[1,numfields] | |
else | |
# did not work. Another expansion attempt. | |
line.gsub!(/" 40([08]) ([0-9-])/, ' -" 40\1 \2') | |
ret = pattern.match(line) | |
if ret != nil | |
linearray = ret[1,numfields] | |
else | |
# no luck | |
raise | |
end | |
end | |
end | |
# --- start filter code | |
display = true | |
filters.each do |filter| | |
value = linearray[filter["field"]] | |
operator = filter["operator"] | |
parameter = filter["parameter"] | |
# puts "#{value} #{operator} #{parameter} #{display}" | |
if not /^\d+([.,]\d+)?$/.match(parameter).nil? and ["==", "!=", ">=", "<=", ">", "<"].index(operator) | |
# filtering for number | |
value = 0 if value == "-" | |
if not eval "#{value} #{operator} #{parameter}" | |
display = false | |
end | |
elsif operator == "=~" | |
unless /#{parameter}/.match(value).to_a.length > 0 | |
display = false | |
end | |
elsif value.to_s == "-" and parameter.to_i > 0 | |
# filtering for number, but there is "-" in the logfile instead. | |
display = false | |
elsif value.to_s.length > 0 and ["==", "!="].index(operator) | |
# filtering for string | |
if not eval "\"#{value}\" #{operator} \"#{parameter.gsub("\"","")}\"" | |
display = false | |
end | |
#puts "#{value} #{operator} #{parameter} #{display}" | |
else | |
$stderr.puts "Can not cope with filter value/operator/parameter (#{value} #{operator} #{parameter}) on line #{linenum}. Not applying this filter to this line." | |
end | |
end | |
# --- end filter code | |
if display | |
n = 0 | |
displine="" | |
displayfields.each do |x| | |
displine += "#{displayfields_prefixes[n]}#{linearray[x]}#{displayfields_suffixes[n]}" | |
n += 1 | |
end | |
print "#{displine}\n" | |
end | |
rescue => err | |
$stderr.puts "Problems parsing line #{linenum}. Ignoring line. (Error #{err})" | |
$stderr.puts line | |
end | |
end | |
# --- end handle each line | |
end | |
# --- end process handler | |
if check_stdin() | |
process_handler(STDIN, informat_fields, displayfields_prefixes, displayfields, displayfields_suffixes, filters, logformat) | |
else | |
File.open(file) { |handler| | |
process_handler(handler, informat_fields, displayfields_prefixes, displayfields, displayfields_suffixes, filters, logformat, file) | |
} | |
end | |
end | |
def read_logfiles(logfiles, logformat, informat_fields, displayfields_prefixes, displayfields, displayfields_suffixes, filters) | |
lines = Array.new | |
unless check_stdin() | |
# logfiles as parameter | |
logfiles.each do |logfile| | |
parse_display_logfile(logfile, informat_fields, displayfields_prefixes, displayfields, displayfields_suffixes, filters, logformat) | |
end | |
else | |
# logfile via stdin | |
parse_display_logfile(STDIN, informat_fields, displayfields_prefixes, displayfields, displayfields_suffixes, filters, logformat) | |
end | |
end | |
def parse_logformat(format) | |
# Take a format string and extract the fieldnames and the separators | |
# The only separators allowed are <space>, " and [ resp. ]. | |
error = false | |
separators = Array.new | |
itemnames = Array.new | |
itemname = "" | |
lookout = "" | |
begin | |
chars = format.scan(/./) | |
chars.each { |char| | |
if char == " " | |
if itemname.length > 0 | |
separators << [" ", " "] | |
itemnames << itemname | |
itemname = "" | |
end | |
elsif char == lookout | |
itemnames << itemname | |
itemname = "" | |
lookout = "" | |
elsif char == "[" | |
separators << ["[", "]"] | |
lookout = "]" | |
elsif char == "\"" | |
separators << ["\"", "\""] | |
lookout = "\"" | |
else | |
itemname += char | |
end | |
} | |
if itemname.length > 0 # cleanup | |
separators << [" ", " "] # this will be the last item of the line | |
itemnames << itemname | |
end | |
vprint "Logformat analysed. Found #{itemnames.length} items in format." | |
rescue | |
error = true | |
end | |
return itemnames, separators, error | |
end | |
def output_parse_logformat(format) | |
fieldnum = 0 | |
lasthit = 0 | |
fieldnames_prefixes = [] | |
fieldnames = [] | |
fieldnames_suffixes = [] | |
curr_prefix = "" | |
curr_field = "" | |
curr_suffix = "" | |
0.upto(format.length - 1) do |n| | |
letter = format[n,1] | |
hit = /[a-zA-Z0-9_-]/.match(letter).to_a.length | |
if hit == 1 | |
if lasthit != hit | |
fieldnames_prefixes << curr_prefix | |
curr_prefix = "" | |
fieldnames_suffixes << curr_suffix if fieldnames_prefixes.length > 1 | |
curr_suffix = "" | |
end | |
curr_field += letter | |
else | |
if lasthit != hit | |
fieldnames << curr_field | |
curr_field = "" | |
end | |
if fieldnames_prefixes.length == fieldnames_suffixes.length | |
curr_prefix += letter | |
else | |
curr_suffix += letter | |
end | |
end | |
lasthit = hit | |
#puts "State run #{n}: P:#{curr_prefix} F:#{curr_field} S:#{curr_suffix} PL:#{fieldnames_prefixes.length} FL:#{fieldnames.length} SL:#{fieldnames_suffixes.length}" | |
end | |
fieldnames << curr_field if curr_field.length > 0 # this one might still be left | |
fieldnames_suffixes << curr_suffix # this one is still left | |
#puts "------------" | |
#puts fieldnames_prefixes | |
#puts "------------" | |
#puts fieldnames | |
#puts "------------" | |
#puts fieldnames_suffixes | |
#puts "------------" | |
return fieldnames_prefixes, fieldnames, fieldnames_suffixes | |
end | |
def check_outputformat_fieldnames(outformat_fields, informat_fields) | |
return (outformat_fields - informat_fields).length == 0 # length of difference set of the arrays | |
end | |
def prepare_outformat(informat, outformat) | |
informat_fields, displayfields_prefixes, displayfields, displayfields_suffixes = [] | |
if outformat == "combined" | |
outformat = LOGFORMAT_COMBINED | |
elsif outformat == "extended2007" | |
outformat = LOGFORMAT_EXTENDED2007 | |
elsif outformat == "extended2011" | |
outformat = LOGFORMAT_EXTENDED2011 | |
elsif outformat == "extended2012" | |
outformat = LOGFORMAT_EXTENDED2012 | |
elsif outformat == "extended2014" | |
outformat = LOGFORMAT_EXTENDED2014 | |
elsif outformat == "extended2015" | |
outformat = LOGFORMAT_EXTENDED2015 | |
elsif outformat == "extended2019" | |
outformat = LOGFORMAT_EXTENDED2019 | |
elsif outformat == "extended" | |
outformat = LOGFORMAT_EXTENDED2019 | |
elsif outformat.nil? | |
vprint "No output parameter passed. Assuming extended-2019." | |
outformat = LOGFORMAT_EXTENDED2019 | |
end | |
if outformat | |
informat_fields = output_parse_logformat(informat)[1] # get the logfile fieldnames | |
displayfields_prefixes, displayfields, displayfields_suffixes = output_parse_logformat(outformat) | |
unless check_outputformat_fieldnames(displayfields, informat_fields) | |
$stderr.puts "Output format contains items not appearing in the input format (=input logfile). This is fatal. Aborting." | |
exit 1 | |
end | |
# now we transform the array displayfields (containing fieldnames) into | |
# index numbers of the fields of the logfile(s) | |
0.upto(displayfields.length - 1) do |n| | |
displayfields[n] = informat_fields.index(displayfields[n]) | |
end | |
end | |
return informat_fields, displayfields_prefixes, displayfields, displayfields_suffixes | |
end | |
def display_lines(lines, displayfields_prefixes, displayfields, displayfields_suffixes) | |
unless displayfields.nil? | |
# now doing the display | |
linenum = 0 | |
lines.each do |line| | |
linenum += 1 | |
displine = "" | |
begin | |
n = 0 | |
displayfields.each do |x| | |
displine += "#{displayfields_prefixes[n]}#{line[x]}#{displayfields_suffixes[n]}" | |
n += 1 | |
end | |
print "#{displine}\n" | |
rescue => err | |
$stderr.puts "Problems parsing line #{linenum}. Ignoring line. (Error #{err})" | |
end | |
end | |
end | |
end | |
# ---------------------------------- | |
# Command line options and arguments | |
# ---------------------------------- | |
opts = GetoptLong.new( | |
[ '-h', '--help', '-?', '--usage', GetoptLong::NO_ARGUMENT ], | |
[ '-v', '--verbose', GetoptLong::NO_ARGUMENT ], | |
[ '-f', '--filter', GetoptLong::REQUIRED_ARGUMENT ], | |
[ '-i', '--informat', GetoptLong::REQUIRED_ARGUMENT ], | |
[ '-o', '--outformat', GetoptLong::REQUIRED_ARGUMENT ] | |
) | |
opts.each do |opt, arg| | |
case opt | |
when '-h' | |
usage | |
exit | |
when '--help' | |
RDoc::usage | |
when '-v' | |
$verbose = true | |
when '--verbose' | |
$verbose = true | |
when '-i', '--informat' | |
if arg == "combined" | |
params["logformat"] = LOGFORMAT_COMBINED | |
elsif arg == "extended2007" | |
params["logformat"] = LOGFORMAT_EXTENDED2007 | |
elsif arg == "extended2011" | |
params["logformat"] = LOGFORMAT_EXTENDED2011 | |
elsif arg == "extended2012" | |
params["logformat"] = LOGFORMAT_EXTENDED2012 | |
elsif arg == "extended2014" | |
params["logformat"] = LOGFORMAT_EXTENDED2014 | |
elsif arg == "extended2015" | |
params["logformat"] = LOGFORMAT_EXTENDED2015 | |
elsif arg == "extended2019" | |
params["logformat"] = LOGFORMAT_EXTENDED2019 | |
elsif arg == "extended" | |
params["logformat"] = LOGFORMAT_EXTENDED2019 | |
else | |
params["logformat"] = arg | |
end | |
when '-f' | |
params["filterstring"] = arg | |
when '--filter' | |
params["filterstring"] = arg | |
when '-o' | |
params["outformat"] = arg | |
when '--outformat' | |
params["outformat"] = arg | |
when '-?' | |
usage | |
exit | |
when '--usage' | |
usage | |
exit | |
end | |
end | |
if ARGV.length < 1 and not check_stdin() | |
puts "Missing logfiles argument and missing stdin as well (try --help). This is fatal. Aborting." | |
exit 1 | |
end | |
params["logfiles"] = ARGV | |
vprint "Logfiles set to #{params["logfiles"].join(" ")}." | |
# ---------------------------------- | |
# MAIN | |
# ---------------------------------- | |
check_parameters(params) | |
informat_fields, displayfields_prefixes, displayfields, displayfields_suffixes = prepare_outformat(params["logformat"], params["outformat"]) | |
filters = parse_filter(params["filterstring"], informat_fields) | |
read_logfiles(params["logfiles"], params["logformat"], informat_fields, displayfields_prefixes, displayfields, displayfields_suffixes, filters) | |