You have log levels but also markers which act as hierarchal filters. Based on the supplied configuration, something like debug-level log statements may become a no-op for a section of code.
The appenders are an extension point and rather diverse - you can go to a file, a REST server, a database, etc.
Writing a log message to a database or REST server may take a while, so there is a system to dispatch logging events to separate worker threads.
Logging messages may be localized. This means there needs to be a parameterization system to inject data values into localized messages.
You also may want to collect data from various parts of the system as available and log them, basically accumulating information as you go. There are thread contexts maps and stacks to accumulate such data, which can also be parameterized into messages.
It's a highly flexible system without clear security boundaries. Maybe a log message comes from user input rather than being a parameter on a formatted log message. Maybe that user-supplied message contains a parameter itself. And maybe the parameters are written such that they are fulfilled by making system calls.