Skip to content

Commit

Permalink
Merge branch 'logging-configurator' of stuartgunter/dropwizard
Browse files Browse the repository at this point in the history
  • Loading branch information
joschi committed Jan 17, 2015
2 parents a350547 + 54cdcb7 commit b61b601
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 4 deletions.
9 changes: 5 additions & 4 deletions docs/source/manual/core.rst
Expand Up @@ -460,10 +460,11 @@ Tasks
=====
A ``Task`` is a run-time action your application provides access to on the administrative port via HTTP.
All Dropwizard applications start with the ``gc`` task, which explicitly triggers the JVM's garbage
collection. (This is useful, for example, for running full garbage collections during off-peak times
or while the given application is out of rotation.) The execute method of a ``Task`` can be annotated
with ``@Timed``, ``@Metered``, and ``@ExceptionMetered``. Dropwizard will automatically
All Dropwizard applications start with: the ``gc`` task, which explicitly triggers the JVM's garbage
collection (This is useful, for example, for running full garbage collections during off-peak times
or while the given application is out of rotation.); and the ``log-level`` task, which configures the level
of any number of loggers at runtime (akin to Logback's ``JmxConfigurator``). The execute method of a ``Task``
can be annotated with ``@Timed``, ``@Metered``, and ``@ExceptionMetered``. Dropwizard will automatically
record runtime information about your tasks. Here's a basic task class:
.. code-block:: java
Expand Down
Expand Up @@ -5,6 +5,7 @@
import com.codahale.metrics.health.jvm.ThreadDeadlockHealthCheck;
import io.dropwizard.jetty.MutableServletContextHandler;
import io.dropwizard.jetty.setup.ServletEnvironment;
import io.dropwizard.logging.tasks.LogConfigurationTask;
import io.dropwizard.servlets.tasks.GarbageCollectionTask;
import io.dropwizard.servlets.tasks.Task;
import io.dropwizard.servlets.tasks.TaskServlet;
Expand Down Expand Up @@ -38,6 +39,7 @@ public AdminEnvironment(MutableServletContextHandler handler,
this.healthChecks.register("deadlocks", new ThreadDeadlockHealthCheck());
this.tasks = new TaskServlet(metricRegistry);
tasks.add(new GarbageCollectionTask());
tasks.add(new LogConfigurationTask());
addServlet("tasks", tasks).addMapping("/tasks/*");
handler.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener() {
@Override
Expand Down
5 changes: 5 additions & 0 deletions dropwizard-logging/pom.xml
Expand Up @@ -22,6 +22,11 @@
<artifactId>dropwizard-validation</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-servlets</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-logback</artifactId>
Expand Down
@@ -0,0 +1,74 @@
package io.dropwizard.logging.tasks;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import com.google.common.collect.ImmutableMultimap;
import io.dropwizard.servlets.tasks.Task;
import org.slf4j.LoggerFactory;

import java.io.PrintWriter;
import java.util.List;

/**
* <p>Sets the logging level for a number of loggers</p>
*
* <b>Parameters:</b>
* <table>
* <tr>
* <td>Name</td>
* <td>Description</td>
* </tr>
* <tr>
* <td>logger</td>
* <td>One or more logger names to be configured with the specified log level.</td>
* </tr>
* <tr>
* <td>level</td>
* <td>An optional Logback {@link Level} to configure. If not provided, the log level will be set to null.</td>
* </tr>
* </table>
*/
public class LogConfigurationTask extends Task {

private final LoggerContext loggerContext;

/**
* Creates a new LogConfigurationTask.
*/
public LogConfigurationTask() {
this((LoggerContext) LoggerFactory.getILoggerFactory());
}

/**
* Creates a new LogConfigurationTask with the given {@link ch.qos.logback.classic.LoggerContext} instance.
* <p/>
* <b>Use {@link LogConfigurationTask#LogConfigurationTask()} instead.</b>
*
* @param loggerContext a {@link ch.qos.logback.classic.LoggerContext} instance
*/
public LogConfigurationTask(LoggerContext loggerContext) {
super("log-level");
this.loggerContext = loggerContext;
}

@Override
public void execute(ImmutableMultimap<String, String> parameters, PrintWriter output) throws Exception {
List<String> loggerNames = getLoggerNames(parameters);
Level loggerLevel = getLoggerLevel(parameters);

for (String loggerName : loggerNames) {
loggerContext.getLogger(loggerName).setLevel(loggerLevel);
output.println(String.format("Configured logging level for %s to %s", loggerName, loggerLevel));
output.flush();
}
}

private List<String> getLoggerNames(ImmutableMultimap<String, String> parameters) {
return parameters.get("logger").asList();
}

private Level getLoggerLevel(ImmutableMultimap<String, String> parameters) {
List<String> loggerLevels = parameters.get("level").asList();
return loggerLevels.isEmpty() ? null : Level.valueOf(loggerLevels.get(0));
}
}
@@ -0,0 +1,86 @@
package io.dropwizard.logging.tasks;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import com.google.common.collect.ImmutableMultimap;
import org.junit.Before;
import org.junit.Test;

import java.io.PrintWriter;
import java.io.StringWriter;

import static org.assertj.core.api.Assertions.assertThat;

public class LogConfigurationTaskTest {

private static final Level DEFAULT_LEVEL = Level.ALL;

private final LoggerContext loggerContext = new LoggerContext();
private final Logger logger1 = loggerContext.getLogger("logger.one");
private final Logger logger2 = loggerContext.getLogger("logger.two");

private final StringWriter stringWriter = new StringWriter();
private final PrintWriter output = new PrintWriter(stringWriter);

private final LogConfigurationTask task = new LogConfigurationTask(loggerContext);

@Before
public void setUp() throws Exception {
logger1.setLevel(DEFAULT_LEVEL);
logger2.setLevel(DEFAULT_LEVEL);
}

@Test
public void configuresSpecificLevelForALogger() throws Exception {
// given
ImmutableMultimap<String, String> parameters = ImmutableMultimap.of(
"logger", "logger.one",
"level", "debug");

// when
task.execute(parameters, output);

// then
assertThat(logger1.getLevel()).isEqualTo(Level.DEBUG);
assertThat(logger2.getLevel()).isEqualTo(DEFAULT_LEVEL);

assertThat(stringWriter.toString()).isEqualTo("Configured logging level for logger.one to DEBUG\n");
}

@Test
public void configuresDefaultLevelForALogger() throws Exception {
// given
ImmutableMultimap<String, String> parameters = ImmutableMultimap.of(
"logger", "logger.one");

// when
task.execute(parameters, output);

// then
assertThat(logger1.getLevel()).isNull();
assertThat(logger2.getLevel()).isEqualTo(DEFAULT_LEVEL);

assertThat(stringWriter.toString()).isEqualTo("Configured logging level for logger.one to null\n");
}

@Test
public void configuresLevelForMultipleLoggers() throws Exception {
// given
ImmutableMultimap<String, String> parameters = ImmutableMultimap.of(
"logger", "logger.one",
"logger", "logger.two",
"level", "INFO");

// when
task.execute(parameters, output);

// then
assertThat(logger1.getLevel()).isEqualTo(Level.INFO);
assertThat(logger2.getLevel()).isEqualTo(Level.INFO);

assertThat(stringWriter.toString()).isEqualTo(
"Configured logging level for logger.one to INFO\n" +
"Configured logging level for logger.two to INFO\n");
}
}

0 comments on commit b61b601

Please sign in to comment.