Skip to content

Commit

Permalink
Test
Browse files Browse the repository at this point in the history
  • Loading branch information
NasyrovYuri committed Oct 2, 2016
0 parents commit 60c84f1
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 0 deletions.
30 changes: 30 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
deps/
.DS_Store
*~
*#
*.o
*.so
*.swp
*.dylib
*.dSYM
*.dll
*.rlib
*.dummy
*.exe
*-test
/src/generated/mimes.txt
/src/generated/mimegen
/src/response/mimes/mod.rs
/bin/main
/bin/test-internal
/bin/test-external
/doc/
/target/
/build/
/.rust/
watch.sh
rusti.sh
/examples/*
!/examples/*.rs
Cargo.lock
.idea
18 changes: 18 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "dialog"
version = "0.1.0"
authors = ["y.nasyrov <[email protected]>"]
keywords = ["log", "file"]
license-file = "LICENSE"
readme = "README.md"
description = "A simple logger."

[dependencies]
log = "0.3"
log-panics = "1.0"

[workspace]
members = [ "crates/stream" ]

[dev-dependencies]
dialog-stream = { path = "crates/stream", version = "0.1.0" }
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Dialog - Logging for Rust
---

## Simply Example

```rust
extern crate dialog;
#[macro_use] extern crate log;

use dialog::Logger;

fn main() {

let logger = Logger::new();
logger.link(FileLoggerHandler::new());
logger.init().unwrap();
error!("something");
}
```

## Overview

Dialog it is the implementation of the chain of responsibilities for Log Crate. The main idea of the Dialog is that you use the macros of the Log Crate. Log add a record to the logger, it traverses the handler stack. Each handler decides whether it fully handled the record, and if so, the propagation of the record ends there.

Dialog is 100% safe code:

```sh
$ ack unsafe src | wc
0 0 0
```

The Dialog itself does not produce action with logs. Middleware are the main ways to extend Dialog with new functionality. Most extensions that would be provided by middleware in other repositories.

## Handlers
18 changes: 18 additions & 0 deletions crates/stream/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "dialog-stream"
version = "0.1.0"
authors = ["y.nasyrov <[email protected]>"]
description = "`io::Write` streamer for dialog"
keywords = ["log", "logging", "structured", "hierarchical", "stream"]
license-file = "LICENSE"
readme = "../../README.md"

[lib]
path = "lib.rs"

[dependencies]
dialog = { version = "0.1.0", path = "../.." }
rustc-serialize = "0.3"
backtrace = "0.2"
log = "0.3"
time = "0.1.34"
147 changes: 147 additions & 0 deletions crates/stream/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
extern crate dialog;
extern crate rustc_serialize;
extern crate backtrace;
extern crate log;
extern crate time;

use rustc_serialize::json::{self, Json, ToJson};
use std::collections::{BTreeMap, HashMap};
use std::sync::Mutex;
use std::fs::{OpenOptions};
use std::io::prelude::*;
use backtrace::Backtrace;
use std::sync::Arc;
use dialog::Handler;
use log::LogRecord;

struct LogJson {
level: String,
extra: MessageJson,
file: String,
program: String,
line: u32,
time: String,
trace: String
}

#[derive(RustcDecodable)]
pub struct MessageJson {
pub message: String,
pub description: String
}

impl ToJson for MessageJson {
fn to_json(&self) -> Json {
let mut d = BTreeMap::new();
d.insert("message".to_string(), self.message.to_json());
d.insert("description".to_string(), self.description.to_json());
Json::Object(d)
}
}

impl ToJson for LogJson {
fn to_json(&self) -> Json {
let mut d = BTreeMap::new();
d.insert("level".to_string(), self.level.to_json());
d.insert("extra".to_string(), self.extra.to_json());
d.insert("file".to_string(), self.file.to_json());
d.insert("program".to_string(), self.program.to_json());
d.insert("line".to_string(), self.line.to_json());
d.insert("time".to_string(), self.time.to_json());
d.insert("trace".to_string(), self.trace.to_json());
Json::Object(d)
}
}

pub struct StreamHandler {
channels: Arc<Mutex<HashMap<String, Vec<String>>>>
, last_time: Mutex<u64>
, count: usize
, delay: u64
, flush_type: String
, trace_types: Vec<String>
}

impl StreamHandler {
pub fn new() -> StreamHandler {
StreamHandler {
channels: Arc::new(Mutex::new(HashMap::new())),
last_time: Mutex::new(time::precise_time_ns()),
count: 100,
delay: 1000u64,
flush_type: "ERROR".to_string(),
trace_types: vec!("ERROR".to_string())
}
}

fn write(&self, msg: &LogJson) {
let mut channel = self.channels.lock().unwrap();

if !channel.contains_key(&msg.program.to_string()) {
channel.insert(msg.program.to_string(), Vec::new());
}

self.write_row(&mut channel, &msg);

if msg.level == self.flush_type {
for (channel_name, channel_row) in channel.iter_mut() {
self.flush(&channel_name.to_string(), channel_row);
}
}
}

fn write_row(&self, channel: &mut HashMap<String, Vec<String>>, msg: &LogJson) {
if let Some(res) = channel.get_mut(&msg.program.to_string()) {
res.push(msg.to_json().to_string());
let mut t = self.last_time.lock().unwrap();

if res.len() > self.count || time::precise_time_ns() - *t > self.delay * 1000000 {
self.flush(&msg.program.to_string(), res);
}

*t = time::precise_time_ns();
}
}

fn flush(&self, path: &str, res: &mut Vec<String>) {

if res.len() == 0 {
return;
}

if let Ok(mut f) = OpenOptions::new().write(true).create(true).append(true).open(format!("{}.{}", path, "txt")) {
for key in res.clone() {
f.write_all(&format!("{}{}" , key, "\n").into_bytes()).unwrap();
}
f.sync_all().unwrap();
res.truncate(0);
}
}
}

impl Handler for StreamHandler {
fn handle(&self, record: &LogRecord) -> Option<bool> {
let mut trace = String::new();

if self.trace_types.contains(&record.level().to_string()) {
trace = format!("{:?}", Backtrace::new());
}

let msg = LogJson {
level: record.level().to_string(),
extra: match json::decode(&record.args().to_string()) {
Ok(n) => n,
Err(_) => MessageJson{ message: format!("{}", record.args().to_string()), description: "".to_string() },
},
file: record.location().file().to_string(),
program: record.location().module_path().to_string(),
line: record.location().line(),
time: time::strftime(&"%FT%T%z".to_string(), &time::now()).unwrap(),
trace: trace
};

self.write(&msg);

Some(true)
}
}
69 changes: 69 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#[macro_use] extern crate log;
extern crate log_panics;

use log::*;
use std::sync::Mutex;
use std::sync::Arc;

pub trait Handler: Send + Sync + 'static {
fn handle(&self, record: &LogRecord) -> Option<bool>;
}

impl Handler for Box<Handler> {
fn handle(&self, record: &LogRecord) -> Option<bool> {
(**self).handle(record)
}
}

pub struct Logger {
handlers: Arc<Mutex<Vec<Box<Handler>>>>,
level: LogLevel
}

impl Logger {

pub fn init(self) -> Result<(), SetLoggerError> {
log_panics::init();
log::set_logger(|max_log_level| {
max_log_level.set(LogLevelFilter::Info);
Box::new(self)
})
}

pub fn new(level: LogLevel) -> Logger {
Logger {
handlers: Arc::new(Mutex::new(Vec::new())),
level: level
}
}

pub fn append<H: Handler>(&self, handler: H) {
let mut handlers = self.handlers.lock().unwrap();
handlers.push(Box::new(handler));
}
}


impl log::Log for Logger {

fn enabled(&self, metadata: &LogMetadata) -> bool {
metadata.level() <= self.level
}

fn log(&self, record: &LogRecord) {

if self.enabled(record.metadata()) {

let handlers = self.handlers.lock().unwrap();

for handler in handlers.iter() {
if handler.handle(record).is_none() {
break;
}
}
}
}
}

#[cfg(test)]
mod test;
18 changes: 18 additions & 0 deletions src/test/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use log::*;
use Logger;
use Handler;

struct DummyHandler;

impl Handler for DummyHandler {
fn handle(&self, record: &LogRecord) -> Option<bool> {
println!("{}", record.args().to_string());
Some(true)
}
}

#[test] fn test_handler() {
let logger = Logger::new(LogLevel::Info);
logger.append(DummyHandler);
info!("hello");
}

0 comments on commit 60c84f1

Please sign in to comment.