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

new feature: keep only the last N log files in rotation #2169

Merged
merged 0 commits into from
Aug 22, 2022
Merged

new feature: keep only the last N log files in rotation #2169

merged 0 commits into from
Aug 22, 2022

Conversation

ozgunozerk
Copy link
Contributor

Motivation

tracing-appender does not have Rotation based on size yet.
Also, it doesn't have the feature of keeping the most recent N log files

I believe the second feature is more easy to implement, and also will partially solve the Rotation based on size problem. Because people may choose hourly or daily rotation based on their needs, and put an extra boundary of keep the last 5 files for example. Of course it won't handle all the edge cases for Rotation based on size. But it will cover most of the scenarios. And also, it is a good feature to have on its own :)

Solution

Introduce another field called keep_last: Option<usize> to the Inner of RollingFileAppender struct.
I managed to did not touch any of the existing functions, so it WON'T BE A BREAKING CHANGE. Yay :)

The solution is, whenever the rotation should happen, the refresh_writer() is called. So I embed the following logic into that function:

1- check the log folder and detect the log files
2- if there are more log files than the last_keep amount
3- store the filenames in a vector, and sort them by their dates (dates are already present in the filename)
4- keep deleting the oldest ones, till we have desired amount of log files in the log folder

@ozgunozerk ozgunozerk requested a review from a team as a code owner June 17, 2022 17:30
@ozgunozerk
Copy link
Contributor Author

ozgunozerk commented Jun 17, 2022

Is related to #858 and #1940

tracing-appender/src/rolling.rs Outdated Show resolved Hide resolved
Comment on lines 531 to 533
let date_str =
filename.clone()[(filename.rfind('.').unwrap() + 1)..].to_string();
let date: usize = date_str.replace("-", "").parse().unwrap();
Copy link

Choose a reason for hiding this comment

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

It might not work if the underlying format of date would be changed.

I would extract Rotation::join_date into a type which would implement both Display and FromStr

tracing-appender/src/rolling.rs Outdated Show resolved Hide resolved
};
(inner, writer)
}

fn prune_old_logs(&self, keep_last: usize) {
Copy link
Member

Choose a reason for hiding this comment

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

If I understand this correctly, this function is collecting files into a Vec of Strings if the path contains the filename prefix and then sorting by the dates.

While this can work, I don't think this is how this should be implemented. Namely, I suggest the following:

  • Use the path components of each path and convert log_file_name to an OsStr.
  • I don't recommend doing the adhoc string parsing in the latter half of the function. I believe we're emitting files with dates and I'm not opposed to enabling time's datetime parsing functionality in tracing-appender to better suppose this use-case, but depending on the compilation size hit, I might want to place this behind a Cargo feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Use the path components of each path and convert log_file_name to an OsStr.

instead of using path components and iterating till the last Some(...), i think using the file_name() from DirEntry can be more useful. It already returns an OsStr :)

Copy link
Contributor Author

@ozgunozerk ozgunozerk Jun 18, 2022

Choose a reason for hiding this comment

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

I don't recommend doing the adhoc string parsing in the latter half of the function. I believe we're emitting files with dates and I'm not opposed to enabling time's datetime parsing functionality in tracing-appender to better suppose this use-case, but depending on the compilation size hit, I might want to place this behind a Cargo feature.

I implemented this, and pushed the change. However, I think it may be useful to compare two solutions.

  • 1st (adhoc): less code, same logic, but more error-prone since we are using some string slice, and if any mistake happens, it will be harder to discover and debug
  • 2nd (date parsing): extra imports, an extra code block of 18 lines just to create a formatter for the date parser. Safer code, since we will be treating the string slice as a proper Date object.

Please check the latest commit for the code changes related to above discussion

Important note: we actually already calculated the formatter in the code. I wanted to use it via storing this in an extra field in the Inner struct. But the type of formatter is FormatItem, which is a lifetimed variable. So the Inner itself should have a lifetime, and I did not want to disrupt the clean/simple code. Hence, the code duplication in my solution.
As you may guess, I'm not a mature Rust developer, but trying my best. If you think another solution may be better, please let me know. I'll try to implement it :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When you are happy with the solution, I can place all this behind a feature 👍

tracing-appender/src/rolling.rs Outdated Show resolved Hide resolved
@ozgunozerk ozgunozerk requested a review from davidbarsky June 18, 2022 17:12
@ozgunozerk
Copy link
Contributor Author

There is one test failing :/

Copy link
Contributor

@CBenoit CBenoit left a comment

Choose a reason for hiding this comment

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

Hi! I would love to see this landing! 🙂

let mut sorted_files = vec![];
for file in files {
let filename = file.file_name().to_string_lossy().to_string();
let date_str = filename[(filename.rfind('.').unwrap() + 1)..].to_string();
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure this will never panic?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As long as the format of adding dates will stay the same, this should not panic imo.
Currently, the date is added to the filename with a ., so there should be a . just before the date :)

Copy link
Contributor

@CBenoit CBenoit Jun 23, 2022

Choose a reason for hiding this comment

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

I see! Probably a bit stretched, but imagine :

  • my prefix doesn’t contain any extension (say for instance: "abc"). tracing-appender will generate files such as abc.2022.06.03 and so on and so forth.
  • however, user can create a file called abc
  • this file will be collected by the code at line 517, because the predicate is true ("abc" is contained in "abc")
  • rfind(’.’) will returns None because "abc" doesn’t contain any .

Quite an edge case though, maybe not worth addressing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. Yes, if the user manipulates the log folder, everything becomes possible :/
Let me try to handle it with a better error handling! Thank you very much for the suggestion

@davidbarsky
Copy link
Member

i want to make a few additional notes, but since i'm packing for my move this weekend, it might be a day or two. sorry!

@ozgunozerk
Copy link
Contributor Author

Any update on this?

@ozgunozerk
Copy link
Contributor Author

I'm willing to make further updates if necessary :)

@davidbarsky
Copy link
Member

Sorry, it's been a busy month. Do you mind if I make commits directly to your branch/fork for the changes that I think we need? Since I believe this approach works for you, I'll also open a branch that targets the master branch—it'll make the subsequent branch maintenance on our side easier.

@ozgunozerk
Copy link
Contributor Author

Of course, no worries!

@ozgunozerk
Copy link
Contributor Author

Any update on this? :)

@ozgunozerk ozgunozerk merged commit 5a141ea into tokio-rs:v0.1.x Aug 22, 2022
@CBenoit
Copy link
Contributor

CBenoit commented Aug 22, 2022

What happened to this PR? GitHub says it's merged, but it's not as far as I can tell

image

@ozgunozerk
Copy link
Contributor Author

Because unfortunately there were no responses from the tokio team, I decided to use the fork for my project, and thus synced the fork. Weird that it says merged. Let me re-open the PR, maybe we can get some attention :)

@ozgunozerk
Copy link
Contributor Author

New one is here: #2284

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants