Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- template: ci/cargo-check.yml
parameters:
name: minrust
rust_version: 1.31.0 # The 2018 edition
rust_version: 1.37.0
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is temporary for CI

displayName: Check rust min ver

################
Expand Down
3 changes: 2 additions & 1 deletion console/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ edition = "2018"
[dependencies]
bytes = "0.4"
crossbeam = "0.7.1"
crossterm = "^0.9"
crossterm = "0.10"
failure = "0.1.5"
futures = "0.1"
http = "0.1"
Expand All @@ -23,6 +23,7 @@ tower-service = "0.2"
tower-util = "0.1"
regex = "1.2.0"
indexmap = "1.0.2"
itertools = "0.8.0"

[dependencies.tui]
version = "0.6"
Expand Down
172 changes: 166 additions & 6 deletions console/src/filter/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,70 @@
use crate::storage::EventEntry;
use crate::storage::{messages, EventEntry, Span, Store};

use std::fmt::{Display, Formatter, Result};

use indexmap::IndexMap;
use itertools::Itertools;

use messages::{value::Value, ValueContainer};
use regex::Regex;

#[derive(Clone, PartialEq)]
pub(crate) enum Entries {
Grouped {
group_by: GroupBy,
groups: Vec<(Option<Value>, Entries)>,
},
Entries(Vec<usize>),
}

impl Entries {
pub fn len(&self) -> usize {
match self {
Entries::Entries(entries) => entries.len(),
Entries::Grouped { groups, .. } => {
groups
.iter()
.map(|(_, entries)| entries.len())
.sum::<usize>()
+ 1
}
}
}

fn retain(&mut self, f: impl Fn(&usize) -> bool + Copy) {
match self {
Entries::Entries(vec) => vec.retain(f),
Entries::Grouped { groups, .. } => {
groups.iter_mut().for_each(|(_, entries)| entries.retain(f))
}
}
}

fn group(self, key: impl Fn(&usize) -> Option<Value> + Copy, group_by: GroupBy) -> Entries {
match self {
Entries::Grouped { .. } => unimplemented!("Nested groups are not yet supported"),
Entries::Entries(mut entries) => {
entries.sort_by_key(key);
let groups = entries
.into_iter()
.filter(|id| key(id).is_some())
.group_by(key)
.into_iter()
.map(|(value, indices)| -> (Option<Value>, Entries) {
(value, Entries::Entries(indices.collect()))
})
.collect();
Entries::Grouped { group_by, groups }
}
}
}
}

#[derive(Clone, Debug, Default)]
pub(crate) struct Filter {
pub(crate) name: String,
pub(crate) modifier: IndexMap<String, Modifier>,
pub(crate) group_by: Option<GroupBy>,
}

impl Filter {
Expand All @@ -23,10 +78,115 @@ impl Filter {
);
}

pub(crate) fn filter(&self, entry: &EventEntry) -> bool {
self.modifier
.values()
.all(|m| m.filter(entry).unwrap_or(false))
pub(crate) fn group(&mut self, group_by: GroupBy) {
self.group_by = Some(group_by);
}

pub(crate) fn apply<'i>(&'i self, raw: &'i Store, mut entries: Entries) -> Entries {
entries.retain(|e| {
let event = &raw.events()[*e];
self.modifier
.values()
.all(|m| m.filter(event).unwrap_or(false))
});
if let Some(group_by) = &self.group_by {
entries = entries.group(
|e| {
let event = &raw.events()[*e];
match group_by {
GroupBy::Field(name) => event.event.value_by_name(&name).cloned(),
GroupBy::Span(selector) => selector.evaluate(raw, event),
}
},
group_by.clone(),
);
}
entries
}
}

#[derive(Clone, Debug, PartialEq)]
pub(crate) enum GroupBy {
Field(String),
Span(SpanSelector),
}

#[derive(Clone, Debug, PartialEq)]
pub(crate) enum SpanCriterion {
Field(String),
Id,
}

#[derive(Clone, Debug, PartialEq)]
pub(crate) enum SpanSelector {
SpanCriterion(SpanCriterion),
ParentByName {
name: String,
criterion: SpanCriterion,
},
}

impl SpanSelector {
fn evaluate(&self, store: &Store, entry: &EventEntry) -> Option<Value> {
let span_id = entry.span?;
let span = &store.spans()[span_id.0];
assert_eq!(span.id, span_id);
match self {
SpanSelector::ParentByName { name, criterion } => {
fn check_parent(
store: &Store,
span: &Span,
name: &String,
criterion: &SpanCriterion,
) -> Option<Value> {
let parent = &store.spans()[span.parent_id?.0];
if &parent.span.attributes.as_ref()?.metadata.as_ref()?.name == name {
match criterion {
SpanCriterion::Field(field_name) => {
parent.value_by_name(field_name).cloned()
}
SpanCriterion::Id => Some(Value::from_u64(parent.id.0 as u64)),
}
} else {
check_parent(store, parent, name, criterion)
}
}
check_parent(store, span, name, criterion)
}
SpanSelector::SpanCriterion(criterion) => match criterion {
SpanCriterion::Field(field_name) => span.value_by_name(field_name).cloned(),
SpanCriterion::Id => Some(Value::from_u64(span.id.0 as u64)),
},
}
}
}

impl Display for SpanCriterion {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
SpanCriterion::Field(name) => write!(f, "field.{}", name),
SpanCriterion::Id => write!(f, "id"),
}
}
}

impl Display for SpanSelector {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
SpanSelector::SpanCriterion(crit) => write!(f, "{}", crit),
SpanSelector::ParentByName { name, criterion } => {
write!(f, r#"parent_by_name("{}").{}"#, name, criterion)
}
}
}
}

impl Display for GroupBy {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
GroupBy::Field(name) => write!(f, "event.group_by.field.{}", name),
GroupBy::Span(selector) => write!(f, "event.group_by.span.{}", selector),
}
}
}

Expand Down Expand Up @@ -108,7 +268,7 @@ impl Modifier {

#[cfg(test)]
mod tests {
use super::*;
use super::Modifier;

use crate::storage::*;

Expand Down
104 changes: 89 additions & 15 deletions console/src/storage/messages.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,46 @@
//! Types generated by gRPC "/proto/tracing.proto"
use std::{
cmp::Ordering,
fmt::Result,
fmt::{Display, Formatter},
};

include!(concat!(env!("OUT_DIR"), "/tracing.rs"));

impl Event {
pub fn value_by_name(&self, name: &str) -> Option<&value::Value> {
for value in &self.values {
if &value.field.as_ref()?.name == name {
return value.value.as_ref();
}
}
None
}
pub trait ValueContainer {
fn value_by_name(&self, name: &str) -> Option<&value::Value>;

pub fn str_by_name(&self, name: &str) -> Option<&str> {
fn str_by_name(&self, name: &str) -> Option<&str> {
match self.value_by_name(name)? {
value::Value::Str(string) => Some(string),
_ => None,
}
}
pub fn debug_by_name(&self, name: &str) -> Option<&DebugRecord> {
fn debug_by_name(&self, name: &str) -> Option<&DebugRecord> {
match self.value_by_name(name)? {
value::Value::Debug(debug) => Some(debug),
_ => None,
}
}
pub fn signed_by_name(&self, name: &str) -> Option<i64> {
fn signed_by_name(&self, name: &str) -> Option<i64> {
match self.value_by_name(name)? {
value::Value::Signed(signed) => Some(*signed),
_ => None,
}
}
pub fn unsigned_by_name(&self, name: &str) -> Option<u64> {
fn unsigned_by_name(&self, name: &str) -> Option<u64> {
match self.value_by_name(name)? {
value::Value::Unsigned(unsigned) => Some(*unsigned),
_ => None,
}
}
pub fn bool_by_name(&self, name: &str) -> Option<bool> {
fn bool_by_name(&self, name: &str) -> Option<bool> {
match self.value_by_name(name)? {
value::Value::Boolean(boolean) => Some(*boolean),
_ => None,
}
}
pub fn any_by_name(&self, name: &str) -> Option<String> {
fn any_by_name(&self, name: &str) -> Option<String> {
Some(match self.value_by_name(name)? {
value::Value::Str(string) => string.clone(),
value::Value::Signed(i) => format!("{}", i),
Expand All @@ -51,3 +50,78 @@ impl Event {
})
}
}

impl ValueContainer for Event {
fn value_by_name(&self, name: &str) -> Option<&value::Value> {
for value in &self.values {
if &value.field.as_ref()?.name == name {
return value.value.as_ref();
}
}
None
}
}

impl ValueContainer for NewSpan {
fn value_by_name(&self, name: &str) -> Option<&value::Value> {
for value in &self.values {
if &value.field.as_ref()?.name == name {
return value.value.as_ref();
}
}
None
}
}

impl ValueContainer for Record {
fn value_by_name(&self, name: &str) -> Option<&value::Value> {
for value in &self.values {
if &value.field.as_ref()?.name == name {
return value.value.as_ref();
}
}
None
}
}

type V = value::Value;

impl V {
pub fn from_u64(u: u64) -> V {
value::Value::Unsigned(u)
}
}

impl Eq for V {}

impl Ord for V {
fn cmp(&self, other: &Self) -> Ordering {
// Unordered or wrongly ordered Values will be moved to the end
self.partial_cmp(other).unwrap_or(Ordering::Greater)
}
}

impl PartialOrd<V> for V {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (&self, &other) {
(V::Signed(s), V::Signed(o)) => s.partial_cmp(o),
(V::Unsigned(s), V::Unsigned(o)) => s.partial_cmp(o),
(V::Boolean(s), V::Boolean(o)) => s.partial_cmp(o),
(V::Str(s), V::Str(o)) => s.partial_cmp(o),
(V::Debug(s), V::Debug(o)) => s.debug.partial_cmp(&o.debug),
_ => None,
}
}
}

impl Display for V {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
V::Unsigned(u) => write!(f, "{}", u),
V::Signed(s) => write!(f, "{}", s),
V::Boolean(b) => write!(f, "{}", b),
V::Str(s) => write!(f, "{}", s),
V::Debug(d) => write!(f, "{}", d.debug),
}
}
}
Loading