Skip to main content

freeswitch_log_parser/
lib.rs

1//! Parser for FreeSWITCH log files.
2//!
3//! Handles the full complexity of `mod_logfile` output: five distinct line
4//! formats, multi-line CHANNEL_DATA and SDP dumps, truncated buffer collisions,
5//! and per-session state tracking — no regex, a single dependency
6//! (`freeswitch-types`).
7//!
8//! # Architecture
9//!
10//! The parser is organized in three composable layers, each wrapping the previous:
11//!
12//! - **Layer 1** ([`parse_line`]) — stateless, zero-allocation single-line classifier
13//! - **Layer 2** ([`LogStream`]) — structural state machine that groups continuations,
14//!   classifies messages, and detects multi-line blocks
15//! - **Layer 3** ([`SessionTracker`]) — per-UUID state machine that propagates
16//!   dialplan context, channel state, and variables across entries; extensible
17//!   via [`SessionTracker::with_pre_hook`] and [`SessionTracker::with_post_hook`]
18//!   for custom leg detection
19//!
20//! See `docs/design-rationale.md` in the repository for the parsing strategy
21//! and why each layer exists; the line-format anatomy lives in the repository's
22//! CLAUDE.md.
23//!
24//! # Examples
25//!
26//! Read lines from stdin, process through all three layers, and print enriched entries:
27//!
28//! ```no_run
29//! use std::io;
30//! use freeswitch_log_parser::{read_log_lines, LogStream, SessionTracker};
31//!
32//! // read_log_lines tolerates mod_logfile's truncated codepoints; the strict
33//! // BufRead::lines() reader would panic on them.
34//! let lines = read_log_lines(io::stdin().lock()).map(|d| d.expect("read error").text);
35//! let stream = LogStream::new(lines);
36//! let mut tracker = SessionTracker::new(stream);
37//!
38//! for enriched in tracker.by_ref() {
39//!     let e = &enriched.entry;
40//!     println!("{} [{}] {}", e.timestamp, e.message_kind, e.message);
41//! }
42//!
43//! let stats = tracker.stats();
44//! eprintln!("{} lines, {} unclassified",
45//!     stats.lines_processed, stats.lines_unclassified);
46//! ```
47//!
48//! # Feature flags
49//!
50//! - **`cli`** — enables the `fslog` binary with clap, xz decompression, and regex filtering
51
52mod attached;
53mod chain;
54mod codec;
55mod decode;
56mod level;
57mod line;
58mod message;
59mod peer;
60mod session;
61mod stamp;
62mod stream;
63mod uuid;
64
65pub use attached::{AttachedLines, AttachedLinesIter};
66pub use chain::{SegmentTracker, TrackedChain};
67pub use codec::{CodecMedia, CodecOffer, CodecParseError};
68pub use decode::{
69    classify_utf8, decode_log_line, read_log_lines, truncate_at_char_boundary, DecodedLine,
70    Utf8Decode,
71};
72pub use freeswitch_types::{
73    variables::{ConferenceVariable, SofiaVariable, VariableName},
74    CallDirection, CallState, ChannelState, ChannelVariable,
75};
76pub use level::{LogLevel, ParseLevelError};
77pub use line::{parse_line, LineKind, RawLine};
78pub use message::{classify_message, DtmfSource, MessageKind, SdpDirection, SipInviteDirection};
79pub use peer::{
80    for_each_peer_uuid, for_each_peer_uuid_with, is_peer_uuid_var, LOOPBACK_PEER_UUID_VARS,
81    PEER_UUID_VARS,
82};
83pub use session::{
84    conference::ConferenceMembership,
85    media::{MediaCodecs, SessionMedia},
86    parse_bridge_args, BridgeInfo, EnrichedEntry, SessionSnapshot, SessionState, SessionTracker,
87};
88pub use stamp::{log_rotation_stamp, normalize_entry_timestamp};
89pub use stream::{
90    Block, LogEntry, LogStream, ParseStats, UnclassifiedLine, UnclassifiedReason,
91    UnclassifiedTracking,
92};
93pub use uuid::{find_uuids, is_uuid, FindUuids};