/** * 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 org.apache.aurora.scheduler.http; import java.io.StringWriter; import java.util.List; import java.util.Optional; import java.util.Set; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import com.google.common.collect.FluentIterable; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; import org.apache.aurora.common.util.templating.StringTemplateHelper; import org.apache.aurora.common.util.templating.StringTemplateHelper.TemplateException; import org.apache.commons.lang.StringUtils; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; /** * Servlet that allows for dynamic adjustment of the logging configuration. * * @author William Farner */ @Path("/logconfig") public class LogConfig { private static final List<String> LOG_LEVELS = Lists.newArrayList( Level.OFF.levelStr, Level.ERROR.levelStr, Level.WARN.levelStr, Level.INFO.levelStr, Level.DEBUG.levelStr, Level.TRACE.levelStr, Level.ALL.levelStr, "INHERIT" // Display value for a null level, the logger inherits from its ancestor. ); private final StringTemplateHelper template = new StringTemplateHelper(getClass(), "logconfig", false); @POST @Produces(MediaType.TEXT_HTML) public String post( @FormParam("logger") String loggerName, @FormParam("level") String loggerLevel) throws TemplateException { Optional<String> configChange = Optional.empty(); if (loggerName != null && loggerLevel != null) { Logger logger = (Logger) LoggerFactory.getLogger(loggerName); Level newLevel = Level.toLevel(loggerLevel, null); logger.setLevel(newLevel); configChange = Optional.of(String.format("%s level changed to %s", loggerName, loggerLevel)); } return displayPage(configChange); } @GET @Produces(MediaType.TEXT_HTML) public String get() throws TemplateException { return displayPage(Optional.empty()); } protected String displayPage(Optional<String> configChange) throws TemplateException { StringWriter writer = new StringWriter(); template.writeTemplate(writer, stringTemplate -> { if (configChange.isPresent()) { stringTemplate.setAttribute("configChange", configChange.get()); } LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); Set<LoggerConfig> loggers = FluentIterable.from(context.getLoggerList()) .transform(logger -> new LoggerConfig( logger.getName(), Optional.ofNullable(logger.getLevel()).map(l -> l.levelStr).orElse("INHERIT"))) .toSortedSet(Ordering.natural().onResultOf(LoggerConfig::getName)); stringTemplate.setAttribute("loggers", loggers); stringTemplate.setAttribute("levels", LOG_LEVELS); }); return writer.toString(); } private static class LoggerConfig { private final String name; private final String level; LoggerConfig(String name, String level) { this.name = name; this.level = StringUtils.isBlank(level) ? "INHERIT" : level; } public String getName() { return name; } public String getLevel() { return level; } } }