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