001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.oozie.service; 019 020import org.apache.commons.logging.LogFactory; 021import org.apache.log4j.LogManager; 022import org.apache.log4j.PropertyConfigurator; 023import org.apache.oozie.util.XLogFilter; 024import org.apache.oozie.util.Instrumentable; 025import org.apache.oozie.util.Instrumentation; 026import org.apache.oozie.util.XLog; 027import org.apache.oozie.util.XConfiguration; 028import org.apache.oozie.BuildInfo; 029import org.apache.oozie.ErrorCode; 030import org.apache.hadoop.conf.Configuration; 031 032import java.io.File; 033import java.io.FileInputStream; 034import java.io.IOException; 035import java.io.InputStream; 036import java.net.URL; 037import java.util.Properties; 038import java.util.Map; 039 040/** 041 * Built-in service that initializes and manages Logging via Log4j. 042 * <p> 043 * Oozie Lo4gj default configuration file is <code>oozie-log4j.properties</code>. 044 * <p> 045 * The file name can be changed by setting the Java System property <code>oozie.log4j.file</code>. 046 * <p> 047 * The Log4j configuration files must be a properties file. 048 * <p> 049 * The Log4j configuration file is first looked in the Oozie configuration directory see {@link ConfigurationService}. 050 * If the file is not found there, it is looked in the classpath. 051 * <p> 052 * If the Log4j configuration file is loaded from Oozie configuration directory, automatic reloading is enabled. 053 * <p> 054 * If the Log4j configuration file is loaded from the classpath, automatic reloading is disabled. 055 * <p> 056 * the automatic reloading interval is defined by the Java System property <code>oozie.log4j.reload</code>. The default 057 * value is 10 seconds. 058 * <p> 059 * <p> 060 * Unlike most of the other Services, XLogService isn't easily overridable because Services depends on XLogService being available 061 */ 062public class XLogService implements Service, Instrumentable { 063 private static final String INSTRUMENTATION_GROUP = "logging"; 064 065 /** 066 * System property that indicates the logs directory. 067 */ 068 public static final String OOZIE_LOG_DIR = "oozie.log.dir"; 069 070 /** 071 * System property that indicates the log4j configuration file to load. 072 */ 073 public static final String LOG4J_FILE = "oozie.log4j.file"; 074 075 /** 076 * System property that indicates the reload interval of the configuration file. 077 */ 078 public static final String LOG4J_RELOAD = "oozie.log4j.reload"; 079 080 /** 081 * Default value for the log4j configuration file if {@link #LOG4J_FILE} is not set. 082 */ 083 public static final String DEFAULT_LOG4J_PROPERTIES = "oozie-log4j.properties"; 084 085 /** 086 * Default value for the reload interval if {@link #LOG4J_RELOAD} is not set. 087 */ 088 public static final String DEFAULT_RELOAD_INTERVAL = "10"; 089 090 private XLog log; 091 private long interval; 092 private boolean fromClasspath; 093 private String log4jFileName; 094 private boolean logOverWS = true; 095 private boolean errorLogEnabled = true; 096 private boolean auditLogEnabled = true; 097 098 099 private static final String STARTUP_MESSAGE = "{E}" 100 + " ******************************************************************************* {E}" 101 + " STARTUP MSG: Oozie BUILD_VERSION [{0}] compiled by [{1}] on [{2}]{E}" 102 + " STARTUP MSG: revision [{3}]@[{4}]{E}" 103 + "*******************************************************************************"; 104 105 private String oozieLogPath; 106 private String oozieLogName; 107 private String oozieErrorLogPath; 108 private String oozieErrorLogName; 109 private String oozieAuditLogPath; 110 private String oozieAuditLogName; 111 private int oozieLogRotation = -1; 112 private int oozieErrorLogRotation = -1; 113 private int oozieAuditLogRotation = -1; 114 115 116 117 public XLogService() { 118 } 119 120 public String getOozieLogPath() { 121 return oozieLogPath; 122 } 123 124 public String getOozieErrorLogPath() { 125 return oozieErrorLogPath; 126 } 127 128 public String getOozieLogName() { 129 return oozieLogName; 130 } 131 132 public String getOozieErrorLogName() { 133 return oozieErrorLogName; 134 } 135 136 137 /** 138 * Initialize the log service. 139 * 140 * @param services services instance. 141 * @throws ServiceException thrown if the log service could not be initialized. 142 */ 143 public void init(Services services) throws ServiceException { 144 String oozieHome = Services.getOozieHome(); 145 String oozieLogs = System.getProperty(OOZIE_LOG_DIR, oozieHome + "/logs"); 146 System.setProperty(OOZIE_LOG_DIR, oozieLogs); 147 148 try { 149 LogManager.resetConfiguration(); 150 log4jFileName = System.getProperty(LOG4J_FILE, DEFAULT_LOG4J_PROPERTIES); 151 if (log4jFileName.contains("/")) { 152 throw new ServiceException(ErrorCode.E0011, log4jFileName); 153 } 154 if (!log4jFileName.endsWith(".properties")) { 155 throw new ServiceException(ErrorCode.E0012, log4jFileName); 156 } 157 String configPath = ConfigurationService.getConfigurationDirectory(); 158 File log4jFile = new File(configPath, log4jFileName); 159 if (log4jFile.exists()) { 160 fromClasspath = false; 161 } 162 else { 163 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 164 URL log4jUrl = cl.getResource(log4jFileName); 165 if (log4jUrl == null) { 166 throw new ServiceException(ErrorCode.E0013, log4jFileName, configPath); 167 } 168 fromClasspath = true; 169 } 170 171 if (fromClasspath) { 172 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 173 URL log4jUrl = cl.getResource(log4jFileName); 174 PropertyConfigurator.configure(log4jUrl); 175 } 176 else { 177 interval = Long.parseLong(System.getProperty(LOG4J_RELOAD, DEFAULT_RELOAD_INTERVAL)); 178 PropertyConfigurator.configureAndWatch(log4jFile.toString(), interval * 1000); 179 } 180 181 log = new XLog(LogFactory.getLog(getClass())); 182 183 log.info(XLog.OPS, STARTUP_MESSAGE, BuildInfo.getBuildInfo().getProperty(BuildInfo.BUILD_VERSION), 184 BuildInfo.getBuildInfo().getProperty(BuildInfo.BUILD_USER_NAME), BuildInfo.getBuildInfo() 185 .getProperty(BuildInfo.BUILD_TIME), BuildInfo.getBuildInfo().getProperty( 186 BuildInfo.BUILD_VC_REVISION), BuildInfo.getBuildInfo().getProperty(BuildInfo.BUILD_VC_URL)); 187 188 String from = (fromClasspath) ? "CLASSPATH" : configPath; 189 String reload = (fromClasspath) ? "disabled" : Long.toString(interval) + " sec"; 190 log.info("Log4j configuration file [{0}]", log4jFileName); 191 log.info("Log4j configuration file loaded from [{0}]", from); 192 log.info("Log4j reload interval [{0}]", reload); 193 194 XLog.Info.reset(); 195 XLog.Info.defineParameter(USER); 196 XLog.Info.defineParameter(GROUP); 197 XLogFilter.reset(); 198 XLogFilter.defineParameter(USER); 199 XLogFilter.defineParameter(GROUP); 200 201 // Getting configuration for oozie log via WS 202 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 203 InputStream is = (fromClasspath) ? cl.getResourceAsStream(log4jFileName) : new FileInputStream(log4jFile); 204 extractInfoForLogWebService(is); 205 } 206 catch (IOException ex) { 207 throw new ServiceException(ErrorCode.E0010, ex.getMessage(), ex); 208 } 209 } 210 211 private void extractInfoForLogWebService(InputStream is) throws IOException { 212 Properties props = new Properties(); 213 props.load(is); 214 215 XConfiguration conf = new XConfiguration(); 216 conf.setRestrictSystemProperties(false); 217 for (Map.Entry entry : props.entrySet()) { 218 conf.set((String) entry.getKey(), (String) entry.getValue()); 219 } 220 221 XLogUtil logUtil = new XLogUtil(conf, "oozie"); 222 logOverWS = logUtil.isLogOverEnable(); 223 oozieLogRotation = logUtil.getLogRotation() == 0 ? oozieLogRotation : logUtil.getLogRotation(); 224 oozieLogPath = logUtil.getLogPath() == null ? oozieLogPath : logUtil.getLogPath(); 225 oozieLogName = logUtil.getLogFileName() == null ? oozieLogName : logUtil.getLogFileName(); 226 227 logUtil = new XLogUtil(conf, "oozieError"); 228 errorLogEnabled = logUtil.isLogOverEnable(); 229 oozieErrorLogRotation = logUtil.getLogRotation() == 0 ? oozieErrorLogRotation : logUtil.getLogRotation(); 230 oozieErrorLogPath = logUtil.getLogPath() == null ? oozieErrorLogPath : logUtil.getLogPath(); 231 oozieErrorLogName = logUtil.getLogFileName() == null ? oozieErrorLogName : logUtil.getLogFileName(); 232 233 logUtil = new XLogUtil(conf, "oozieaudit"); 234 auditLogEnabled = logUtil.isLogOverEnable(); 235 oozieAuditLogRotation = logUtil.getLogRotation() == 0 ? oozieAuditLogRotation : logUtil.getLogRotation(); 236 oozieAuditLogPath = logUtil.getLogPath() == null ? oozieAuditLogPath : logUtil.getLogPath(); 237 oozieAuditLogName = logUtil.getLogFileName() == null ? oozieAuditLogName : logUtil.getLogFileName(); 238 239 } 240 241 /** 242 * Destroy the log service. 243 */ 244 public void destroy() { 245 LogManager.shutdown(); 246 XLog.Info.reset(); 247 XLogFilter.reset(); 248 } 249 250 /** 251 * Group log info constant. 252 */ 253 public static final String USER = "USER"; 254 255 /** 256 * Group log info constant. 257 */ 258 public static final String GROUP = "GROUP"; 259 260 /** 261 * Return the public interface for log service. 262 * 263 * @return {@link XLogService}. 264 */ 265 public Class<? extends Service> getInterface() { 266 return XLogService.class; 267 } 268 269 /** 270 * Instruments the log service. 271 * <p> 272 * It sets instrumentation variables indicating the config file, reload interval and if loaded from the classpath. 273 * 274 * @param instr instrumentation to use. 275 */ 276 public void instrument(Instrumentation instr) { 277 278 instr.addVariable(INSTRUMENTATION_GROUP, "config.file", new Instrumentation.Variable<String>() { 279 public String getValue() { 280 return log4jFileName; 281 } 282 }); 283 instr.addVariable(INSTRUMENTATION_GROUP, "reload.interval", new Instrumentation.Variable<Long>() { 284 public Long getValue() { 285 return interval; 286 } 287 }); 288 instr.addVariable(INSTRUMENTATION_GROUP, "from.classpath", new Instrumentation.Variable<Boolean>() { 289 public Boolean getValue() { 290 return fromClasspath; 291 } 292 }); 293 instr.addVariable(INSTRUMENTATION_GROUP, "log.over.web-service", new Instrumentation.Variable<Boolean>() { 294 public Boolean getValue() { 295 return logOverWS; 296 } 297 }); 298 } 299 300 boolean getLogOverWS() { 301 return logOverWS; 302 } 303 304 boolean isErrorLogEnabled(){ 305 return errorLogEnabled; 306 } 307 308 int getOozieLogRotation() { 309 return oozieLogRotation; 310 } 311 312 int getOozieErrorLogRotation() { 313 return oozieErrorLogRotation; 314 } 315 316 int getOozieAuditLogRotation() { 317 return oozieAuditLogRotation; 318 } 319 320 public String getOozieAuditLogPath() { 321 return oozieAuditLogPath; 322 } 323 324 public String getOozieAuditLogName() { 325 return oozieAuditLogName; 326 } 327 328 boolean isAuditLogEnabled() { 329 return auditLogEnabled; 330 } 331 332 String getLog4jProperties() { 333 return log4jFileName; 334 } 335 336 boolean getFromClasspath() { 337 return fromClasspath; 338 } 339 340}