2 * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package edu.internet2.middleware.shibboleth.log;
19 import java.io.IOException;
21 import org.apache.log4j.ConsoleAppender;
22 import org.apache.log4j.FileAppender;
23 import org.apache.log4j.Level;
24 import org.apache.log4j.Logger;
25 import org.apache.log4j.PatternLayout;
26 import org.apache.log4j.PropertyConfigurator;
27 import org.apache.log4j.xml.DOMConfigurator;
28 import org.w3c.dom.Element;
29 import org.w3c.dom.NamedNodeMap;
30 import org.w3c.dom.Node;
31 import org.w3c.dom.NodeList;
33 import edu.internet2.middleware.shibboleth.common.ShibResource;
34 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
35 import edu.internet2.middleware.shibboleth.idp.IdPConfig;
38 * A helper class for configuring the the IdP transaction log, general system log, and any logs specified in a Log4J
41 * The IdP transaction log with the name <code>Shibboleth-TRANSACTION</code> and should be used to track an
42 * individuals path through the IdP. It's default logging level is <code>INFO</code>.
44 * The general system log logs messages from any class in either the <code>edu.internet2.middleware.shibboleth</code>
45 * package or the <code>org.opensaml</code> package. It's default logging level is <code>WARN</code>
47 * All logs are configured through information found in the IdP XML configuration file.
49 * @author Chad La Joie
51 public class LoggingInitializer {
54 * The log file extension
56 private static String logFileExtension = ".log";
59 * Log message layout pattern for the transaction log
61 private static String txLogLayoutPattern = "%d{ISO8601} %m%n";
64 * Date pattern used at the end of the transaction log filename
66 private static String txLogAppenderDatePattern = "'.'yyyy-MM-dd";
69 * Log message layout pattern for the general system log
71 private static String sysLogLayoutPattern = "%d{ISO8601} %-5p %-41X{serviceId} - %m%n";
74 * Date pattern used at the end of the general system log filename
76 private static String sysLogAppenderDatePattern = "'.'yyyy-MM-dd";
79 * Initializes the Log4J logging framework.
81 * @param configuration
82 * logging configuration element from the IdP XML configuration file
83 * @throws ShibbolethConfigurationException
84 * thrown if there is a problem configuring the logs
86 public static void initializeLogging(Element configuration) throws ShibbolethConfigurationException {
88 NodeList txLogElems = configuration.getElementsByTagNameNS(IdPConfig.configNameSpace, "TransactionLog");
89 if (txLogElems.getLength() > 0) {
90 if (txLogElems.getLength() > 1) {
91 System.err.println("WARNING: More than one TransactionLog element detected in IdP logging "
92 + "configuration, only the first one will be used.");
94 Element txLogConfig = (Element) txLogElems.item(0);
95 configureTransactionLog(txLogConfig);
97 configureTransactionLog();
100 NodeList sysLogElems = configuration.getElementsByTagNameNS(IdPConfig.configNameSpace, "ErrorLog");
101 if (sysLogElems.getLength() > 0) {
102 if (sysLogElems.getLength() > 1) {
103 System.err.println("WARNING: More than one ErrorLog element detected in IdP logging configuration, "
104 + "only the first one will be used.");
106 Element sysLogConfig = (Element) sysLogElems.item(0);
107 configureSystemLog(sysLogConfig);
109 configureSystemLog();
112 NodeList log4jElems = configuration.getElementsByTagNameNS(IdPConfig.configNameSpace, "Log4JConfig");
113 if (log4jElems.getLength() > 0) {
114 if (log4jElems.getLength() > 1) {
115 System.err.println("WARNING: More than one Log4JConfig element detected in IdP logging configuration, "
116 + "only the first one will be used.");
118 Element log4jConfig = (Element) log4jElems.item(0);
119 configureLog4J(log4jConfig);
124 * Initialize the logs for the Shibboleth-TRANSACTION log, edu.internet2.middleware.shibboleth, and org.opensaml
125 * logs. Output is directed to the standard out with the the transaction log at INFO level and the remainder at
128 public static void initializeLogging() {
130 configureTransactionLog();
131 configureSystemLog();
135 * Configured the transaction log to log to the console at INFO level.
137 private static void configureTransactionLog() {
139 ConsoleAppender appender = new ConsoleAppender(new PatternLayout(txLogLayoutPattern),
140 ConsoleAppender.SYSTEM_OUT);
141 Logger log = Logger.getLogger("Shibboleth-TRANSACTION");
142 log.setAdditivity(false); // do not want parent's messages
143 log.setLevel(Level.INFO);
144 log.addAppender(appender);
148 * Configures the transaction log.
150 * @param configuration
151 * the TransactionLog element from the IdP XML logging configuration
152 * @throws ShibbolethConfigurationException
153 * thrown if there is a problem configuring the logs
155 private static void configureTransactionLog(Element configuration) throws ShibbolethConfigurationException {
157 NamedNodeMap attributes = configuration.getAttributes();
159 String location = attributes.getNamedItem("location").getNodeValue();
160 if (location == null) { throw new ShibbolethConfigurationException(
161 "No log file location attribute specified in TransactionLog element"); }
163 FileAppender appender = null;
165 String logPath = new ShibResource(location, LoggingInitializer.class).getFile().getCanonicalPath();
166 appender = createRollingFileAppender(txLogLayoutPattern, logPath, txLogAppenderDatePattern);
167 appender.setName("shibboleth-transaction");
168 } catch (Exception e) {
169 throw new ShibbolethConfigurationException("<TransactionLog location=\"" + location
170 + "\">: error creating DailyRollingFileAppender: " + e);
173 Level level = Level.INFO;
174 if (attributes.getNamedItem("level") != null) {
175 level = Level.toLevel(attributes.getNamedItem("level").getNodeValue());
178 Logger log = Logger.getLogger("Shibboleth-TRANSACTION");
179 log.setAdditivity(false); // do not want parent's messages
181 log.addAppender(appender);
185 * Configures the standard system log to log messages from edu.internet2.middleware.shibboleth and org.opensaml to
186 * the console at WARN level.
188 private static void configureSystemLog() {
190 ConsoleAppender appender = new ConsoleAppender(new PatternLayout(sysLogLayoutPattern),
191 ConsoleAppender.SYSTEM_OUT);
192 Logger shibLog = Logger.getLogger("edu.internet2.middleware.shibboleth");
193 shibLog.setLevel(Level.WARN);
194 shibLog.addAppender(appender);
196 Logger openSAMLLog = Logger.getLogger("org.opensaml");
197 openSAMLLog.setLevel(Level.WARN);
198 openSAMLLog.addAppender(appender);
202 * Configures the system-wide IdP log.
204 * @param configuration
205 * the ErrorLog element from the IdP XML logging configuration
206 * @throws ShibbolethConfigurationException
207 * thrown if there is a problem configuring the logs
209 private static void configureSystemLog(Element configuration) throws ShibbolethConfigurationException {
211 NamedNodeMap attributes = configuration.getAttributes();
213 String location = attributes.getNamedItem("location").getNodeValue();
214 if (location == null) { throw new ShibbolethConfigurationException(
215 "No log file location attribute specified in ErrorLog element"); }
217 FileAppender appender = null;
219 String logPath = new ShibResource(location, LoggingInitializer.class).getFile().getCanonicalPath();
220 appender = createRollingFileAppender(sysLogLayoutPattern, logPath, sysLogAppenderDatePattern);
221 appender.setName("shibboleth-error");
222 } catch (Exception e) { // catch any exception
223 throw new ShibbolethConfigurationException("<ErrorLog location=\"" + location
224 + "\">: error creating DailyRollingFileAppender: " + e);
227 Level level = Level.WARN;
228 if (attributes.getNamedItem("level") != null) {
229 level = Level.toLevel(attributes.getNamedItem("level").getNodeValue());
232 Logger shibLog = Logger.getLogger("edu.internet2.middleware.shibboleth");
233 shibLog.setLevel(level);
234 shibLog.addAppender(appender);
236 Logger openSAMLLog = Logger.getLogger("org.opensaml");
237 openSAMLLog.setLevel(level);
238 openSAMLLog.addAppender(appender);
242 * Creates a rolling file appender. If the given log file ends with .* the characters after the .
243 * will be treated as the logs extension. If there is no . in the log file path a default extension
244 * of "log" will be used. When the log file is rolled the resulting file name is "logfile"."date"."extension".
246 * @param messagePattern patterns for the log messages
247 * @param logFile the log file
248 * @param datePattern the date pattern to roll the file one
250 * @return a rolling file appender
252 * @throws IOException thrown if the appender can not create the initial log file
254 private static FileAppender createRollingFileAppender(String messagePattern, String logFile, String datePattern) throws IOException {
255 PatternLayout messageLayout = new PatternLayout(messagePattern);
257 int fileExtDelimIndex = logFile.lastIndexOf(".");
258 if(fileExtDelimIndex <= 0) {
259 return new RollingFileAppender(messageLayout, logFile, datePattern, ".log");
261 String filePath = logFile.substring(0, fileExtDelimIndex);
262 String fileExtension = logFile.substring(fileExtDelimIndex);
264 return new RollingFileAppender(messageLayout, filePath, datePattern, fileExtension);
269 * Configures Log4J by way of a Log4J specific configuration file.
271 * @param configuration
272 * the Log4JConfig element from the IdP XML logging configuration
273 * @throws ShibbolethConfigurationException
274 * thrown if there is a problem configuring the logs
276 private static void configureLog4J(Element configuration) throws ShibbolethConfigurationException {
278 NamedNodeMap attributes = configuration.getAttributes();
280 String location = attributes.getNamedItem("location").getNodeValue();
281 if (location == null) { throw new ShibbolethConfigurationException(
282 "No configuration file location attribute specified in Log4JConfig element"); }
285 Node typeNode = attributes.getNamedItem("type");
286 if (typeNode != null) {
287 type = typeNode.getNodeValue();
290 ShibResource log4jConfig;
292 log4jConfig = new ShibResource(location);
293 if (type == null || "properties".equals(type)) {
294 PropertyConfigurator.configure(log4jConfig.getURL());
295 } else if ("xml".equals(type)) {
296 DOMConfigurator.configure(log4jConfig.getURL());
298 throw new ShibbolethConfigurationException(
299 "<Log4JConfig (type) attribute must be one of \"xml\" or \"properties\".");
301 } catch (IOException e) {
302 throw new ShibbolethConfigurationException("<Log4JConfig location=\"" + location + "\">: not a valid URL: "