Changed to use RollingFileAppender to allow for log file names to end with a log...
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / log / LoggingInitializer.java
1 /*
2  * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package edu.internet2.middleware.shibboleth.log;
18
19 import java.io.IOException;
20
21 import org.apache.log4j.ConsoleAppender;
22 import org.apache.log4j.Level;
23 import org.apache.log4j.Logger;
24 import org.apache.log4j.PatternLayout;
25 import org.apache.log4j.PropertyConfigurator;
26 import org.apache.log4j.xml.DOMConfigurator;
27 import org.w3c.dom.Element;
28 import org.w3c.dom.NamedNodeMap;
29 import org.w3c.dom.Node;
30 import org.w3c.dom.NodeList;
31
32 import edu.internet2.middleware.shibboleth.common.ShibResource;
33 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
34 import edu.internet2.middleware.shibboleth.idp.IdPConfig;
35
36 /**
37  * A helper class for configuring the the IdP transaction log, general system log, and any logs specified in a Log4J
38  * configuration file.
39  * <p>
40  * The IdP transaction log with the name <code>Shibboleth-TRANSACTION</code> and should be used to track an
41  * individuals path through the IdP. It's default logging level is <code>INFO</code>.
42  * <p>
43  * The general system log logs messages from any class in either the <code>edu.internet2.middleware.shibboleth</code>
44  * package or the <code>org.opensaml</code> package. It's default logging level is <code>WARN</code>
45  * <p>
46  * All logs are configured through information found in the IdP XML configuration file.
47  * 
48  * @author Chad La Joie
49  */
50 public class LoggingInitializer {
51
52     /**
53      * The log file extension
54      */
55     private static String logFileExtension = ".log";
56     
57         /**
58          * Log message layout pattern for the transaction log
59          */
60         private static String txLogLayoutPattern = "%d{ISO8601} %m%n";
61
62         /**
63          * Date pattern used at the end of the transaction log filename
64          */
65         private static String txLogAppenderDatePattern = "'.'yyyy-MM-dd";
66
67         /**
68          * Log message layout pattern for the general system log
69          */
70         private static String sysLogLayoutPattern = "%d{ISO8601} %-5p %-41X{serviceId} - %m%n";
71
72         /**
73          * Date pattern used at the end of the general system log filename
74          */
75         private static String sysLogAppenderDatePattern = "'.'yyyy-MM-dd";
76
77         /**
78          * Initializes the Log4J logging framework.
79          * 
80          * @param configuration
81          *            logging configuration element from the IdP XML configuration file
82          * @throws ShibbolethConfigurationException
83          *             thrown if there is a problem configuring the logs
84          */
85         public static void initializeLogging(Element configuration) throws ShibbolethConfigurationException {
86
87                 NodeList txLogElems = configuration.getElementsByTagNameNS(IdPConfig.configNameSpace, "TransactionLog");
88                 if (txLogElems.getLength() > 0) {
89                         if (txLogElems.getLength() > 1) {
90                                 System.err.println("WARNING: More than one TransactionLog element detected in IdP logging "
91                                                 + "configuration, only the first one will be used.");
92                         }
93                         Element txLogConfig = (Element) txLogElems.item(0);
94                         configureTransactionLog(txLogConfig);
95                 } else {
96                         configureTransactionLog();
97                 }
98
99                 NodeList sysLogElems = configuration.getElementsByTagNameNS(IdPConfig.configNameSpace, "ErrorLog");
100                 if (sysLogElems.getLength() > 0) {
101                         if (sysLogElems.getLength() > 1) {
102                                 System.err.println("WARNING: More than one ErrorLog element detected in IdP logging configuration, "
103                                                 + "only the first one will be used.");
104                         }
105                         Element sysLogConfig = (Element) sysLogElems.item(0);
106                         configureSystemLog(sysLogConfig);
107                 } else {
108                         configureSystemLog();
109                 }
110
111                 NodeList log4jElems = configuration.getElementsByTagNameNS(IdPConfig.configNameSpace, "Log4JConfig");
112                 if (log4jElems.getLength() > 0) {
113                         if (log4jElems.getLength() > 1) {
114                                 System.err.println("WARNING: More than one Log4JConfig element detected in IdP logging configuration, "
115                                                 + "only the first one will be used.");
116                         }
117                         Element log4jConfig = (Element) log4jElems.item(0);
118                         configureLog4J(log4jConfig);
119                 }
120         }
121
122         /**
123          * Initialize the logs for the Shibboleth-TRANSACTION log, edu.internet2.middleware.shibboleth, and org.opensaml
124          * logs. Output is directed to the standard out with the the transaction log at INFO level and the remainder at
125          * warn.
126          */
127         public static void initializeLogging() {
128
129                 configureTransactionLog();
130                 configureSystemLog();
131         }
132
133         /**
134          * Configured the transaction log to log to the console at INFO level.
135          */
136         private static void configureTransactionLog() {
137
138                 ConsoleAppender appender = new ConsoleAppender(new PatternLayout(txLogLayoutPattern),
139                                 ConsoleAppender.SYSTEM_OUT);
140                 Logger log = Logger.getLogger("Shibboleth-TRANSACTION");
141                 log.setAdditivity(false); // do not want parent's messages
142                 log.setLevel(Level.INFO);
143                 log.addAppender(appender);
144         }
145
146         /**
147          * Configures the transaction log.
148          * 
149          * @param configuration
150          *            the TransactionLog element from the IdP XML logging configuration
151          * @throws ShibbolethConfigurationException
152          *             thrown if there is a problem configuring the logs
153          */
154         private static void configureTransactionLog(Element configuration) throws ShibbolethConfigurationException {
155
156                 NamedNodeMap attributes = configuration.getAttributes();
157
158                 String location = attributes.getNamedItem("location").getNodeValue();
159                 if (location == null) { throw new ShibbolethConfigurationException(
160                                 "No log file location attribute specified in TransactionLog element"); }
161
162                 RollingFileAppender appender = null;
163                 try {
164                         String logPath = new ShibResource(location, LoggingInitializer.class).getFile().getCanonicalPath();
165                         PatternLayout messageLayout = new PatternLayout(txLogLayoutPattern);
166
167                         appender = new RollingFileAppender(messageLayout, logPath, txLogAppenderDatePattern, logFileExtension);
168                         appender.setName("shibboleth-transaction");
169                 } catch (Exception e) {
170                         throw new ShibbolethConfigurationException("<TransactionLog location=\"" + location
171                                         + "\">: error creating DailyRollingFileAppender: " + e);
172                 }
173
174                 Level level = Level.INFO;
175                 if (attributes.getNamedItem("level") != null) {
176                         level = Level.toLevel(attributes.getNamedItem("level").getNodeValue());
177                 }
178
179                 Logger log = Logger.getLogger("Shibboleth-TRANSACTION");
180                 log.setAdditivity(false); // do not want parent's messages
181                 log.setLevel(level);
182                 log.addAppender(appender);
183         }
184
185         /**
186          * Configures the standard system log to log messages from edu.internet2.middleware.shibboleth and org.opensaml to
187          * the console at WARN level.
188          */
189         private static void configureSystemLog() {
190
191                 ConsoleAppender appender = new ConsoleAppender(new PatternLayout(sysLogLayoutPattern),
192                                 ConsoleAppender.SYSTEM_OUT);
193                 Logger shibLog = Logger.getLogger("edu.internet2.middleware.shibboleth");
194                 shibLog.setLevel(Level.WARN);
195                 shibLog.addAppender(appender);
196
197                 Logger openSAMLLog = Logger.getLogger("org.opensaml");
198                 openSAMLLog.setLevel(Level.WARN);
199                 openSAMLLog.addAppender(appender);
200         }
201
202         /**
203          * Configures the system-wide IdP log.
204          * 
205          * @param configuration
206          *            the ErrorLog element from the IdP XML logging configuration
207          * @throws ShibbolethConfigurationException
208          *             thrown if there is a problem configuring the logs
209          */
210         private static void configureSystemLog(Element configuration) throws ShibbolethConfigurationException {
211
212                 NamedNodeMap attributes = configuration.getAttributes();
213
214                 String location = attributes.getNamedItem("location").getNodeValue();
215                 if (location == null) { throw new ShibbolethConfigurationException(
216                                 "No log file location attribute specified in ErrorLog element"); }
217
218                 RollingFileAppender appender = null;
219                 try {
220                         String logPath = new ShibResource(location, LoggingInitializer.class).getFile().getCanonicalPath();
221                         PatternLayout messageLayout = new PatternLayout(sysLogLayoutPattern);
222
223                         appender = new RollingFileAppender(messageLayout, logPath, sysLogAppenderDatePattern, logFileExtension);
224                         appender.setName("shibboleth-error");
225                 } catch (Exception e) { // catch any exception
226                         throw new ShibbolethConfigurationException("<ErrorLog location=\"" + location
227                                         + "\">: error creating DailyRollingFileAppender: " + e);
228                 }
229
230                 Level level = Level.WARN;
231                 if (attributes.getNamedItem("level") != null) {
232                         level = Level.toLevel(attributes.getNamedItem("level").getNodeValue());
233                 }
234
235                 Logger shibLog = Logger.getLogger("edu.internet2.middleware.shibboleth");
236                 shibLog.setLevel(level);
237                 shibLog.addAppender(appender);
238
239                 Logger openSAMLLog = Logger.getLogger("org.opensaml");
240                 openSAMLLog.setLevel(level);
241                 openSAMLLog.addAppender(appender);
242         }
243
244         /**
245          * Configures Log4J by way of a Log4J specific configuration file.
246          * 
247          * @param configuration
248          *            the Log4JConfig element from the IdP XML logging configuration
249          * @throws ShibbolethConfigurationException
250          *             thrown if there is a problem configuring the logs
251          */
252         private static void configureLog4J(Element configuration) throws ShibbolethConfigurationException {
253
254                 NamedNodeMap attributes = configuration.getAttributes();
255
256                 String location = attributes.getNamedItem("location").getNodeValue();
257                 if (location == null) { throw new ShibbolethConfigurationException(
258                                 "No configuration file location attribute specified in Log4JConfig element"); }
259
260                 String type = null;
261                 Node typeNode = attributes.getNamedItem("type");
262                 if (typeNode != null) {
263                         type = typeNode.getNodeValue();
264                 }
265
266                 ShibResource log4jConfig;
267                 try {
268                         log4jConfig = new ShibResource(location);
269                         if (type == null || "properties".equals(type)) {
270                                 PropertyConfigurator.configure(log4jConfig.getURL());
271                         } else if ("xml".equals(type)) {
272                                 DOMConfigurator.configure(log4jConfig.getURL());
273                         } else {
274                                 throw new ShibbolethConfigurationException(
275                                                 "<Log4JConfig (type) attribute must be one of \"xml\" or \"properties\".");
276                         }
277                 } catch (IOException e) {
278                         throw new ShibbolethConfigurationException("<Log4JConfig location=\"" + location + "\">: not a valid URL: "
279                                         + e);
280                 }
281
282         }
283 }