group by user agent
Some checks failed
CI / build (push) Successful in 50s
CI / checks (push) Failing after 2m9s
CI / build-nixpkgs (push) Has been skipped

This commit is contained in:
Robin Appelman 2025-08-16 23:09:42 +02:00
commit cbdc59d591
3 changed files with 44 additions and 0 deletions

View file

@ -3,12 +3,14 @@ mod remote;
mod unique; mod unique;
mod url; mod url;
mod user; mod user;
mod useragent;
use crate::app::{Filter, LineSet}; use crate::app::{Filter, LineSet};
use crate::grouping::app::{AppGrouping, APP_GROUPING_UI}; use crate::grouping::app::{AppGrouping, APP_GROUPING_UI};
use crate::grouping::remote::{RemoteGrouping, REMOTE_GROUPING_UI}; use crate::grouping::remote::{RemoteGrouping, REMOTE_GROUPING_UI};
use crate::grouping::url::{UrlGrouping, URL_GROUPING_UI}; use crate::grouping::url::{UrlGrouping, URL_GROUPING_UI};
use crate::grouping::user::{UserGrouping, USER_GROUPING_UI}; use crate::grouping::user::{UserGrouping, USER_GROUPING_UI};
use crate::grouping::useragent::{UserAgentGrouping, USER_AGENT_GROUPING_UI};
use crate::logfile::LogLine; use crate::logfile::LogLine;
use crate::matcher::MatchResult; use crate::matcher::MatchResult;
use crate::timegraph::{SparkLine, TimeGraph}; use crate::timegraph::{SparkLine, TimeGraph};
@ -132,6 +134,7 @@ pub enum Groupings<'logs> {
App(AppGrouping<'logs>), App(AppGrouping<'logs>),
User(UserGrouping<'logs>), User(UserGrouping<'logs>),
Remote(RemoteGrouping<'logs>), Remote(RemoteGrouping<'logs>),
UserAgent(UserAgentGrouping<'logs>),
} }
impl<'logs> GroupingResult<'logs> for Groupings<'logs> { impl<'logs> GroupingResult<'logs> for Groupings<'logs> {
@ -142,6 +145,7 @@ impl<'logs> GroupingResult<'logs> for Groupings<'logs> {
Groupings::App(r) => r.matches(filter), Groupings::App(r) => r.matches(filter),
Groupings::User(r) => r.matches(filter), Groupings::User(r) => r.matches(filter),
Groupings::Remote(r) => r.matches(filter), Groupings::Remote(r) => r.matches(filter),
Groupings::UserAgent(r) => r.matches(filter),
} }
} }
@ -154,6 +158,7 @@ impl<'logs> GroupingResult<'logs> for Groupings<'logs> {
Groupings::App(r) => Box::new(r.render()), Groupings::App(r) => Box::new(r.render()),
Groupings::User(r) => Box::new(r.render()), Groupings::User(r) => Box::new(r.render()),
Groupings::Remote(r) => Box::new(r.render()), Groupings::Remote(r) => Box::new(r.render()),
Groupings::UserAgent(r) => Box::new(r.render()),
} }
} }
} }
@ -164,6 +169,7 @@ pub enum GroupingOptions {
App, App,
User, User,
Remote, Remote,
UserAgent,
} }
impl GroupingOptions { impl GroupingOptions {
@ -173,6 +179,7 @@ impl GroupingOptions {
GroupingOptions::App, GroupingOptions::App,
GroupingOptions::User, GroupingOptions::User,
GroupingOptions::Remote, GroupingOptions::Remote,
GroupingOptions::UserAgent,
] ]
.into_iter() .into_iter()
} }
@ -184,6 +191,7 @@ impl GroupingOptions {
GroupingOptions::App => line.app.hash(&mut hasher), GroupingOptions::App => line.app.hash(&mut hasher),
GroupingOptions::User => line.user.hash(&mut hasher), GroupingOptions::User => line.user.hash(&mut hasher),
GroupingOptions::Remote => line.remote.hash(&mut hasher), GroupingOptions::Remote => line.remote.hash(&mut hasher),
GroupingOptions::UserAgent => line.user_agent.hash(&mut hasher),
} }
hasher.finish() hasher.finish()
} }
@ -202,6 +210,9 @@ impl GroupingOptions {
GroupingOptions::Remote => Groupings::Remote(RemoteGrouping { GroupingOptions::Remote => Groupings::Remote(RemoteGrouping {
remote: line.remote.as_str(), remote: line.remote.as_str(),
}), }),
GroupingOptions::UserAgent => Groupings::UserAgent(UserAgentGrouping {
user_agent: line.user_agent.as_ref(),
}),
} }
} }
@ -211,6 +222,7 @@ impl GroupingOptions {
GroupingOptions::App => "App", GroupingOptions::App => "App",
GroupingOptions::User => "User", GroupingOptions::User => "User",
GroupingOptions::Remote => "Remote", GroupingOptions::Remote => "Remote",
GroupingOptions::UserAgent => "User Agent",
} }
} }
@ -220,6 +232,7 @@ impl GroupingOptions {
GroupingOptions::App => APP_GROUPING_UI, GroupingOptions::App => APP_GROUPING_UI,
GroupingOptions::User => USER_GROUPING_UI, GroupingOptions::User => USER_GROUPING_UI,
GroupingOptions::Remote => REMOTE_GROUPING_UI, GroupingOptions::Remote => REMOTE_GROUPING_UI,
GroupingOptions::UserAgent => USER_AGENT_GROUPING_UI,
} }
} }

29
src/grouping/useragent.rs Normal file
View file

@ -0,0 +1,29 @@
use crate::app::Filter;
use crate::grouping::{GroupingResult, GroupingUi};
use ratatui::layout::{Alignment, Constraint};
use std::borrow::Cow;
#[derive(PartialEq, Clone)]
pub struct UserAgentGrouping<'a> {
pub user_agent: &'a str,
}
pub const USER_AGENT_GROUPING_UI: GroupingUi = GroupingUi {
header: &[("User Agent", Alignment::Left)],
widths: &[Constraint::Percentage(100)],
};
impl<'a> GroupingResult<'a> for UserAgentGrouping<'a> {
fn matches(&self, filter: &Filter) -> bool {
if filter.is_empty() {
return true;
}
filter
.parts()
.all(|filter_part| filter_part.is_match(self.user_agent))
}
fn render(&self) -> impl Iterator<Item = Cow<'a, str>> {
[self.user_agent.into()].into_iter()
}
}

View file

@ -43,6 +43,8 @@ pub struct LogLine<'a> {
pub message: Cow<'a, str>, pub message: Cow<'a, str>,
pub exception: Option<Exception<'a>>, pub exception: Option<Exception<'a>>,
pub app: Cow<'a, str>, pub app: Cow<'a, str>,
#[serde(rename = "userAgent")]
pub user_agent: Cow<'a, str>,
#[serde(with = "date")] #[serde(with = "date")]
pub time: OffsetDateTime, pub time: OffsetDateTime,
} }