1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
//! [Fakelogs](https://gitlab.com/ufoot/fakelogs) is a random log generator.
//! It can be used for load testing of log parsers.
//!
//! It is written in [Rust](https://www.rust-lang.org/)
//! and is mostly a toy project to ramp up on the language.
//! It might however be useful. Use at your own risk.
//!
//! ![Fakelogs icon](https://gitlab.com/ufoot/fakelogs/raw/master/fakelogs.png)

pub mod generator;
pub mod options;

extern crate chrono;
extern crate rand;
extern crate ticker;

use rand::Rng;
use std::borrow::BorrowMut;
use std::io;
use std::io::Error;
use std::time::Duration;
use ticker::Ticker;

fn header(cfg: &options::Config) -> Result<(), std::io::Error> {
    if !cfg.header {
        return Ok(());
    }
    let stdout = io::stdout();
    let mut handle = stdout.lock();
    match if cfg.csv_output {
        generator::write_csv_header(&mut handle.borrow_mut())
    } else {
        generator::write_common_log_header(&mut handle.borrow_mut())
    } {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}

fn chunk(i: impl Iterator<Item = i64>, cfg: &options::Config) -> Result<(), std::io::Error> {
    for _ in i {
        let l = generator::Line::random(&cfg);
        let stdout = io::stdout();
        let mut handle = stdout.lock();
        let r: Result<usize, Error>;
        if cfg.csv_output {
            r = l.write_csv(handle.borrow_mut());
        } else {
            r = l.write_common_log(handle.borrow_mut());
        }
        r?;
    }
    Ok(())
}

///  Run the generator with the given options.
///
/// ```no_run
/// use fakelogs::{options, run};
///
/// run(&options::Config::parse());
/// ```
pub fn run(cfg: &options::Config) {
    const CHUNK_SECS: i64 = 5;
    const BURST_FREQ: f64 = 0.4;

    let mut freq: f64 = cfg.lines_per_second as f64;
    if freq < 1.0 {
        freq = 1.0;
    }
    let b_freq = if cfg.burst { BURST_FREQ } else { 1.0 };
    let mut d_micros = 1000000.0 * b_freq / freq;
    if d_micros < 1.0 {
        d_micros = 1.0;
    }
    let n = CHUNK_SECS * 1000000 / (d_micros as i64);
    let d = Duration::from_micros(d_micros as u64);

    match header(cfg) {
        Ok(()) => {}
        Err(e) => {
            eprintln!("{}", e);
            return;
        }
    }

    loop {
        let mut rng = rand::thread_rng();
        match if (!cfg.burst) || rng.gen_bool(BURST_FREQ) {
            chunk(Ticker::new(0..n, d).into_iter(), &cfg)
        } else {
            chunk(
                Ticker::new(0..CHUNK_SECS, Duration::from_secs(1)).into_iter(),
                &cfg,
            )
        } {
            Ok(()) => {}
            Err(e) => {
                eprintln!("{}", e);
                break;
            }
        }
    }
}