最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

kotlin - Configure Logback to write each log level to a separate file - Stack Overflow

programmeradmin3浏览0评论

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
Add a comment  | 

1 Answer 1

Reset to default 0

I 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" } 
}
发布评论

评论列表(0)

  1. 暂无评论