-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 60c84f1
Showing
7 changed files
with
334 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
} |