gst-log-parser 0.5.0

Crate providing a convenient API to parse GStreamer logs
Documentation
// Copyright (C) 2017-2019 Guillaume Desmottes <guillaume@desmottes.be>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::collections::HashMap;
use std::fs::File;
use std::process::exit;

use colored::*;
use gst_log_parser::parse;
use gstreamer::ClockTime;
use itertools::Itertools;
use structopt::StructOpt;

#[derive(StructOpt)]
#[structopt(
    name = "ts-diff",
    about = "Display the timestamp difference between the previous entry from the thread"
)]
struct Opt {
    #[structopt(help = "Input log file")]
    input: String,
    #[structopt(
        short = "p",
        help = "Percentage of the longest entries to highlight",
        default_value = "1"
    )]
    top: usize,
    #[structopt(short = "s", help = "Sort by decreasing ts difference")]
    sort: bool,
}

struct TsEntry {
    entry: gst_log_parser::Entry,
    diff: ClockTime,
    top: bool,
}

impl TsEntry {
    fn new(entry: gst_log_parser::Entry, diff: ClockTime) -> TsEntry {
        TsEntry {
            entry,
            diff,
            top: false,
        }
    }

    fn new_top(e: TsEntry) -> TsEntry {
        TsEntry {
            entry: e.entry,
            diff: e.diff,
            top: true,
        }
    }
}

fn generate() -> Result<bool, std::io::Error> {
    let opt = Opt::from_args();
    let input = File::open(opt.input)?;

    let parsed = parse(input);
    let mut previous: HashMap<String, ClockTime> = HashMap::new();

    // Compute ts diff
    let entries = parsed.map(|entry| {
        let diff = match previous.get(&entry.thread) {
            Some(p) => entry.ts - *p,
            None => ClockTime::from_seconds(0),
        };

        previous.insert(entry.thread.clone(), entry.ts);

        TsEntry::new(entry, diff)
    });

    // Sort by ts diff
    let entries = entries.sorted_by(|a, b| Ord::cmp(&b.diff, &a.diff));

    // Mark the top entries
    let n = entries.len() * opt.top / 100;

    let entries = entries
        .into_iter()
        .enumerate()
        .map(|(i, e)| if i < n { TsEntry::new_top(e) } else { e });

    let entries = if opt.sort {
        // Sort by decreasing diff
        entries.sorted_by(|a, b| Ord::cmp(&b.diff, &a.diff))
    } else {
        // Sort by increasing ts
        entries.sorted_by(|a, b| Ord::cmp(&a.entry.ts, &b.entry.ts))
    };

    // Display
    for e in entries {
        let diff = {
            if e.top {
                e.diff.to_string().red().to_string()
            } else {
                e.diff.to_string()
            }
        };

        println!(
            "{} ({}) {} {:?} {} {}:{}:{}:<{}> {}",
            e.entry.ts,
            diff,
            e.entry.thread,
            e.entry.level,
            e.entry.category,
            e.entry.file,
            e.entry.line,
            e.entry.function,
            e.entry.object.clone().unwrap_or_default(),
            e.entry.message
        );
    }

    Ok(true)
}

fn main() {
    if generate().is_err() {
        exit(1);
    }
}