freeswitch-log-parser 0.4.3

Parser for FreeSWITCH log files — handles compressed .xz files, multi-line dumps, truncated buffers, and stateful UUID/timestamp tracking
Documentation

freeswitch-log-parser

CI Tests crates.io docs.rs license

Rust library for parsing FreeSWITCH log files. Three-layer streaming architecture, no regex, single runtime dependency (freeswitch-types for typed enums).

Layers

Layer 1: parse_line()       &str -> RawLine        (stateless, zero-alloc)
Layer 2: LogStream          Iterator -> LogEntry    (structural state machine)
Layer 3: SessionTracker     LogStream -> EnrichedEntry (per-UUID state)

Layer 1 classifies individual log lines into five formats (Full, System, UuidContinuation, BareContinuation, Truncated) and extracts positional fields (UUID, timestamp, log level, source, message).

Layer 2 groups continuation lines, detects block boundaries (CHANNEL_DATA dumps, SDP bodies), reassembles multi-line variable values, and classifies messages into semantic MessageKind variants: Execute, Dialplan, ChannelData, ChannelField, Variable, SdpMarker, StateChange, CodecNegotiation, Media, ChannelLifecycle, SipInvite, EventSocket, General, plus the synthetic FileChange/DateChange markers. SipInvite is the canonical sip_call_id ↔ channel_uuid correlation primitive — sofia emits it for every inbound and outbound call regardless of dialplan. Every entry carries both a typed Block and raw attached lines.

Layer 3 maintains per-UUID session state (dialplan context, channel state, learned variables, call direction, caller/destination numbers) and propagates it across entries. Also links bridged a-leg ↔ b-leg sessions via other_leg_uuid — from the explicit Peer UUID: suffix when present, or by matching a live b-leg channel_name when FreeSWITCH omits it. Yields EnrichedEntry with a SessionSnapshot alongside the raw LogEntry.

Each layer wraps the previous and can be used independently.

Performance

Built to handle the worst mod_logfile produces — 2 KiB buffer truncations, multi-line CHANNEL_DATA dumps with embedded SDP/XML, write-contention collisions. An 11 MB fixture (185 physical lines averaging ~60 KB each) parses in ~60 ms. LogEntry::attached uses a compact contiguous buffer (AttachedLines) instead of Vec<String> to amortize allocations on CHANNEL_DATA-heavy entries — iteration via &entry.attached works unchanged.

Usage

use std::io::{self, BufRead};
use freeswitch_log_parser::{LogStream, SessionTracker};

let lines = io::stdin().lock().lines().map(|l| l.unwrap());
let stream = LogStream::new(lines);

for enriched in SessionTracker::new(stream) {
    let entry = &enriched.entry;
    println!("{} {} {}", entry.uuid, entry.message_kind, entry.message);
    if let Some(session) = &enriched.session {
        if let Some(ctx) = &session.dialplan_context {
            println!("  context: {ctx}");
        }
    }
}

Unclassified data tracking

Lines that can't be fully classified are tracked, never silently dropped:

use freeswitch_log_parser::{LogStream, UnclassifiedTracking};

let mut stream = LogStream::new(lines)
    .unclassified_tracking(UnclassifiedTracking::TrackLines);

for entry in stream.by_ref() { /* ... */ }

let stats = stream.stats();
eprintln!("{} lines, {} unclassified",
    stats.lines_processed, stats.lines_unclassified);

Multi-file input with segment tracking

TrackedChain concatenates named input segments (typically rotated log files) into a single iterator and records where each segment starts. SegmentTracker then maps any line number back to its source file — useful for emitting FileChange markers or reporting errors with the original filename:

use freeswitch_log_parser::{LogStream, TrackedChain};

let segments = vec![
    ("freeswitch.log.1".into(), open_xz("freeswitch.log.1.xz")),
    ("freeswitch.log".into(), open_plain("freeswitch.log")),
];
let (chain, tracker) = TrackedChain::new(segments);
let stream = LogStream::new(chain);
// ... consume stream; query tracker.segment_for_line(n) as needed.

fslog binary

The crate also ships an fslog CLI for searching and tailing logs. Build with cargo build --release --features cli (search/filter) or --features tui (adds the monitor subcommand with a ratatui call table). The library itself has no CLI dependencies unless a feature is enabled.

License

LGPL-2.1-or-later