Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement compression in tracing-appender #1779

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions tracing-appender/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ rust-version = "1.51.0"
[dependencies]
crossbeam-channel = "0.5.0"
time = { version = "0.3", default-features = false, features = ["formatting"] }
flate2 = { optional = true, version = "1.0.22" }
parking_lot = { optional = true, version = "0.11.2" }

[dependencies.tracing-subscriber]
Expand All @@ -35,3 +36,6 @@ features = ["fmt", "std"]
tracing = { path = "../tracing", version = "0.2" }
time = { version = "0.3", default-features = false, features = ["formatting", "parsing"] }
tempfile = "3"

[features]
compression = ["flate2"]
pjankiewicz marked this conversation as resolved.
Show resolved Hide resolved
88 changes: 88 additions & 0 deletions tracing-appender/src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use std::sync::atomic::AtomicUsize;
use time::OffsetDateTime;
use crate::rolling::{create_writer_file, Inner, RollingFileAppender, Rotation};
use crate::sync::RwLock;

#[cfg(feature="compression")]
pjankiewicz marked this conversation as resolved.
Show resolved Hide resolved
use crate::compression::CompressionConfig;

use crate::writer::WriterChannel;

pub struct RollingFileAppenderBuilder {
log_directory: Option<String>,
log_filename_prefix: Option<String>,
rotation: Option<Rotation>,
next_date: Option<AtomicUsize>,
hawkw marked this conversation as resolved.
Show resolved Hide resolved
#[cfg(feature = "compression")]
compression: Option<CompressionConfig>
}

impl RollingFileAppenderBuilder {
pub fn new() -> Self {
RollingFileAppenderBuilder {
log_directory: None,
log_filename_prefix: None,
rotation: None,
next_date: None,
#[cfg(feature = "compression")]
compression: None
}
}

pub fn log_directory(mut self, log_directory: String) -> Self {
self.log_directory = Some(log_directory);
self
}

pub fn log_filename_prefix(mut self, log_filename_prefix: String) -> Self {
pjankiewicz marked this conversation as resolved.
Show resolved Hide resolved
self.log_filename_prefix = Some(log_filename_prefix);
self
}

pub fn rotation(mut self, rotation: Rotation) -> Self {
self.rotation = Some(rotation);
self
}

pub fn next_date(mut self, next_date: AtomicUsize) -> Self {
self.next_date = Some(next_date);
self
}
hawkw marked this conversation as resolved.
Show resolved Hide resolved

#[cfg(feature = "compression")]
pjankiewicz marked this conversation as resolved.
Show resolved Hide resolved
pub fn compression(mut self, compression: CompressionConfig) -> Self {
self.compression = Some(compression);
self
}

pub fn build(self) -> RollingFileAppender {
let now = OffsetDateTime::now_utc();
let log_directory = self.log_directory.expect("log_directory is required to build RollingFileAppender");
let log_filename_prefix = self.log_filename_prefix.expect("log_filename_prefix is required to build RollingFileAppender");
let rotation = self.rotation.expect("rotation is required to build RollingFileAppender");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not a huge fan of having the builder take several required parameters and then panic if they're not set. Can we consider having the required parameters be passed as arguments to either RollingFileAppenderBuilder::new() or to RollingFileAppenderBuilder::build()? that way, we can ensure at the type level that the parameters that must be provided are present.

alternatively, if we can provide a reasonable default for any of these parameters, we could do that if they're unset...but we don't have defaults for these elsewhere. we could, for example, have the rotation parameter default to Rotation::NEVER, so that not setting one will build a non-rolling compressed file appender...but then we might want to change the name of the type to just FileAppender, since it's not always rolling... :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did change new to accept those 3 parameters: directory, filename, rotation and set rotation default to Rotation::NEVER.


let filename = rotation.join_date(log_filename_prefix.as_str(), &now, false);
let next_date = rotation.next_date(&now);
let writer = RwLock::new(
WriterChannel::File(create_writer_file(log_directory.as_str(), &filename).expect("failed to create appender"))
);

let next_date = AtomicUsize::new(
next_date
.map(|date| date.unix_timestamp() as usize)
.unwrap_or(0),
);

RollingFileAppender {
state: Inner {
log_directory,
log_filename_prefix,
next_date,
rotation,
#[cfg(feature = "compression")]
compression: self.compression
},
writer,
}
}
}
79 changes: 79 additions & 0 deletions tracing-appender/src/compression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use flate2::Compression;

pub enum GzipCompressionLevelLiteral {
None,
Fast,
Best,
}

pub enum GzipCompressionLevelNumerical {
Level0,
Level1,
Level2,
Level3,
Level4,
Level5,
Level6,
Level7,
Level8,
Level9
}

pub enum GzipCompressionLevel {
Literal(GzipCompressionLevelLiteral),
Numerical(GzipCompressionLevelNumerical)
}

impl Into<Compression> for GzipCompressionLevel {
fn into(self) -> Compression {
match GzipCompressionLevel {
GzipCompressionLevel::Literal(lit) => {
match lit {
GzipCompressionLevelLiteral::None => Compression::none(),
GzipCompressionLevelLiteral::Fast => Compression::fast(),
GzipCompressionLevelLiteral::Best => Compression::best()
}
},
GzipCompressionLevel::Numerical(num) => {
match num {
GzipCompressionLevelNumerical::Level0 => Compression(0),
GzipCompressionLevelNumerical::Level1 => Compression(1),
GzipCompressionLevelNumerical::Level2 => Compression(2),
GzipCompressionLevelNumerical::Level3 => Compression(3),
GzipCompressionLevelNumerical::Level4 => Compression(4),
GzipCompressionLevelNumerical::Level5 => Compression(5),
GzipCompressionLevelNumerical::Level6 => Compression(6),
GzipCompressionLevelNumerical::Level7 => Compression(7),
GzipCompressionLevelNumerical::Level8 => Compression(8),
GzipCompressionLevelNumerical::Level9 => Compression(9)
}
}
}
}
}

pub struct GzipCompression {
pub level: GzipCompressionLevel
}
pjankiewicz marked this conversation as resolved.
Show resolved Hide resolved

/// Data structure to pass compression parameters
pub enum CompressionConfig {
Gzip(GzipCompression)
}
pjankiewicz marked this conversation as resolved.
Show resolved Hide resolved

mod compression_options {
use super::*;
pub const GZIP_NONE: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Literal(GzipCompressionLevelLiteral::None) });
pub const GZIP_FAST: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Literal(GzipCompressionLevelLiteral::Fast) });
pub const GZIP_BEST: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Literal(GzipCompressionLevelLiteral::Best) });
pub const GZIP_0: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level0) });
pub const GZIP_1: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level1) });
pub const GZIP_2: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level2) });
pub const GZIP_3: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level3) });
pub const GZIP_4: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level4) });
pub const GZIP_5: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level5) });
pub const GZIP_6: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level6) });
pub const GZIP_7: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level7) });
pub const GZIP_8: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level8) });
pub const GZIP_9: CompressionConfig = CompressionConfig::Gzip(GzipCompression { level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level9) });
}
7 changes: 7 additions & 0 deletions tracing-appender/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ mod worker;

pub(crate) mod sync;

mod writer;

#[cfg(feature = "compression")]
mod compression;

mod builder;

/// Convenience function for creating a non-blocking, off-thread writer.
///
/// See the [`non_blocking` module's docs][mod@non_blocking]'s for more details.
Expand Down
Loading