Skip to content

Commit

Permalink
Merge pull request #69 from qwerty541/feat_file_logger
Browse files Browse the repository at this point in the history
feat: file logger
  • Loading branch information
qwerty541 authored Jun 29, 2024
2 parents e52437c + 8748c68 commit 9f00d92
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 5 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ path = "examples/tcp-stream-console-logger.rs"
[[example]]
name = "tokio-tcp-stream-console-logger"
path = "examples/tokio-tcp-stream-console-logger.rs"

[[example]]
name = "file-logger"
path = "examples/file-logger.rs"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
- Underlying IO object, which must implement `std::io::Write` and `std::io::Read` traits or their asynchronous analogues from `tokio` library: `tokio::io::AsyncRead` and `tokio::io::AsyncWrite`.
- Buffer formatting part, which must implement `BufferFormatter` trait provided by this library. This part of `LoggedStream` is responsible for the form you will see the input and output bytes. Currently this library provides the following implementations of `BufferFormatter` trait: `LowercaseHexadecimalFormatter`, `UppercaseHexadecimalFormatter`, `DecimalFormatter`, `BinaryFormatter` and `OctalFormatter`. Also `BufferFormatter` is public trait so you are free to construct your own implementation.
- Filtering part, which must implement `RecordFilter` trait provide by this library. This part of `LoggedStream` is responsible for log records filtering. Currently this library provides the following implementation of `RecordFilter` trait: `DefaultFilter` which accepts all log records and `RecordKindFilter` which accepts logs with kinds specified during construct. Also `RecordFilter` is public trait and you are free to construct your own implementation.
- Logging part, which must implement `Logger` trait provided by this library. This part of `LoggedStream` is responsible for further work with constructed, formatter and filtered log record. For example, it can be outputted to console, written to the file, written to database, written to the memory for further use or sended by the channel. Currently this library provides the following implementations of `Logger` trait: `ConsoleLogger`, `MemoryStorageLogger` and `ChannelLogger`. Also `Logger` is public trait and you are free to construct you own implementation.
- Logging part, which must implement `Logger` trait provided by this library. This part of `LoggedStream` is responsible for further work with constructed, formatter and filtered log record. For example, it can be outputted to console, written to the file, written to database, written to the memory for further use or sended by the channel. Currently this library provides the following implementations of `Logger` trait: `ConsoleLogger`, `MemoryStorageLogger`, `ChannelLogger` and `FileLogger`. Also `Logger` is public trait and you are free to construct your own implementation.

### Use Cases

Expand Down
70 changes: 70 additions & 0 deletions examples/file-logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use logged_stream::DefaultFilter;
use logged_stream::FileLogger;
use logged_stream::LoggedStream;
use logged_stream::LowercaseHexadecimalFormatter;
use std::env;
use std::fs;
use tokio::io::AsyncReadExt;
use tokio::io::AsyncWriteExt;
use tokio::net;

async fn handle_connection(mut stream: net::TcpStream) {
loop {
let mut read = [0; 1028];
match stream.read(&mut read).await {
Ok(n) => {
stream.write_all(&read[0..n]).await.unwrap();
}
Err(err) => panic!("{err}"),
}
}
}

#[tokio::main(flavor = "multi_thread", worker_threads = 4)]
async fn main() {
env::set_var("RUST_LOG", "debug");
env_logger::builder()
.default_format()
.format_timestamp_millis()
.init();

let listener = net::TcpListener::bind("127.0.0.1:8080").await.unwrap();

tokio::spawn(async move {
loop {
match listener.accept().await {
Ok((stream, _addr)) => {
tokio::spawn(handle_connection(stream));
}
Err(err) => panic!("{err}"),
}
}
});

let mut client = LoggedStream::new(
net::TcpStream::connect("127.0.0.1:8080").await.unwrap(),
LowercaseHexadecimalFormatter::new_default(),
DefaultFilter,
FileLogger::new(fs::File::create("./examples/traffic.log").unwrap()),
);

let send = [0x01, 0x02, 0x03, 0x04];
client.write_all(&send).await.unwrap();
let mut response = [0u8; 4];
client.read_exact(&mut response).await.unwrap();

let send = [0x05, 0x06, 0x07, 0x08];
client.write_all(&send).await.unwrap();
let mut response = [0u8; 4];
client.read_exact(&mut response).await.unwrap();

let send = [0x09, 0x0a, 0x0b, 0x0c];
client.write_all(&send).await.unwrap();
let mut response = [0u8; 4];
client.read_exact(&mut response).await.unwrap();

let send = [0x01, 0x02, 0x03, 0x04];
client.write_all(&send).await.unwrap();
let mut response = [0u8; 4];
client.read_exact(&mut response).await.unwrap();
}
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
//! of [`LoggedStream`] is responsible for further work with constructed, formatter and filtered
//! log record. For example, it can be outputted to console, written to the file, written to database,
//! written to the memory for further use or sended by the channel. Currently this library provides
//! the following implementations of [`Logger`] trait: [`ConsoleLogger`], [`MemoryStorageLogger`] and
//! [`ChannelLogger`]. Also [`Logger`] is public trait and you are free to construct you own implementation.
//! the following implementations of [`Logger`] trait: [`ConsoleLogger`], [`MemoryStorageLogger`],
//! [`ChannelLogger`] and [`FileLogger`]. Also [`Logger`] is public trait and you are free to construct
//! your own implementation.
//!
//! [`Write`]: std::io::Write
//! [`Read`]: std::io::Read
Expand All @@ -47,6 +48,7 @@ pub use filter::RecordFilter;
pub use filter::RecordKindFilter;
pub use logger::ChannelLogger;
pub use logger::ConsoleLogger;
pub use logger::FileLogger;
pub use logger::Logger;
pub use logger::MemoryStorageLogger;
pub use record::Record;
Expand Down
40 changes: 40 additions & 0 deletions src/logger.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::record::Record;
use crate::RecordKind;
use std::collections;
use std::io::Write;
use std::str::FromStr;
use std::sync::mpsc;

Expand Down Expand Up @@ -179,6 +180,40 @@ impl Logger for Box<ChannelLogger> {
}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FileLogger
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

/// This implementation of [`Logger`] trait writes log records ([`Record`]) into provided file.
pub struct FileLogger {
file: std::fs::File,
}

impl FileLogger {
/// Construct a new instance of [`FileLogger`] using provided file.
pub fn new(file: std::fs::File) -> Self {
Self { file }
}
}

impl Logger for FileLogger {
fn log(&mut self, record: Record) {
let _ = writeln!(
self.file,
"[{}] {} {}",
record.time.format("%+"),
record.kind,
record.message
);
}
}

impl Logger for Box<FileLogger> {
fn log(&mut self, record: Record) {
(**self).log(record)
}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Tests
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -187,6 +222,7 @@ impl Logger for Box<ChannelLogger> {
mod tests {
use crate::logger::ChannelLogger;
use crate::logger::ConsoleLogger;
use crate::logger::FileLogger;
use crate::logger::Logger;
use crate::logger::MemoryStorageLogger;
use crate::record::Record;
Expand All @@ -199,6 +235,7 @@ mod tests {
assert_unpin::<ConsoleLogger>();
assert_unpin::<ChannelLogger>();
assert_unpin::<MemoryStorageLogger>();
assert_unpin::<FileLogger>();
}

#[test]
Expand All @@ -224,6 +261,7 @@ mod tests {
assert_logger::<Box<ConsoleLogger>>();
assert_logger::<Box<MemoryStorageLogger>>();
assert_logger::<Box<ChannelLogger>>();
assert_logger::<Box<FileLogger>>();
}

fn assert_send<T: Send>() {}
Expand All @@ -233,10 +271,12 @@ mod tests {
assert_send::<ConsoleLogger>();
assert_send::<MemoryStorageLogger>();
assert_send::<ChannelLogger>();
assert_send::<FileLogger>();

assert_send::<Box<dyn Logger>>();
assert_send::<Box<ConsoleLogger>>();
assert_send::<Box<MemoryStorageLogger>>();
assert_send::<Box<ChannelLogger>>();
assert_send::<Box<FileLogger>>();
}
}
6 changes: 4 additions & 2 deletions src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ use tokio::io as tokio_io;
/// of [`LoggedStream`] is responsible for further work with constructed, formatter and filtered
/// log record. For example, it can be outputted to console, written to the file, written to database,
/// written to the memory for further use or sended by the channel. Currently this library provides
/// the following implementations of [`Logger`] trait: [`ConsoleLogger`], [`MemoryStorageLogger`] and [`ChannelLogger`].
/// Also [`Logger`] is public trait and you are free to construct you own implementation.
/// the following implementations of [`Logger`] trait: [`ConsoleLogger`], [`MemoryStorageLogger`],
/// [`ChannelLogger`] and [`FileLogger`]. Also [`Logger`] is public trait and you are free to construct
/// your own implementation.
///
/// [`Read`]: io::Read
/// [`Write`]: io::Write
Expand All @@ -52,6 +53,7 @@ use tokio::io as tokio_io;
/// [`DefaultFilter`]: crate::DefaultFilter
/// [`RecordKindFilter`]: crate::RecordKindFilter
/// [`ConsoleLogger`]: crate::ConsoleLogger
/// [`FileLogger`]: crate::FileLogger
pub struct LoggedStream<
S: 'static,
Formatter: 'static,
Expand Down

0 comments on commit 9f00d92

Please sign in to comment.