diff --git a/CHANGES.txt b/CHANGES.txt index 08b875a8881..f88e233f230 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -339,7 +339,9 @@ New Features 67. SOLR-622: SpellCheckComponent supports auto-loading indices on startup and optionally, (re)builds indices on newSearcher event, if configured in solrconfig.xml - (shalin) + (shalin) + +68. SOLR-554: Hierarchical JDK log level selector for SOLR Admin replaces logging.jsp (Sean Timm via shalin) Changes in runtime behavior 1. SOLR-559: use Lucene updateDocument, deleteDocuments methods. This diff --git a/src/webapp/src/org/apache/solr/servlet/LogLevelSelection.java b/src/webapp/src/org/apache/solr/servlet/LogLevelSelection.java new file mode 100644 index 00000000000..54ca53ced42 --- /dev/null +++ b/src/webapp/src/org/apache/solr/servlet/LogLevelSelection.java @@ -0,0 +1,288 @@ +/** + * 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.solr.servlet; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + + +/** + * Admin JDK Logger level report and selection servlet. + * + * @version $Id$ + * @since solr 1.3 + */ +public final class LogLevelSelection extends HttpServlet { + public void init() throws ServletException { + } + + /** + * Processes an HTTP GET request and changes the logging level as + * specified. + */ + public void doGet(HttpServletRequest request, + HttpServletResponse response) + throws IOException, ServletException { + // Output page + + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.write("\n"); + out.write("Solr Admin: JDK Log Level Selector\n"); + out.write(""); + out.write("\n"); + out.write("\"Solr\""); + out.write("

JDK Log Level Selector

"); + + out.write("

Below is the complete JDK Log hierarchy with " + + "intermediate logger/categories synthesized. " + + "The effective logging level is shown to the " + + "far right. If a logger has unset level, then " + + "the effective level is that of the nearest ancestor " + + "with a level setting. Note that this only shows " + + "JDK Log levels.

\n"); + + out.write("
\n"); + + out.write("\n"); + out.write("\n"); + out.write("

\n"); + + out.write(""); + + out.write("" + + "" + + "" + + ""); + + for (int j = 0; j < LEVELS.length; ++j) { + out.write(""); + } + out.write("\n"); + out.write("\n"); + + Iterator iWrappers = buildWrappers().iterator(); + while (iWrappers.hasNext()) { + + LogWrapper wrapper = (LogWrapper) iWrappers.next(); + + out.write("\n"); + for (int j = 0; j < LEVELS.length; ++j) { + out.write("\n"); + } + out.write("\n"); + } + out.write("
Logger/Category name
" + + "
Level
" + + "(Dark rows don't yet exist.)"); + if (LEVELS[j] != null) out.write(LEVELS[j].toString()); + else out.write("unset"); + out.write("Effective
"); + if ("".equals(wrapper.name)) { + out.write("root"); + } else { + out.write(wrapper.name); + } + out.write(""); + if (!wrapper.name.equals("root") || + (LEVELS[j] != null)) { + out.write("'); + } + out.write(""); + if (wrapper.logger != null) { + out.write(getEffectiveLevel(wrapper.logger).toString()); + } + out.write("
\n"); + + out.write("
\n"); + out.write("\n"); + out.write("\n"); + + out.write("
\n"); + + out.write("\n"); + } + + + public void doPost(HttpServletRequest request, + HttpServletResponse response) + throws IOException, ServletException { + if (request.getParameter("submit").equals("set")) { + + Map paramMap = request.getParameterMap(); + + Iterator iParams = paramMap.entrySet().iterator(); + while (iParams.hasNext()) { + Map.Entry p = (Map.Entry) iParams.next(); + String name = (String) p.getKey(); + String value = ((String[]) p.getValue())[0]; + + if (name.equals("submit")) continue; + Logger logger; + LogManager logManager = LogManager.getLogManager(); + if ("root".equals(name)) { + logger = logManager.getLogger(""); + } else logger = logManager.getLogger(name); + + if ("unset".equals(value)) { + if ((logger != null) && (logger.getLevel() != null)) { + logger.setLevel(null); + log.info("Unset log level on '" + name + "'."); + } + } else { + Level level = Level.parse(value); + if (logger == null) logger = Logger.getLogger(name); + if (logger.getLevel() != level) { + logger.setLevel(level); + log.info("Set '" + name + "' to " + + level + " level."); + } + } + } + } else { + log.fine("Selection form cancelled"); + } + + // Redirect back to standard get page. + response.sendRedirect(request.getRequestURI()); + } + + + private Collection buildWrappers() { + // Use tree to get sorted results + SortedSet roots = new TreeSet(); + + roots.add(LogWrapper.ROOT); + + LogManager logManager = LogManager.getLogManager(); + + Enumeration loggerNames = logManager.getLoggerNames(); + while (loggerNames.hasMoreElements()) { + String name = loggerNames.nextElement(); + Logger logger = Logger.getLogger(name); + LogWrapper wrapper = new LogWrapper(logger); + roots.remove(wrapper); // Make sure add occurs + roots.add(wrapper); + + while (true) { + int dot = name.lastIndexOf("."); + if (dot < 0) break; + name = name.substring(0, dot); + roots.add(new LogWrapper(name)); // if not already + } + } + + return roots; + } + + private Level getEffectiveLevel(Logger logger) { + Level level = logger.getLevel(); + if (level != null) { + return level; + } + for (Level l : LEVELS) { + if (l == null) { + continue; + } + if (logger.isLoggable(l)) { + level = l; + } + } + return level; + } + + private static class LogWrapper + implements Comparable { + public static LogWrapper ROOT = + new LogWrapper(LogManager.getLogManager().getLogger("")); + + public LogWrapper(Logger logger) { + this.logger = logger; + this.name = logger.getName(); + } + + public LogWrapper(String name) { + this.name = name; + } + + + public int compareTo(Object other) { + if (this.equals(other)) return 0; + if (this == ROOT) return -1; + if (other == ROOT) return 1; + + return name.compareTo(((LogWrapper) other).name); + } + + public boolean equals(Object other) { + return name.equals(((LogWrapper) other).name); + } + + public Level level() { + if (logger != null) return logger.getLevel(); + return null; + } + + public Logger logger = null; + public String name; + } + + private static Level[] LEVELS = { + null, // aka unset + Level.FINEST, + Level.FINE, + Level.CONFIG, + Level.INFO, + Level.WARNING, + Level.SEVERE, + Level.OFF + // Level.ALL -- ignore. It is useless. + }; + + private Logger log = Logger.getLogger(getClass().getName()); +} diff --git a/src/webapp/web/WEB-INF/web.xml b/src/webapp/web/WEB-INF/web.xml index 1908bac31dd..058fca49e24 100644 --- a/src/webapp/web/WEB-INF/web.xml +++ b/src/webapp/web/WEB-INF/web.xml @@ -101,6 +101,11 @@ 2 + + Logging + org.apache.solr.servlet.LogLevelSelection + + ping @@ -117,6 +122,11 @@ /update/* + + Logging + /admin/logging + + ping diff --git a/src/webapp/web/admin/index.jsp b/src/webapp/web/admin/index.jsp index 967d0af15f6..acebd912202 100644 --- a/src/webapp/web/admin/index.jsp +++ b/src/webapp/web/admin/index.jsp @@ -43,7 +43,7 @@ [Info] [Distribution] [Ping] - [Logging] + [Logging]