Blocking vs Non-blocking mode
In a recent incident investigation, one of our services kept returning 504 (Gateway Timeout status code) for seemingly no particular reason – no extra load, not any particular operation, and not reproducible in lower environments.
… Until we enabled debug logs in the lower environment to help the investigation, that the issue finally reproduced itself.
It turned out that our service containers were logging using the default blocking delivery mode, which led to really slow requests up to the point were the request was cut off with 504.
This article has a detailed explanation on Docker Logging Best Practices.
Logback / Log4J hierarchy cheat sheet
We were continuously fine-tuning the logging setup in our Java/Springboot applications that use Logback library. Here is a cheat sheet on the hierarchy of log levels to stop ourselves from getting confused. (Documentations from Log4J and Logback are detailed, but we needed an “in-a-glance” view.)
# logback.xml
# Content taken directly from https://logback.qos.ch/manual/configuration.html
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
# Custom Logger config *** this is of our interest
<logger name="org.foo" level="error" />
# Root Logger config *** this is of our interest
<root level="debug">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Root logger has higher level than other loggers
Library org.foo
will be logging all logs from info
and above. Logs for all other libraries will logged only if they are error
.
Here are the options to implement it:
## Option 1
<logger name="org.foo" level="info" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
<root level="error">
<appender-ref ref="STDOUT"/>
</root>
## Option 2
<logger name="org.foo" level="info" />
<root level="error">
<appender-ref ref="STDOUT"/>
</root>
However, this leads to duplicated INFO
logs from org.foo
library:
<logger name="org.foo" level="info">
<appender-ref ref="STDOUT"/>
</logger>
<root level="error">
<appender-ref ref="STDOUT"/>
</root>
Root logger has lower level than other loggers
There will only be error
level logs from library org.foo
. Logs for all other libraries will logged if they are debug
and above. Note: this could be confusing – there will be no logs lower than error
(i.e. info
and warn
) for org.foo
.
Here are the options to implement it:
## Option 1
<logger name="org.foo" level="error" additivity="false">
<appender-ref ref="STDOUT"/>
</logger>
<root level="debug">
<appender-ref ref="STDOUT"/>
</root>
## Option 2
<logger name="org.foo" level="error" />
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
But this will lead to duplicated error
logs from org.foo
:
<logger name="org.foo" level="error">
<appender-ref ref="STDOUT"/>
</logger>
<root level="debug">
<appender-ref ref="STDOUT"/>
</root>
Root logger has same level as other loggers
These two options will yield the same results:
## Option 1
## Some would argue this line is not necessary, since the effect is the same as without. But if there are different Spring profiles in the same logback.xml file and you want to easily tweak levels according to the profiles, this could be more explicit, i.e. one could see org.foo would log info level in "local" profile, but would log warn level in "prod" profile.
<logger name="org.foo" level="info"/>
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
## Option 2
<root level="info">
<appender-ref ref="STDOUT"/>
</root>