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 13 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 @@ -20,9 +20,13 @@ keywords = ["logging", "tracing", "file-appender", "non-blocking-writer"]
edition = "2018"
rust-version = "1.51.0"

[features]
compression_gzip = ["flate2"]

[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 Down
114 changes: 114 additions & 0 deletions tracing-appender/src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//! Builder struct for `RollingFileAppender`
//!
//! Gives access to setting additional options which are not avaible using standard interface.
//! Currently it is the only way to enable compression of logs.
use crate::rolling::{create_writer_file, Inner, RollingFileAppender, Rotation};
use crate::sync::RwLock;
use std::path::Path;
use std::sync::atomic::AtomicUsize;
use time::OffsetDateTime;

#[cfg(feature = "compression_gzip")]
use crate::compression::CompressionConfig;

#[cfg(feature = "compression_gzip")]
use crate::compression::CompressionOption;

use crate::writer::WriterChannel;

/// Struct for keeping temporary values of `RollingFileAppender`.
///
/// Note that `log_directory` and `log_filename_prefix` are obligatory parameters and should
/// be passed into the constructor of `RollingFileAppenderBuilder`.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RollingFileAppenderBuilder {
log_directory: String,
log_filename_prefix: String,
rotation: Option<Rotation>,
#[cfg(feature = "compression_gzip")]
compression: Option<CompressionConfig>,
}

impl RollingFileAppenderBuilder {
/// Creates a new `RollingFileAppnderBuilder`
///
/// It was introduced to open up the possibility to use `compression` without
/// breaking the current interface.
///
/// Note that `compression` module is enabled by using an optional feature flag
/// `compression_gzip` (for gzip algorithm)
///
/// # Examples
/// ```rust
/// use tracing_appender::builder::RollingFileAppenderBuilder;
/// use tracing_appender::compression::CompressionOption;
/// use tracing_appender::rolling::Rotation;
/// let builder = RollingFileAppenderBuilder::new("/var/tmp", "my-app")
/// .rotation(Rotation::DAILY)
/// .compression(CompressionOption::GzipFast);
/// ```
pub fn new(log_directory: impl AsRef<Path>, log_filename_prefix: impl AsRef<Path>) -> Self {
let log_directory = log_directory
.as_ref()
.to_str()
.expect("Cannot convert log_directory Path to str")
.to_string();
let log_filename_prefix = log_filename_prefix
.as_ref()
.to_str()
.expect("Cannot convert log_filename_prefix Path to str")
.to_string();
RollingFileAppenderBuilder {
log_directory,
log_filename_prefix,
rotation: None,
#[cfg(feature = "compression_gzip")]
#[cfg_attr(docsrs, doc(cfg(feature = "compression_gzip")))]
compression: None,
}
}

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

#[cfg(feature = "compression_gzip")]
#[cfg_attr(docsrs, doc(cfg(feature = "compression_gzip")))]
pub fn compression(mut self, compression: CompressionOption) -> Self {
self.compression = Some(compression.into());
self
}

/// Builds an instance of `RollingFileAppender` using previously defined attributes.
pub fn build(self) -> RollingFileAppender {
let now = OffsetDateTime::now_utc();
let rotation = self.rotation.unwrap_or(Rotation::NEVER);
let filename = rotation.join_date(self.log_filename_prefix.as_str(), &now, false);
let next_date = rotation.next_date(&now);

let writer = RwLock::new(WriterChannel::File(
create_writer_file(self.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: self.log_directory,
log_filename_prefix: self.log_filename_prefix,
next_date,
rotation: rotation,
#[cfg(feature = "compression_gzip")]
compression: self.compression,
},
writer,
}
}
}
154 changes: 154 additions & 0 deletions tracing-appender/src/compression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use flate2::Compression;

#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum GzipCompressionLevelLiteral {
None,
Fast,
Best,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum GzipCompressionLevelNumerical {
Level0,
Level1,
Level2,
Level3,
Level4,
Level5,
Level6,
Level7,
Level8,
Level9,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum GzipCompressionLevel {
Literal(GzipCompressionLevelLiteral),
Numerical(GzipCompressionLevelNumerical),
}

/// Defines a conversion between `CompressionOption` and `flate2::Compression`
impl Into<Compression> for GzipCompressionLevel {
fn into(self) -> Compression {
match self {
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::new(0),
GzipCompressionLevelNumerical::Level1 => Compression::new(1),
GzipCompressionLevelNumerical::Level2 => Compression::new(2),
GzipCompressionLevelNumerical::Level3 => Compression::new(3),
GzipCompressionLevelNumerical::Level4 => Compression::new(4),
GzipCompressionLevelNumerical::Level5 => Compression::new(5),
GzipCompressionLevelNumerical::Level6 => Compression::new(6),
GzipCompressionLevelNumerical::Level7 => Compression::new(7),
GzipCompressionLevelNumerical::Level8 => Compression::new(8),
GzipCompressionLevelNumerical::Level9 => Compression::new(9),
},
}
}
}
pjankiewicz marked this conversation as resolved.
Show resolved Hide resolved

#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct GzipCompression {
pub(crate) level: GzipCompressionLevel,
}

/// Data structure to pass compression parameters
#[derive(Debug, Clone, Eq, PartialEq)]
#[non_exhaustive]
pjankiewicz marked this conversation as resolved.
Show resolved Hide resolved
pub(crate) enum CompressionConfig {
Gzip(GzipCompression),
}

impl CompressionConfig {
pub(crate) fn gz_compress_level(&self) -> Compression {
match self {
CompressionConfig::Gzip(gz) => {
let level = gz.level.clone().into();
level
}
}
}
}

/// Defines a compression level for gzip algorithm.
///
/// Compression levels are defined as they are in `flate2` crate where
/// - compression level 0 (`CompressionOption::GzipNone` or `CompressionOption::GzipLevel0`)
/// - compression level 1 (`CompressionOption::GzipFast` or `CompressionOption::GzipLevel1`)
/// - compression level n (where n is between 2 and 9)
/// - compression level 9 (`CompressionOption::GzipBest` or `CompressionOption::GzipLevel9`)
///
/// ```rust
/// # fn docs() {
/// use tracing_appender::compression::CompressionOption;
/// let compression_level = CompressionOption::GzipBest;
/// # }
/// ```
#[derive(Debug, Clone, Eq, PartialEq)]
#[non_exhaustive]
pub enum CompressionOption {
GzipNone,
GzipFast,
GzipBest,
GzipLevel0,
GzipLevel1,
GzipLevel2,
GzipLevel3,
GzipLevel4,
GzipLevel5,
GzipLevel6,
GzipLevel7,
GzipLevel8,
GzipLevel9,
}

impl Into<CompressionConfig> for CompressionOption {
fn into(self) -> CompressionConfig {
match self {
CompressionOption::GzipNone => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Literal(GzipCompressionLevelLiteral::None),
}),
CompressionOption::GzipFast => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Literal(GzipCompressionLevelLiteral::Fast),
}),
CompressionOption::GzipBest => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Literal(GzipCompressionLevelLiteral::Best),
}),
CompressionOption::GzipLevel0 => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level0),
}),
CompressionOption::GzipLevel1 => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level1),
}),
CompressionOption::GzipLevel2 => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level2),
}),
CompressionOption::GzipLevel3 => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level3),
}),
CompressionOption::GzipLevel4 => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level4),
}),
CompressionOption::GzipLevel5 => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level5),
}),
CompressionOption::GzipLevel6 => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level6),
}),
CompressionOption::GzipLevel7 => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level7),
}),
CompressionOption::GzipLevel8 => CompressionConfig::Gzip(GzipCompression {
level: GzipCompressionLevel::Numerical(GzipCompressionLevelNumerical::Level8),
}),
CompressionOption::GzipLevel9 => 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_gzip")]
mod compression;

pub mod builder;
pjankiewicz marked this conversation as resolved.
Show resolved Hide resolved

/// 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