Created
November 9, 2017 16:47
-
-
Save kbabioch/be5c70ea48ddfc6fc508adad6f2db576 to your computer and use it in GitHub Desktop.
Analyzes mail log files from Postfix (in Go)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"flag" | |
"fmt" | |
"os" | |
"regexp" | |
"strconv" | |
"time" | |
) | |
const ( | |
EXIT_OK int = 0 | |
EXIT_WARNING int = 1 | |
EXIT_CRITICAL int = 2 | |
EXIT_UNKNOWN int = 3 | |
) | |
func main() { | |
// Setup command line arguments | |
optw := flag.String("w", "0,0,0", "warning thresholds for sent, received and picked up mail") | |
optc := flag.String("c", "0,0,0", "critical thresholds for sent, received and picked up mail") | |
optf := flag.String("F", "/var/log/mail.log", "destination of mail log file") | |
optp := flag.Int("p", 1, "period (in hours) to analyze") | |
// Parse command line arguments | |
flag.Parse() | |
// Split and parse warning thresholds | |
var sw, rw, pw int | |
if argc, err := fmt.Sscanf(*optw, "%d,%d,%d", &sw, &rw, &pw); argc != 3 || err != nil { | |
flag.PrintDefaults() | |
os.Exit(EXIT_UNKNOWN) | |
} | |
// Split and parse criticial thresholds | |
var sc, rc, pc int | |
if argc, err := fmt.Sscanf(*optc, "%d,%d,%d", &sc, &rc, &pc); argc != 3 || err != nil { | |
flag.PrintDefaults() | |
os.Exit(EXIT_UNKNOWN) | |
} | |
// Counter for sent, received and picked up mail | |
var s, r, p int | |
// Open mail log | |
f, err := os.Open(*optf) | |
if err != nil { | |
fmt.Println(err) | |
os.Exit(EXIT_UNKNOWN) | |
} | |
// Current time | |
now := time.Now() | |
// Regular expression to match log entries and extract interesting fields as groups | |
r1 := regexp.MustCompile("([A-Z][a-z]{2}) +([0-9]{1,2}) ([0-9]{2}):([0-9]{2}):([0-9]{2}) (.*)") | |
r2 := regexp.MustCompile("smtp.*status=sent") | |
r3 := regexp.MustCompile("smtpd.*client=") | |
r4 := regexp.MustCompile("pickup.*(sender|uid)=") | |
// Static map for translation of month names to int | |
months := map[string]time.Month { | |
"Jan": time.January, | |
"Feb": time.February, | |
"Mar": time.March, | |
"Apr": time.April, | |
"May": time.May, | |
"Jun": time.June, | |
"Jul": time.July, | |
"Aug": time.August, | |
"Sep": time.September, | |
"Oct": time.October, | |
"Nov": time.November, | |
"Dec": time.December, | |
} | |
// Read file line by line | |
scanner := bufio.NewScanner(f) | |
for scanner.Scan() { | |
var result = r1.FindStringSubmatch(scanner.Text()) | |
// TODO Use time.Parse() + time.Add() (for year/tz) instead? | |
month := months[result[1]] | |
day, _ := strconv.Atoi(result[2]) | |
hour, _ := strconv.Atoi(result[3]) | |
minute, _ := strconv.Atoi(result[4]) | |
second, _ := strconv.Atoi(result[5]) | |
// Get time for this entry | |
ld := time.Date(now.Year(), month, day, hour, minute, second, 0, now.Location()) | |
// Check whether entry is in the future (something is wrong?) | |
if ld.After(now) { | |
continue | |
} | |
// Only consider lines that are covered by the specified period | |
if ld.Add(time.Duration(*optp) * time.Hour).Before(now) { | |
continue | |
} | |
if r2.MatchString(result[6]) { | |
s++; | |
} else if r3.MatchString(result[6]) { | |
r++; | |
} else if r4.MatchString(result[6]) { | |
p++; | |
} | |
} | |
// Output stats | |
fmt.Printf("Sent: %d, received: %d, picked up: %d\n", s, r, p) | |
// Check if any of the critical thresholds has been reached | |
if (sc > 0 && s > sc) || (rc > 0 && r > rc) || (pc > 0 && p > pc) { | |
os.Exit(EXIT_CRITICAL) | |
// Check if any of the warning thresholds has been reached | |
} else if (sw > 0 && s > sw) || (rw > 0 && r > rw) || (pw > 0 && p > pw) { | |
os.Exit(EXIT_WARNING) | |
} | |
// No thresholds reached, exit normally | |
os.Exit(EXIT_OK) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment