freeswitch_log_parser/lib.rs
1//! Parser for FreeSWITCH log files.
2//!
3//! Handles the full complexity of `mod_logfile` output: the six [`LineKind`]
4//! shapes, 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//! All off by default; docs.rs builds with all of them on.
51//!
52//! - **`sdp`** — `Block::sdp_codecs`, which parses an SDP body through `freeswitch-types`
53//! - **`cli`** — the `fslog` binary: clap, xz decompression, regex filtering. Enables `sdp`
54//! - **`tui`** — `fslog monitor`, the live call table. Enables `cli`
55//! - **`fixtures`** — the production-log test suite, which requires a corpus in
56//! `tests/fixtures/` and fails rather than skips without one. Affects tests only
57
58mod attached;
59mod chain;
60mod codec;
61mod decode;
62mod fields;
63mod line;
64mod mask;
65mod message;
66mod peer;
67mod session;
68mod stamp;
69mod stream;
70#[cfg(test)]
71mod testdata;
72mod uuid;
73
74pub use attached::{AttachedLines, AttachedLinesIter, AttachedOverflow};
75pub use chain::{SegmentTracker, TrackedChain};
76pub use codec::{CodecMedia, CodecOffer, CodecParseError};
77pub use decode::{
78 classify_utf8, decode_log_line, read_log_line_capped, read_log_lines, read_log_lines_capped,
79 trim_capped_tail, truncate_at_char_boundary, CappedLine, DecodedLine, LineRead, OverCap,
80 Utf8Decode,
81};
82pub use fields::{
83 apply_fields, message_fields, Field, FieldKind, FieldLocation, RenderError, RenderedEntry,
84};
85pub use freeswitch_types::{
86 variables::{ConferenceVariable, SofiaVariable, VariableName},
87 CallDirection, CallState, ChannelState, ChannelVariable, HangupCause, LogLevel,
88 ParseLogLevelError,
89};
90pub use line::{level_from_bracketed, parse_line, LineKind, RawLine};
91pub use message::{
92 classify_message, regex_condition_parts, DtmfSource, LifecycleEvent, MessageKind,
93 RegexCondition, SdpDirection, SipInviteDirection, VarName,
94};
95pub use peer::{
96 for_each_peer_uuid, for_each_peer_uuid_with, LOOPBACK_PEER_UUID_VARS, PEER_UUID_VARS,
97};
98pub use session::{
99 conference::ConferenceMembership,
100 media::{CodecImpl, MediaCodecs, SessionMedia},
101 parse_bridge_args, BridgeInfo, EnrichedEntry, SessionSnapshot, SessionState, SessionTracker,
102};
103pub use stamp::{
104 log_rotation_stamp, normalize_entry_timestamp, stamp_lower_bound, stamp_upper_bound,
105};
106pub use stream::{
107 Block, LogEntry, LogStream, ParseStats, ParseWarning, SessionReading, UnclassifiedLine,
108 UnclassifiedReason, UnclassifiedTracking,
109};
110pub use uuid::{find_uuids, is_uuid, FindUuids};