-
Notifications
You must be signed in to change notification settings - Fork 780
Implement compression in tracing-appender #1779
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
base: master
Are you sure you want to change the base?
Conversation
Thanks for working on this! I'm not opposed to adding compression support to tracing-appender, but here are my initial thoughts on this PR:
I'll add a few additional comments on the code, but those are my main thoughts at this time. |
+1. I'd expect the filename extensions to be added automatically based on which compression algorithm was selected in the builder, rather than selecting a compression algorithm automatically based on the user adding a filename extension. |
@davidbarsky @hawkw Thank you for the feedback. I will implement at least 2 compression crates behind feature flags. About the file extensions it is quite common to use it as a selector for the compression method but I understand the need to more explicit 😄 . The question is about the interface. There are 2 options I see:
impl RollingFileAppender
pub fn new(
rotation: Rotation,
directory: impl AsRef<Path>,
file_name_prefix: impl AsRef<Path>,
compression: Option<CompressionParams> // new parameter
) -> RollingFileAppender { ... }
}
struct CompressionParams {
algorithm: SomeEnum,
levl: SomeOtherEnum
} and then change the constructors:
by adding
I like the second option because it is backward compatible but eventually th first one is better (maybe reserved for a major release?). |
@pjankiewicz Hmm, regarding the API design, I think the ideal approach would be adding a new builder type for construcing a rolling appender. I would expect the API to look sort of like this: let compressed_appender = RollingFileAppender::builder()
.compressed(Compression::gzip().level(3)) // or whatever
.directory("/var/log")
.file_name_prefix("myapp-")
.build(Rotation::Hourly);
let non_compressed_appender = let compressed_appender = RollingFileAppender::builder()
.directory("/var/log")
.file_name_prefix("myapp-")
.build(Rotation::Hourly); I think it's fine if the Also, this has the advantage that, because the compression feature is feature-flagged, we can also feature flag the |
@hawkw I managed to rewrite the PR according to your suggestions also I merged your latest changes from master. There were some hurdles with compression feature flag but I think it looks decent. I still need to update the docs, add tests and format the code. The code will look like this: RollingFileAppenderBuilder::new()
.rotation(Roration::Hourly)
.log_directory("/var/log")
.log_filename_prefix("myapp-")
.compression(compression_options::GZIP_BEST)
.build() I expect the rest will take me a few days more. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall, this looks like the right direction! i left some suggestions, hopefully they're helpful!
845a69c
to
6601402
Compare
@hawkw thank you for the detailed code review, I really appreciate it. The issues you mentioned were fixed. It turned out that IntelliJ didn't even compile the code with the feature flag enabled even though it was selected. Not it compiles with and without the compression feature. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall, this looks very good. i had a bunch of small suggestions, but I think this should be ready to merge soon!
tracing-appender/src/writer.rs
Outdated
#[derive(Debug)] | ||
pub(crate) struct CompressedGzip { | ||
compression: CompressionConfig, | ||
buffer: Arc<RwLock<BufWriter<GzEncoder<BufWriter<File>>>>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a few questions about this:
- why does this need to be inside an
Arc
? as far as I can tell, we never clone it, so i don't think it needs to be reference counted? - i don't love that the
RwLock
here adds a second lock that has to be acquired in the write path when compression is in use, but i see why it's necessary. we probably can't get rid of that without a fairly big rewrite of how the rest ofRollingFileAppender
works, so this is fine for now :/ - on that subject, I don't think this needs to be a
RwLock
. this is only ever locked mutably to write to it, and there's no case where multiple threads might concurrently access it immutably, so i think this should just be aMutex
, which should be a little more lightweight in cases where shared immutable access isn't needed. - what's the motivation for wrapping the
GzEncoder
and theFile
itself in separateBufWriter
s? why do we need two layers of buffering? if this is important, a comment explaining it would be nice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ad 1. Yeah, I might treat Arc<RwLock<...>>
as an idiom by now.
ad 2. I didn't see another option. I tried for many hours to have it without any sort of lock but I always stumbled on the same problem that the GzEncoder couldn't be changed only behind a mut ref
.
ad 3. I will change it to Mutex.
ad 4. I will check this again. You are right that it is confusing a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ad 2. I didn't see another option. I tried for many hours to have it without any sort of lock but I always stumbled on the same problem that the GzEncoder couldn't be changed only behind a
mut ref
.
yeah, i think this is fine, since it would probably take a lot of work to work around that part. don't worry about it.
@pjankiewicz @hawkw What is the current status of this merge? When we can expect it to be part of the official tracing crate? I really like and need this feature. Thank you for your effort. |
@sw-dev-code Sorry for the delay I had some other priorities. I will get back to it this week. |
@pjankiewicz Thank you. Your solution is the most elegant one and the only one I found available to work with the Tracing crate. My devices would have limited memory so this feature is a must. Can I somehow use it before the final release to the Tracing crate? |
Great, I'm happy to help get this over the finish line --- I think it's pretty close to done.
You'll need to backport this PR onto the |
It is easy to reproduce. Just writing |
Co-authored-by: Eliza Weisman <[email protected]>
Co-authored-by: Eliza Weisman <[email protected]>
Co-authored-by: Eliza Weisman <[email protected]>
Co-authored-by: Eliza Weisman <[email protected]>
@hawkw I managed to find the issue with decoding. Before reading from the file the appender needs to be dropped to properly close the buffer. let expected_value = "Hello";
write_to_log(&mut appender, expected_value);
drop(appender); // had to add this to read from the file
assert!(find_str_in_compressed_log(directory.path(), expected_value)); |
@hawkw @sw-dev-code I think the PR is ready. I went back to our previous discussions and resolved some issues - hopefully I didn't miss anything. I checked clippy, tests, formatting etc. I can remove WIP flag right now in my opinion. |
@davidbarsky I might have misclicked to ask you for the review in this PR. Sorry about this I think @hawkw has everything under control. |
@pjankiewicz Thank you so much for your effort. How will I know when this implementation becomes part of the official crate? |
@sw-dev-code It is not up to me. Assuming that it will be merged soon - it will be a part of some future release. As it is a change that doesn't break the current API I hope it will be pretty soon. In the meantime you could use this branch or manually apply the changes to 1.x version. |
@pjankiewicz Ok, understood. Can you guide me on how to configure Rust to use your branch for tracing crate? |
It is possible to specify dependencies from git / github. See here for more information https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-git-repositories. But I wouldn't suggest you use a specific version from any branch - it is much better to rely on stable releases of crates. |
@pjankiewicz Thanks. |
(Going through all open PRs) Seems this PR is pretty close to getting merged, added myself as reviewer for now, will try to look at it later today or tomorrow. |
@pjankiewicz seems the CI has some failures, could you resolve those? After that we can probably merge |
And I think there might be some merge conflicts too |
What happened? I need compression and thinning, pls someone make it! XD |
Is it possible to merge this feature? |
Motivation
This PR implements compression mechanism for tracing-appender crate. In our project we use tracing for event logging and they tend to be very big and compress quite well. We wanted to have a pure Rust solution to handle compression.
Solution
To overcome compatibility issues it looks at the file extension that is given by the user.
will output compressed logs to
/some/path/rolling.log.yyyy-MM-dd-HH-mm.gz
Note that I add non-optional dependency flate2 to Cargo.toml.
Let me know if this is ok so I can update the documentation.
Update
After @hawkw suggestion I have a new plan: