fail2ban-log-parser-core 0.1.0

Fast, zero-copy fail2ban log parser built with winnow
Documentation

Fail2Ban log parser

[!IMPORTANT] This library is still WIP.

Installation

cargo add fail2ban-log-parser-core

With serde support:

cargo add fail2ban-log-parser-core --features serde

Usage

Parse a single line

use fail2ban_log_parser_core::parse;

let line = "2024-01-15 14:32:01,847 fail2ban.filter [12345] INFO [sshd] Found 192.168.1.1";
let log = parse(line).next().unwrap().unwrap();

assert_eq!(log.jail(), Some("sshd"));
assert_eq!(log.pid(), Some(12345));
println!("{:?} {:?} {:?}", log.event(), log.ip(), log.timestamp());

Parse a batch and handle errors

use fail2ban_log_parser_core::parse;

let input = "\
2024-01-15 14:32:01,847 fail2ban.filter [12345] INFO [sshd] Found 192.168.1.100
this line is not a valid log entry
2024-01-15 14:32:03,456 fail2ban.actions [12345] NOTICE [sshd] Ban 192.168.1.100";

for result in parse(input) {
    match result {
        Ok(log) => println!("{:?} {:?}", log.event(), log.ip()),
        Err(e) => eprintln!("parse error: {e}"),
    }
}

Filter specific events

use fail2ban_log_parser_core::{Fail2BanEvent, parse};

let input = "..."; // multi-line log
let bans: Vec<_> = parse(input)
    .filter_map(|r| r.ok())
    .filter(|log| log.event() == Some(&Fail2BanEvent::Ban))
    .collect();

Log structure

2024-01-15 14:32:01,847  fail2ban.filter  [12345]  INFO  [sshd] Found 1.2.3.4
|___________________|    |______________|  |____|   |__|  |____| |__| |______|
    timestamp              header            pid    level  jail  event  IP address

Supported formats

Field Formats
Timestamp 2024-01-15 14:32:01,847, Jan 15 2024 14:32:01,847, 2024-01-15T14:32:01,847Z, ±HH:MM offset
Header fail2ban.filter, fail2ban.actions, fail2ban.server
Level INFO, NOTICE, WARNING, ERROR, DEBUG (case-insensitive)
Event Found, Ban, Unban, Restore, Ignore, AlreadyBanned, Failed, Unknown
IP IPv4 (192.168.1.1) and IPv6 (2001:db8::1)

API

Type Description
parse(&str) Returns a lazy Iterator<Item = Result<Fail2BanStructuredLog, ParseError>>
Fail2BanStructuredLog Parsed log line with accessor methods: timestamp(), header(), pid(), level(), jail(), event(), ip()
Fail2BanEvent Enum: Found, Ban, Unban, Restore, Ignore, AlreadyBanned, Failed, Unknown
Fail2BanHeaderType Enum: Filter, Actions, Server
Fail2BanLevel Enum: Info, Notice, Warning, Error, Debug
ParseError Contains line_number: usize and line: String

Features

Feature Description
serde Enables Serialize/Deserialize on all public types
debug_errors Extra error debugging information

Examples

cargo run -p fail2ban-log-parser-core --example parse_single
cargo run -p fail2ban-log-parser-core --example parse_batch
cargo run -p fail2ban-log-parser-core --example filter_bans

How it works

flowchart TD
    A["&str (multi-line log)"] --> B["parse()"]
    B --> C["Split into lines"]
    C --> D["parse_log_line() per line"]

    D --> T["timestamp"]
    D --> H["header"]
    D --> P["pid"]
    D --> L["level"]
    D --> J["jail"]
    D --> E["event"]
    D --> I["ip"]

    T & H & P & L & J & E & I --> R{Result}
    R -->|Ok| S["Fail2BanStructuredLog"]
    R -->|Err| F["ParseError"]

    S & F --> O["Iterator of Results"]

Benchmarks

Measured with Criterion.rs + dhat on Apple M4 Pro / 48 GB / rustc 1.94.0.

Single-line parsing

Variant Time
ISO date + IPv4 ~138 ns
Syslog date ~145 ns
ISO 8601 (T-separator) ~141 ns
IPv6 address ~158 ns

Batch parsing (throughput)

Lines Time Per line
10 ~1.57 µs ~157 ns
100 ~16.4 µs ~164 ns
1,000 ~165 µs ~165 ns
10,000 ~1.66 ms ~166 ns
100,000 ~16.7 ms ~167 ns
1,000,000 ~167 ms ~167 ns

Collection strategies (1,000 lines)

Strategy Time
Iterate + count ~166 µs
Collect to Vec ~171 µs
Partition ok/err ~170 µs

Error handling

Scenario Time
1,000 lines (50% invalid) ~94 µs
All 8 event types (8 lines) ~1.31 µs

Memory usage (collect to Vec)

Lines Total allocated Peak in-use Allocs Per line
1 260 B 260 B 2 260 B
100 16.14 KB 8.39 KB 106 165 B
1,000 131.66 KB 67.91 KB 1,009 134 B
10,000 2.04 MB 1.04 MB 10,013 213 B
100,000 16.38 MB 8.38 MB 100,016 171 B
1,000,000 131.81 MB 67.81 MB 1,000,019 138 B

To reproduce:

cargo bench -p fail2ban-log-parser-core --bench parsing