Changing log level at run-time

Sometimes you need to change log-level for parts of your application at run-time. Most likely for debugging.

When researching this topic I found a useful trait that logs log-level. https://github.com/jfrosch/GrailsLoggingIssue

package no.prpr

trait LogHelper {

    void log() {
        String simpleName = this.class.simpleName

        log.error ">>> ${simpleName} error message"
        log.warn ">>> ${simpleName} warn message"
        log.info ">>> ${simpleName} info message"
        log.debug ">>> ${simpleName} debug message"
        log.trace ">>> ${simpleName} trace message"
    }

}

With this I created a LogLevel controller to visualize and manipulate log-levels:

package no.prpr

import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.LoggerContext
import org.slf4j.LoggerFactory

class LogLevelController implements LogHelper {

    def index() {
        log.debug("index")
        log()

        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        def loggerList = context.getLoggerList().findAll() { logger ->
            logger.name.startsWith('no.prpr')
        }
        def loggerNames = loggerList*.name
        def logLevels = new LinkedHashMap<>()
        loggerList.each { logger ->
            logLevels.put(logger.name, [level: logger.level, effectiveLevel: logger.effectiveLevel])
        }
        def logLevelList = []
        respond logLevelList, model: [loggers: loggerNames, logLevels: logLevels]
    }
    def update() {
        log.debug("update")
        log.debug("logLevel: ${params['level']}")
        log.debug("logger: ${params['logger']}")
        Logger logger = LoggerFactory.getLogger(params['logger'])
        logger.setLevel(Level.toLevel(params['level']))

        redirect action: 'index'
    }

}

The loggerList variable gets very long if not filtered.

Here’s the logLevel/index.gsp:

<!doctype html>
<html>
<head>
    <meta name="layout" content="main"/>
    <title>Welcome to Grails</title>
</head>
<body>

<div id="content" role="main">
    <section class="row colset-2-its">
        <h1>LogLevel controller</h1>
    </section>
    <div  >
        <br>
        loggerList: ${loggers}
        <br>
        <br>
        logLevels: ${logLevels}
        <br>
    </div>
    <g:form action="update">
        <br>
        Logger: <g:select name="logger" from="${loggers}"></g:select>
        <br>
        LogLevel: <g:select name="level" from="['DEBUG', 'INFO', 'TRACE']"></g:select>
        <br>
        <input class="save" type="submit" value="Set loglevel"  />
    </g:form>
</div>

</body>
</html>

It seems to update immediately.

Beware that depending on other settings logback can update from configuration-file at interval.