All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hive.http.Log4j2ConfiguratorServlet Maven / Gradle / Ivy

There is a newer version: 4.0.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.hive.http;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A servlet to configure log4j2.
 * 
* HTTP GET returns all loggers and it's log level in JSON formatted response. *
* HTTP POST is used for configuring the loggers. POST data should be in the same format as GET's response. * To configure (add/update existing loggers), use HTTP POST with logger names and level in the following JSON format. * *
*
 * {
 *  "loggers": [ {
 *    "logger" : "",
 *    "level" : "INFO"
 *  }, {
 *    "logger" : "LlapIoOrc",
 *    "level" : "WARN"
 *  }, {
 *    "logger" : "org.apache.zookeeper.server.NIOServerCnxn",
 *    "level" : "WARN"
 *  }]
 * }
 * 
* *
* Example usage: *
    *
  • * Returns all loggers with levels in JSON format: *
     *      curl http://hostame:port/conflog
     *    
    *
  • *
  • * Set root logger to INFO: *
     *      curl -v -H "Content-Type: application/json" -X POST -d '{ "loggers" : [ { "logger" : "", "level" : "INFO" } ] }'
     *      http://hostame:port/conflog
     *    
    *
  • *
  • * Set logger with level: *
     *      curl -v -H "Content-Type: application/json" -X POST -d '{ "loggers" : [
     *      { "logger" : "LlapIoOrc", "level" : "INFO" } ] }' http://hostame:port/conflog
     *    
    *
  • *
  • * Set log level for all classes under a package: *
     *      curl -v -H "Content-Type: application/json" -X POST -d '{ "loggers" : [
     *      { "logger" : "org.apache.orc", "level" : "INFO" } ] }' http://hostame:port/conflog
     *    
    *
  • *
  • * Set log levels for multiple loggers: *
     *      curl -v -H "Content-Type: application/json" -X POST -d '{ "loggers" : [ { "logger" : "", "level" : "INFO" },
     *      { "logger" : "LlapIoOrc", "level" : "WARN" },
     *      { "logger" : "org.apache.hadoop.hive.llap.daemon.impl.LlapDaemon", "level" : "INFO" },
     *      { "logger" : "org.apache.orc", "level" : "INFO" } ] }' http://hostame:port/conflog
     *    
    *
  • *
*
* Response Status Codes: *
*
    *
  • 200 - OK : If the POST data is valid and if the request succeeds or if GET request succeeds.
  • *
  • 401 - UNAUTHORIZED : If the user does not have privileges to access instrumentation servlets. * Refer hadoop.security.instrumentation.requires.admin config for more info.
  • *
  • 400 - BAD_REQUEST : If the POST data is not a valid JSON.
  • *
  • 500 - INTERNAL_SERVER_ERROR : If GET requests throws any IOException during JSON output generation.
  • *
*/ public class Log4j2ConfiguratorServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static final Logger LOG = LoggerFactory.getLogger(Log4j2ConfiguratorServlet.class); private static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; private static final String ALLOWED_METHODS = "POST, GET"; private static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; private static final String CONTENT_TYPE_JSON_UTF8 = "application/json; charset=utf8"; private transient LoggerContext context; private transient Configuration conf; private static class ConfLoggers { private List loggers; public ConfLoggers() { this.loggers = new ArrayList<>(); } public List getLoggers() { return loggers; } public void setLoggers(final List loggers) { this.loggers = loggers; } } private static class ConfLogger { private String logger; private String level; // no-arg ctor required for JSON deserialization public ConfLogger() { this(null, null); } public ConfLogger(String logger, String level) { this.logger = logger; this.level = level; } public String getLogger() { return logger == null ? logger : logger.trim(); } public void setLogger(final String logger) { this.logger = logger; } public String getLevel() { return level == null ? level : level.trim().toUpperCase(); } public void setLevel(final String level) { this.level = level; } } /** * Initialize this servlet. */ @Override public void init() throws ServletException { context = (LoggerContext) LogManager.getContext(false); conf = context.getConfiguration(); } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (!HttpServer.isInstrumentationAccessAllowed(getServletContext(), request, response)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } setResponseHeader(response); // list the loggers and their levels listLoggers(response); } private void setResponseHeader(final HttpServletResponse response) { response.setContentType(CONTENT_TYPE_JSON_UTF8); response.setHeader(ACCESS_CONTROL_ALLOW_METHODS, ALLOWED_METHODS); response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*"); } @Override protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { if (!HttpServer.isInstrumentationAccessAllowed(getServletContext(), request, response)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } setResponseHeader(response); String dataJson = request.getReader().lines().collect(Collectors.joining()); ObjectMapper objectMapper = new ObjectMapper(); try { ConfLoggers confLoggers = objectMapper.readValue(dataJson, ConfLoggers.class); configureLogger(confLoggers); } catch (IOException e) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); LOG.error("Error configuring log4j2 via /conflog endpoint.", e); return; } response.setStatus(HttpServletResponse.SC_OK); } private void configureLogger(final ConfLoggers confLoggers) { if (confLoggers != null) { for (ConfLogger logger : confLoggers.getLoggers()) { String loggerName = logger.getLogger(); Level logLevel = Level.getLevel(logger.getLevel()); if (logLevel == null) { LOG.warn("Invalid log level: {} for logger: {}. Ignoring reconfiguration.", loggerName, logger.getLevel()); continue; } LoggerConfig loggerConfig = conf.getLoggerConfig(loggerName); // if the logger name is not found, root logger is returned. We don't want to change root logger level // since user either requested a new logger or specified invalid input. In which, we will add the logger // that user requested. if (!loggerName.equals(LogManager.ROOT_LOGGER_NAME) && loggerConfig.getName().equals(LogManager.ROOT_LOGGER_NAME)) { if (LOG.isDebugEnabled()) { LOG.debug("Requested logger ({}) not found. Adding as new logger with {} level", loggerName, logLevel); } // requested logger not found. Add the new logger with the requested level conf.addLogger(loggerName, new LoggerConfig(loggerName, logLevel, true)); } else { if (LOG.isDebugEnabled()) { LOG.debug("Updating logger ({}) to {} level", loggerName, logLevel); } // update the log level for the specified logger loggerConfig.setLevel(logLevel); } } context.updateLoggers(conf); } } private void listLoggers(final HttpServletResponse response) throws IOException { PrintWriter writer = null; try { writer = response.getWriter(); ConfLoggers confLoggers = new ConfLoggers(); Collection loggerConfigs = conf.getLoggers().values(); loggerConfigs.forEach(lc -> confLoggers.getLoggers().add(new ConfLogger(lc.getName(), lc.getLevel().toString()))); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.writerWithDefaultPrettyPrinter().writeValue(writer, confLoggers); } catch (IOException e) { LOG.error("Caught an exception while processing Log4j2 configuration request", e); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } finally { if (writer != null) { writer.close(); } } response.setStatus(HttpServletResponse.SC_OK); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy