Exercise: Logger Trait

Let’s design a simple logging utility, using a trait Logger with a log method. Code which might log its progress can then take an &impl Logger. In testing, this might put messages in the test logfile, while in a production build it would send messages to a log server.

However, the StderrLogger given below logs all messages, regardless of verbosity. Your task is to write a VerbosityFilter type that will ignore messages above a maximum verbosity.

This is a common pattern: a struct wrapping a trait implementation and implementing that same trait, adding behavior in the process. What other kinds of wrappers might be useful in a logging utility?

1. use std::fmt::Display;

3. pub trait Logger {
4. /// Log a message at the given verbosity level.
5. fn log(&self, verbosity: u8, message: impl Display);
6. }

8. struct StderrLogger;

10. impl Logger for StderrLogger {
11. fn log(&self, verbosity: u8, message: impl Display) {
12. eprintln!("verbosity={verbosity}: {message}");
13. }
14. }

16. fn do_things(logger: &impl Logger) {
17. logger.log(5, "FYI");
18. logger.log(2, "Uhoh");
19. }

21. // TODO: Define and implement `VerbosityFilter`.

23. fn main() {
24. let l = VerbosityFilter { max_verbosity: 3, inner: StderrLogger };
25. do_things(&l);
26. }