Skip to content

Instantly share code, notes, and snippets.

@datenimperator
Last active December 29, 2018 15:00
Show Gist options
  • Save datenimperator/5fe86f6a318b7b27a0a934f72af3340d to your computer and use it in GitHub Desktop.
Save datenimperator/5fe86f6a318b7b27a0a934f72af3340d to your computer and use it in GitHub Desktop.
Ruby Postfix log file report
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'pp'
require 'time'
PROCESS = /^(?<process>[a-z]+)\/?(?<sub>[a-z]+)?\[?(?<pid>\d+)?\]?/
class Histo < Hash
def default(val)
0
end
def top(max=5)
sort{|a, b| b[1]<=>a[1]}.take(max)
end
end
class Entry
attr_accessor :timestamp, :hostname, :process, :sub_process, :pid, :message
end
class Statistics
def initialize(entry)
@entry = entry
end
def process_to(rep)
rep['stats'] ||= Histo.new
rep['stats']['lines'] += 1
end
def self.accept?(entry)
true
end
end
class RejectAnalyzer
PREFIX = 'NOQUEUE: reject: '
KEY_VAL = /([a-z]+)=(\S+)/
def initialize(entry)
@entry = entry
end
def process_to(rep)
rep['rejects'] ||= Hash.new
@entry.message.scan(KEY_VAL).each do |k, v|
rep['rejects'][k] ||= Histo.new(0)
rep['rejects'][k][v] += 1
end
end
def self.accept?(entry)
return false unless entry.process == 'postfix'
return false unless entry.sub_process == 'smtpd'
return false if entry.message.nil?
entry.message.start_with?(PREFIX)
end
end
TESTS = [
Statistics,
RejectAnalyzer
]
def parse(path)
File.open(path) do |f|
f.readlines.each do |line|
raw = line.split(' ')
syslog_prefix = raw[0..4]
entry = Entry.new.tap do |e|
e.timestamp = Time.parse(syslog_prefix[0..2].join(' '))
e.hostname, e.process = syslog_prefix[3..4]
if m = e.process.match(PROCESS)
e.process = m[:process]
e.sub_process = m[:sub]
e.pid = m[:pid]
end
e.message = raw[5..-1].join(' ')
end
TESTS.each do |t|
t.new(entry).process_to(@report) if t.accept?(entry)
end
end
end
end
@report = Hash.new
parse(ARGV[0])
%w[stats rejects].each do |s|
if o = @report[s]
puts s.upcase
puts '='*40
o.each do |k, v|
case v
when Histo then
puts "#{k.capitalize}:"
puts '-'*40
v.top.each do |k, v|
puts v.to_s.rjust(6) << ' ' << k.to_s
end
else
puts k.to_s.rjust(6) << ' ' << v.to_s
end
puts "\n"
end
end
puts "\n"*2
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment