2 * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
3 * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
4 * provided that the following conditions are met: Redistributions of source code must retain the above copyright
5 * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the
6 * above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other
7 * materials provided with the distribution, if any, must include the following acknowledgment: "This product includes
8 * software developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu>Internet2
9 * Project. Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
10 * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2,
11 * nor the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
12 * products derived from this software without specific prior written permission. For written permission, please
13 * contact shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2,
14 * UCAID, or the University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name,
15 * without prior written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS
16 * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES,
17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
18 * NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS
19 * WITH LICENSEE. IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED
20 * INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
23 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
27 package edu.internet2.middleware.shibboleth.log;
29 import java.io.IOException;
30 import java.io.PrintWriter;
31 import java.net.MalformedURLException;
34 import javax.servlet.ServletContextEvent;
35 import javax.servlet.ServletContextListener;
37 import org.apache.log4j.ConsoleAppender;
38 import org.apache.log4j.Level;
39 import org.apache.log4j.LogManager;
40 import org.apache.log4j.Logger;
41 import org.apache.log4j.PatternLayout;
42 import org.apache.log4j.PropertyConfigurator;
43 import org.apache.log4j.RollingFileAppender;
44 import org.apache.log4j.xml.DOMConfigurator;
45 import org.apache.xml.security.Init;
46 import org.w3c.dom.Document;
47 import org.w3c.dom.NamedNodeMap;
48 import org.w3c.dom.Node;
49 import org.w3c.dom.NodeList;
51 import edu.internet2.middleware.shibboleth.common.OriginConfig;
52 import edu.internet2.middleware.shibboleth.common.ShibResource;
53 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
54 import edu.internet2.middleware.shibboleth.common.ShibbolethOriginConfig;
57 * {@link ServletContextListener}used to configure logging for other components.
59 * @author Walter Hoehn
62 public class LoggingContextListener implements ServletContextListener {
64 private static Logger log = Logger.getLogger(LoggingContextListener.class.getName());
66 // tomcat calls this before the servlet init()s, but is that guaranteed?
67 public void contextInitialized(ServletContextEvent sce) {
68 //Silliness to get around xmlsec doing its own configuration, ie: we might need to override it
71 ConsoleAppender rootAppender = new ConsoleAppender();
72 rootAppender.setWriter(new PrintWriter(System.out));
73 rootAppender.setName("stdout");
74 Logger.getRootLogger().addAppender(rootAppender);
76 // rootAppender.setLayout(new PatternLayout("%-5p %-41X{serviceId} %d{ISO8601} (%c:%L) - %m%n"));
77 // Logger.getRootLogger().setLevel((Level) Level.DEBUG);
78 Logger.getRootLogger().setLevel((Level) Level.INFO);
79 rootAppender.setLayout(new PatternLayout("%d{ISO8601} %-5p %-41X{serviceId} - %m%n"));
82 Document originConfig = OriginConfig.getOriginConfig(sce.getServletContext());
83 loadConfiguration(originConfig);
84 } catch (ShibbolethConfigurationException e) {
85 sce.getServletContext().log("Problem setting up logging.", e);
86 log.fatal("Problem setting up logging: " + e);
87 throw new Error("Problem setting up logging: " + e); // XXX
90 log.info("Logger initialized.");
93 public void contextDestroyed(ServletContextEvent sce) {
94 log.info("Shutting down logging infrastructure.");
95 LogManager.shutdown();
98 protected void loadConfiguration(Document originConfig) throws ShibbolethConfigurationException {
99 NodeList itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(
100 ShibbolethOriginConfig.originConfigNamespace, "Logging");
101 Node errorLogNode = null;
102 boolean encounteredLog4JConfig = false;
104 if (itemElements.getLength() > 1) {
105 log.warn("Encountered multiple <Logging> configuration elements. Using first one.");
108 if (itemElements.getLength() >= 1) {
109 Node loggingNode = itemElements.item(0);
111 for (int i = 0; i < loggingNode.getChildNodes().getLength(); i++) {
112 Node node = loggingNode.getChildNodes().item(i);
114 if ("Log4JConfig".equals(node.getNodeName())) {
116 encounteredLog4JConfig = true;
117 } else if ("TransactionLog".equals(node.getNodeName())) {
118 configureTransactionLog(node);
119 } else if ("ErrorLog".equals(node.getNodeName())) {
120 // make sure we do ErrorLog after TransactionLog so that the transaction log
121 // initialization info always gets logged in the same place
127 if (errorLogNode != null) {
128 configureErrorLog(errorLogNode);
130 // started out at INFO for logging config messages
131 Logger.getRootLogger().setLevel((Level) Level.WARN);
134 // turn these off by default
135 if (!encounteredLog4JConfig) {
136 Logger.getLogger("org.apache.xml.security").setLevel((Level) Level.OFF);
137 Logger.getLogger("org.opensaml").setLevel((Level) Level.OFF);
141 // location should be a "file:/" uri
142 private RollingFileAppender makeRollingFileAppender(String location, String pattern)
143 throws ShibbolethConfigurationException {
145 String logPath = new ShibResource(location, LoggingContextListener.class).getFile().getCanonicalPath();
146 RollingFileAppender appender = new RollingFileAppender(new PatternLayout(pattern), logPath);
148 appender.setMaximumFileSize(1024 * 1024); // 1 megabyte
149 appender.setMaxBackupIndex(Integer.MAX_VALUE); // imho we should not delete any log files
152 } catch (IOException e) {
153 log.fatal("<TransactionLog location=\"" + location + "\">: error creating RollingFileAppender: " + e);
154 throw new ShibbolethConfigurationException("<TransactionLog location=\"" + location
155 + "\">: error creating RollingFileAppender: " + e);
159 private void configureErrorLog(Node node) throws ShibbolethConfigurationException {
160 NamedNodeMap attributes = node.getAttributes();
162 /* schema check should catch if location is missing, NullPointerException here if not */
163 String location = attributes.getNamedItem("location").getNodeValue();
164 RollingFileAppender appender = makeRollingFileAppender(location, "%d{ISO8601} %-5p %-41X{serviceId} - %m%n");
166 appender.setName("error");
167 appender.setMaxBackupIndex(Integer.MAX_VALUE); // imho we should not delete any log files
169 Level level = (Level) Level.WARN;
170 if (attributes.getNamedItem("level") != null) {
171 log.info("Setting log level to " + attributes.getNamedItem("level").getNodeValue());
172 level = Level.toLevel(attributes.getNamedItem("level").getNodeValue());
173 Logger.getRootLogger().setLevel(level);
176 // log this before switching levels
177 log.info("Switching logging to " + appender.getFile());
178 Logger.getRootLogger().removeAllAppenders();
179 Logger.getRootLogger().addAppender(appender);
181 Logger.getRootLogger().setLevel(level);
184 private void configureTransactionLog(Node node) throws ShibbolethConfigurationException {
185 NamedNodeMap attributes = node.getAttributes();
187 // schema check should catch if location is missing, NullPointerException here if not
188 String location = attributes.getNamedItem("location").getNodeValue();
189 RollingFileAppender appender = makeRollingFileAppender(location, "%d{ISO8601} %m%n");
190 appender.setName("transaction");
192 Logger log = Logger.getLogger("Shibboleth-TRANSACTION");
193 log.setAdditivity(false); // do not want parent's messages
194 log.setLevel((Level) Level.INFO); // all messages to this log are INFO
196 // log.removeAllAppenders(); // imho we want these messages to appear in the "error" log if level >= INFO
197 log.addAppender(appender);
200 private void doLog4JConfig(Node node) throws ShibbolethConfigurationException {
201 NamedNodeMap attributes = node.getAttributes();
203 // schema check should catch if location is missing, NullPointerException here if not
204 String location = attributes.getNamedItem("location").getNodeValue();
207 if (attributes.getNamedItem("type") != null) {
208 type = attributes.getNamedItem("type").getNodeValue();
213 url = new URL(location);
214 } catch (MalformedURLException e) {
215 log.fatal("<Log4JConfig location=\"" + location + "\">: not a valid URL: " + e);
216 throw new ShibbolethConfigurationException("<Log4JConfig location=\"" + location + "\">: not a valid URL: "
220 if (type == null || "properties".equals(type)) {
221 log.info("Using Properties log4j configuration from " + url);
222 PropertyConfigurator.configure(url);
223 } else if ("xml".equals(type)) {
224 log.info("Using XML log4j configuration from " + url);
225 DOMConfigurator.configure(url);