I'm developing a desktop application with Kotlin and when configuring the log I'm not able to write each log level in a different file.
In the file build.gradle.kts
I have added the dependencies:
implementation("io.github.oshai:kotlin-logging-jvm:7.0.5")
implementation("ch.qos.logback:logback-classic:1.5.18")
And in src/main/resources
the logback.xml
file with the following content:
<configuration>
<import class="ch.qos.logback.core.ConsoleAppender"/>
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
<import class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"/>
<import class="ch.qos.logback.classic.filter.LevelFilter"/>
<timestamp key="bySeconds" datePattern="yyyy-MM-dd HH:mm:ss.SSS"/>
<appender name="STDOUT" class="ConsoleAppender">
<encoder>
<pattern>${bySeconds} [%level] [%thread] %logger{36} %C{0}.%M\(%L\) - %msg%n</pattern>
</encoder>
</appender>
<appender name="DEBUG_FILE" class="RollingFileAppender">
<file>logs/debug.log</file>
<filter class="LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<append>true</append>
<rollingPolicy class="SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/%d{yyyy-MM-dd}/debug.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${bySeconds} [%level] [%thread] %logger{36} %C{0}.%M\(%L\) - %msg%n</pattern>
</encoder>
</appender>
<appender name="INFO_FILE" class="RollingFileAppender">
<file>logs/info.log</file>
<filter class="LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<append>true</append>
<rollingPolicy class="SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/%d{yyyy-MM-dd}/info.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${bySeconds} [%level] [%thread] %logger{36} %C{0}.%M\(%L\) - %msg%n</pattern>
</encoder>
</appender>
<appender name="WARN_FILE" class="RollingFileAppender">
<file>logs/warn.log</file>
<filter class="LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<append>true</append>
<rollingPolicy class="SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/%d{yyyy-MM-dd}/warn.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${bySeconds} [%level] [%thread] %logger{36} %C{0}.%M\(%L\) - %msg%n</pattern>
</encoder>
</appender>
<appender name="ERROR_FILE" class="RollingFileAppender">
<file>logs/error.log</file>
<filter class="LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<append>true</append>
<rollingPolicy class="SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/%d{yyyy-MM-dd}/error.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${bySeconds} [%level] [%thread] %logger{36} %C{0}.%M\(%L\) - %msg%n</pattern>
</encoder>
</appender>
<root>
<appender-ref ref="STDOUT"/>
</root>
<logger level="debug" additivity="false">
<appender-ref ref="DEBUG_FILE"/>
</logger>
<logger level="info" additivity="false">
<appender-ref ref="INFO_FILE"/>
</logger>
<logger level="warn" additivity="false">
<appender-ref ref="WARN_FILE"/>
</logger>
<logger level="error" additivity="false">
<appender-ref ref="ERROR_FILE"/>
</logger>
</configuration>
If I run the application, I see that only the messages are printed on the console and not in the files. I see that the folder is created with the files of each level, but without content. I see this message on the console:
09:52:29,394 |-INFO in ch.qos.logback.core.model.processor.AppenderRefModelHandler - Attaching appender named [STDOUT] to Logger[ROOT]
09:52:29,396 |-ERROR in ch.qos.logback.core.model.processor.AppenderRefModelHandler - Could not find an AppenderAttachable at the top of execution stack. Near <appender-ref> at line 89
09:52:29,396 |-ERROR in ch.qos.logback.core.model.processor.AppenderRefModelHandler - Could not find an AppenderAttachable at the top of execution stack. Near <appender-ref> at line 93
09:52:29,396 |-ERROR in ch.qos.logback.core.model.processor.AppenderRefModelHandler - Could not find an AppenderAttachable at the top of execution stack. Near <appender-ref> at line 97
09:52:29,396 |-ERROR in ch.qos.logback.core.model.processor.AppenderRefModelHandler - Could not find an AppenderAttachable at the top of execution stack. Near <appender-ref> at line 101
09:52:29,396 |-INFO in ch.qos.logback.core.model.processor.DefaultProcessor@3c9d0b9d - End of configuration.
These errors correspond to this section of the configuration file:
<logger level="debug" additivity="false">
<appender-ref ref="DEBUG_FILE"/>
</logger>
<logger level="info" additivity="false">
<appender-ref ref="INFO_FILE"/>
</logger>
<logger level="warn" additivity="false">
<appender-ref ref="WARN_FILE"/>
</logger>
<logger level="error" additivity="false">
<appender-ref ref="ERROR_FILE"/>
</logger>
However, if I put those inside
<root>
<appender-ref ref="STDOUT"/>
</root>
it writes to the files, but all messages of all levels.
I have gone through several posts on the internet and questions on this site, the configuration file is a collection of what I have been finding but even reading the Logback documentation, I can't find where I have the problem.
What am I doing wrong?
I'm developing a desktop application with Kotlin and when configuring the log I'm not able to write each log level in a different file.
In the file build.gradle.kts
I have added the dependencies:
implementation("io.github.oshai:kotlin-logging-jvm:7.0.5")
implementation("ch.qos.logback:logback-classic:1.5.18")
And in src/main/resources
the logback.xml
file with the following content:
<configuration>
<import class="ch.qos.logback.core.ConsoleAppender"/>
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
<import class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"/>
<import class="ch.qos.logback.classic.filter.LevelFilter"/>
<timestamp key="bySeconds" datePattern="yyyy-MM-dd HH:mm:ss.SSS"/>
<appender name="STDOUT" class="ConsoleAppender">
<encoder>
<pattern>${bySeconds} [%level] [%thread] %logger{36} %C{0}.%M\(%L\) - %msg%n</pattern>
</encoder>
</appender>
<appender name="DEBUG_FILE" class="RollingFileAppender">
<file>logs/debug.log</file>
<filter class="LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<append>true</append>
<rollingPolicy class="SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/%d{yyyy-MM-dd}/debug.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${bySeconds} [%level] [%thread] %logger{36} %C{0}.%M\(%L\) - %msg%n</pattern>
</encoder>
</appender>
<appender name="INFO_FILE" class="RollingFileAppender">
<file>logs/info.log</file>
<filter class="LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<append>true</append>
<rollingPolicy class="SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/%d{yyyy-MM-dd}/info.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${bySeconds} [%level] [%thread] %logger{36} %C{0}.%M\(%L\) - %msg%n</pattern>
</encoder>
</appender>
<appender name="WARN_FILE" class="RollingFileAppender">
<file>logs/warn.log</file>
<filter class="LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<append>true</append>
<rollingPolicy class="SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/%d{yyyy-MM-dd}/warn.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${bySeconds} [%level] [%thread] %logger{36} %C{0}.%M\(%L\) - %msg%n</pattern>
</encoder>
</appender>
<appender name="ERROR_FILE" class="RollingFileAppender">
<file>logs/error.log</file>
<filter class="LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<append>true</append>
<rollingPolicy class="SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/%d{yyyy-MM-dd}/error.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>${bySeconds} [%level] [%thread] %logger{36} %C{0}.%M\(%L\) - %msg%n</pattern>
</encoder>
</appender>
<root>
<appender-ref ref="STDOUT"/>
</root>
<logger level="debug" additivity="false">
<appender-ref ref="DEBUG_FILE"/>
</logger>
<logger level="info" additivity="false">
<appender-ref ref="INFO_FILE"/>
</logger>
<logger level="warn" additivity="false">
<appender-ref ref="WARN_FILE"/>
</logger>
<logger level="error" additivity="false">
<appender-ref ref="ERROR_FILE"/>
</logger>
</configuration>
If I run the application, I see that only the messages are printed on the console and not in the files. I see that the folder is created with the files of each level, but without content. I see this message on the console:
09:52:29,394 |-INFO in ch.qos.logback.core.model.processor.AppenderRefModelHandler - Attaching appender named [STDOUT] to Logger[ROOT]
09:52:29,396 |-ERROR in ch.qos.logback.core.model.processor.AppenderRefModelHandler - Could not find an AppenderAttachable at the top of execution stack. Near <appender-ref> at line 89
09:52:29,396 |-ERROR in ch.qos.logback.core.model.processor.AppenderRefModelHandler - Could not find an AppenderAttachable at the top of execution stack. Near <appender-ref> at line 93
09:52:29,396 |-ERROR in ch.qos.logback.core.model.processor.AppenderRefModelHandler - Could not find an AppenderAttachable at the top of execution stack. Near <appender-ref> at line 97
09:52:29,396 |-ERROR in ch.qos.logback.core.model.processor.AppenderRefModelHandler - Could not find an AppenderAttachable at the top of execution stack. Near <appender-ref> at line 101
09:52:29,396 |-INFO in ch.qos.logback.core.model.processor.DefaultProcessor@3c9d0b9d - End of configuration.
These errors correspond to this section of the configuration file:
<logger level="debug" additivity="false">
<appender-ref ref="DEBUG_FILE"/>
</logger>
<logger level="info" additivity="false">
<appender-ref ref="INFO_FILE"/>
</logger>
<logger level="warn" additivity="false">
<appender-ref ref="WARN_FILE"/>
</logger>
<logger level="error" additivity="false">
<appender-ref ref="ERROR_FILE"/>
</logger>
However, if I put those inside
<root>
<appender-ref ref="STDOUT"/>
</root>
it writes to the files, but all messages of all levels.
I have gone through several posts on the internet and questions on this site, the configuration file is a collection of what I have been finding but even reading the Logback documentation, I can't find where I have the problem.
What am I doing wrong?
Share Improve this question edited Mar 20 at 10:51 MinionAttack asked Mar 20 at 10:27 MinionAttackMinionAttack 5401 gold badge13 silver badges32 bronze badges 2- Why do you even want to do that? You will be missing a lot of context and need to compare multiple files to see what actually happened. If you just want to see, let's say, errors, just filter the one log file you have accordingly. – tyg Commented Mar 20 at 11:01
- Hi @tyg, it's a toy project. I like to try different things, is it possible to achieve it? – MinionAttack Commented Mar 20 at 11:07
1 Answer
Reset to default 0I have found the problem. As I don't define loggers by classes or packages, I had to put them in the global (<root>
) since in each <appender>
I have the filtering of the level I want.
So, the change to make is to delete all the <logger>
and add the <appender-ref>
inside <root>
, defining a minimun level for the <root>
:
...............
<root level="debug">
<appender-ref ref="STDOUT"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</configuration>
And now each log level goes into its own file.
To test it, in the code I have:
import io.github.oshai.kotlinlogging.KotlinLogging
private val logger = KotlinLogging.logger {}
fun main() = application {
logger.debug { "Debug message" }
logger.info { "Info message" }
logger.warn { "Warn message" }
logger.error { "Error message" }
}