From 8eecb0aa5c74be0418bb3d9fb2fd85ff92e860e3 Mon Sep 17 00:00:00 2001 From: Stuart Gunter Date: Wed, 27 Aug 2014 08:33:16 +0100 Subject: [PATCH 1/2] Added LogConfigurationTask to allow runtime changes to log levels --- docs/source/manual/core.rst | 9 +- .../io/dropwizard/setup/AdminEnvironment.java | 2 + dropwizard-logging/pom.xml | 5 + .../logging/tasks/LogConfigurationTask.java | 90 ++++++++++++++++ .../tasks/LogConfigurationTaskTest.java | 102 ++++++++++++++++++ 5 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 dropwizard-logging/src/main/java/io/dropwizard/logging/tasks/LogConfigurationTask.java create mode 100644 dropwizard-logging/src/test/java/io/dropwizard/logging/tasks/LogConfigurationTaskTest.java diff --git a/docs/source/manual/core.rst b/docs/source/manual/core.rst index 836aa3f422c..14162ad97b4 100644 --- a/docs/source/manual/core.rst +++ b/docs/source/manual/core.rst @@ -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 diff --git a/dropwizard-core/src/main/java/io/dropwizard/setup/AdminEnvironment.java b/dropwizard-core/src/main/java/io/dropwizard/setup/AdminEnvironment.java index 24e4e0ef298..023bec270f4 100644 --- a/dropwizard-core/src/main/java/io/dropwizard/setup/AdminEnvironment.java +++ b/dropwizard-core/src/main/java/io/dropwizard/setup/AdminEnvironment.java @@ -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; @@ -37,6 +38,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 diff --git a/dropwizard-logging/pom.xml b/dropwizard-logging/pom.xml index d0e02828ce4..99be74318c0 100644 --- a/dropwizard-logging/pom.xml +++ b/dropwizard-logging/pom.xml @@ -22,6 +22,11 @@ dropwizard-validation ${project.version} + + io.dropwizard + dropwizard-servlets + ${project.version} + com.codahale.metrics metrics-logback diff --git a/dropwizard-logging/src/main/java/io/dropwizard/logging/tasks/LogConfigurationTask.java b/dropwizard-logging/src/main/java/io/dropwizard/logging/tasks/LogConfigurationTask.java new file mode 100644 index 00000000000..f16ba9f44a1 --- /dev/null +++ b/dropwizard-logging/src/main/java/io/dropwizard/logging/tasks/LogConfigurationTask.java @@ -0,0 +1,90 @@ +/* + * Copyright 2014 Stuart Gunter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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; + +/** + *

Sets the logging level for a number of loggers

+ * + * Parameters: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
NameDescription
loggerOne or more logger names to be configured with the specified log level.
levelAn optional Logback {@link Level} to configure. If not provided, the log level will be set to null.
+ */ +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. + *

+ * Use {@link LogConfigurationTask#LogConfigurationTask()} instead. + * + * @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 parameters, PrintWriter output) throws Exception { + List 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 getLoggerNames(ImmutableMultimap parameters) { + return parameters.get("logger").asList(); + } + + private Level getLoggerLevel(ImmutableMultimap parameters) { + List loggerLevels = parameters.get("level").asList(); + return loggerLevels.isEmpty() ? null : Level.valueOf(loggerLevels.get(0)); + } +} diff --git a/dropwizard-logging/src/test/java/io/dropwizard/logging/tasks/LogConfigurationTaskTest.java b/dropwizard-logging/src/test/java/io/dropwizard/logging/tasks/LogConfigurationTaskTest.java new file mode 100644 index 00000000000..231721d13ee --- /dev/null +++ b/dropwizard-logging/src/test/java/io/dropwizard/logging/tasks/LogConfigurationTaskTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2014 Stuart Gunter + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +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 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 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 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"); + } +} From 54cdcb7492b23d8e21b4af4ad560806e159ffabe Mon Sep 17 00:00:00 2001 From: Stuart Gunter Date: Wed, 27 Aug 2014 08:45:22 +0100 Subject: [PATCH 2/2] Removed copyright (accidentally included when copying over from my personal project) --- .../logging/tasks/LogConfigurationTask.java | 16 ---------------- .../logging/tasks/LogConfigurationTaskTest.java | 16 ---------------- 2 files changed, 32 deletions(-) diff --git a/dropwizard-logging/src/main/java/io/dropwizard/logging/tasks/LogConfigurationTask.java b/dropwizard-logging/src/main/java/io/dropwizard/logging/tasks/LogConfigurationTask.java index f16ba9f44a1..3df89d1087f 100644 --- a/dropwizard-logging/src/main/java/io/dropwizard/logging/tasks/LogConfigurationTask.java +++ b/dropwizard-logging/src/main/java/io/dropwizard/logging/tasks/LogConfigurationTask.java @@ -1,19 +1,3 @@ -/* - * Copyright 2014 Stuart Gunter - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package io.dropwizard.logging.tasks; import ch.qos.logback.classic.Level; diff --git a/dropwizard-logging/src/test/java/io/dropwizard/logging/tasks/LogConfigurationTaskTest.java b/dropwizard-logging/src/test/java/io/dropwizard/logging/tasks/LogConfigurationTaskTest.java index 231721d13ee..3172bf51f2c 100644 --- a/dropwizard-logging/src/test/java/io/dropwizard/logging/tasks/LogConfigurationTaskTest.java +++ b/dropwizard-logging/src/test/java/io/dropwizard/logging/tasks/LogConfigurationTaskTest.java @@ -1,19 +1,3 @@ -/* - * Copyright 2014 Stuart Gunter - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package io.dropwizard.logging.tasks; import ch.qos.logback.classic.Level;