1 5 package com.tc.logging; 6 7 import org.apache.commons.io.FileUtils; 8 import org.apache.log4j.Appender; 9 import org.apache.log4j.ConsoleAppender; 10 import org.apache.log4j.Level; 11 import org.apache.log4j.Logger; 12 import org.apache.log4j.PatternLayout; 13 import org.apache.log4j.PropertyConfigurator; 14 import org.apache.log4j.RollingFileAppender; 15 import org.apache.log4j.varia.NullAppender; 16 17 import com.tc.util.Assert; 18 import com.tc.util.ProductInfo; 19 20 import java.io.File ; 21 import java.io.IOException ; 22 import java.io.PrintWriter ; 23 import java.io.RandomAccessFile ; 24 import java.io.StringWriter ; 25 import java.nio.channels.FileChannel ; 26 import java.nio.channels.FileLock ; 27 import java.nio.channels.OverlappingFileLockException ; 28 import java.util.Arrays ; 29 import java.util.Iterator ; 30 import java.util.Properties ; 31 32 37 public class TCLogging { 38 39 private static final int MAX_BUFFERED_LOG_MESSAGES = 10 * 1000; 40 41 private static final String TERRACOTTA_L1_LOG_FILE_NAME = "terracotta-client.log"; 42 private static final String TERRACOTTA_L2_LOG_FILE_NAME = "terracotta-server.log"; 43 private static final String TERRACOTTA_GENERIC_LOG_FILE_NAME = "terracotta-generic.log"; 44 45 private static final String LOCK_FILE_NAME = ".terracotta-logging.lock"; 46 47 private static final String INTERNAL_LOGGER_NAMESPACE = "com.tc"; 48 private static final String INTERNAL_LOGGER_NAMESPACE_WITH_DOT = INTERNAL_LOGGER_NAMESPACE + "."; 49 50 private static final String CUSTOMER_LOGGER_NAMESPACE = "com.terracottatech"; 51 private static final String CUSTOMER_LOGGER_NAMESPACE_WITH_DOT = CUSTOMER_LOGGER_NAMESPACE + "."; 52 53 private static final String CONSOLE_LOGGER_NAME = CUSTOMER_LOGGER_NAMESPACE + ".console"; 54 55 private static final String BENCH_LOGGER_NAME = "terracotta.bench"; 56 private static final String MAX_LOG_FILE_SIZE = "512MB"; 57 private static final int MAX_BACKUPS = 20; 58 private static final String LOG4J_PROPERTIES_FILENAME = ".tc.dev.log4j.properties"; 59 60 private static final String CONSOLE_PATTERN = "%d %p - %m%n"; 61 private static final String CONSOLE_PATTERN_DEVELOPMENT = "%d [%t] %p %c - %m%n"; 62 private static final String CONSOLE_LOGGING_ONLY_PATTERN = "[TC] %d %p - %m%n"; 64 private static final String FILE_AND_JMX_PATTERN = "%d [%t] %p %c - %m%n"; 65 66 private static TCLogger console; 67 private static JMXAppender jmxAppender; 68 private static Appender consoleAppender; 69 private static DelegatingAppender delegateFileAppender; 70 private static DelegatingAppender delegateBufferingAppender; 71 private static boolean buffering; 72 73 private static Logger[] allLoggers; 74 75 private static File currentLoggingDirectory; 76 private static FileLock currentLoggingDirectoryFileLock = null; 77 private static boolean lockingDisabled = false; 78 79 public static JMXAppender getJMXAppender() { 80 return jmxAppender; 81 } 82 83 public static TCLogger getLogger(Class clazz) { 84 if (clazz == null) { throw new IllegalArgumentException ("Class cannot be null"); } 85 return getLogger(clazz.getName()); 86 } 87 88 public static TCLogger getLogger(String name) { 89 if ((name == null) || !name.startsWith(INTERNAL_LOGGER_NAMESPACE_WITH_DOT)) { 90 throw new IllegalArgumentException ("Logger not in valid namepsace (ie. '" + INTERNAL_LOGGER_NAMESPACE_WITH_DOT 92 + "' ): " + name); 93 } 94 95 return new TCLoggerImpl(name); 96 } 97 98 101 public static TCLogger getTestingLogger(String name) { 102 if (name == null) { throw new IllegalArgumentException ("Name cannot be null"); } 103 return new TCLoggerImpl(name); 104 } 105 106 109 public static TCLogger getTestingLogger(Class clazz) { 110 if (clazz == null) { throw new IllegalArgumentException ("Class cannot be null"); } 111 return getTestingLogger(clazz.getName()); 112 } 113 114 public static TCLogger getBenchLogger() { 115 return new TCLoggerImpl(BENCH_LOGGER_NAME); 116 } 117 118 static TCLogger getCustomerLogger(String name) { 120 if (name == null) { throw new IllegalArgumentException ("name cannot be null"); } 121 122 name = CUSTOMER_LOGGER_NAMESPACE_WITH_DOT + name; 123 124 if (CONSOLE_LOGGER_NAME.equals(name)) { throw new IllegalArgumentException ("Illegal name: " + name); } 125 126 return new TCLoggerImpl(name); 127 } 128 129 static TCLogger getConsoleLogger() { 131 return console; 132 } 133 134 private static void reportLoggingError(Exception e) { 135 reportLoggingError(null, e); 136 } 137 138 private static void reportLoggingError(String message, Exception e) { 139 StringBuffer errorMsg = new StringBuffer ("\n"); 140 141 if (message != null) { 142 errorMsg.append("WARN: ").append(message).append("\n"); 143 } 144 145 if (e != null) { 146 StringWriter sw = new StringWriter (); 147 e.printStackTrace(new PrintWriter (sw)); 148 errorMsg.append(sw.toString()); 149 } 150 151 System.err.println(errorMsg.toString()); 152 } 153 154 private static boolean developmentConfiguration() { 155 try { 156 File props = new File (System.getProperty("user.dir"), LOG4J_PROPERTIES_FILENAME); 157 158 if (props.canRead() && props.isFile()) { 159 Logger.getRootLogger().setLevel(Level.INFO); 160 PropertyConfigurator.configure(props.getAbsolutePath()); 161 return true; 162 } 163 } catch (Exception e) { 164 reportLoggingError(e); 165 } 166 167 return false; 168 } 169 170 176 public static synchronized void disableLocking() { 177 lockingDisabled = true; 178 179 if (currentLoggingDirectoryFileLock != null) { 180 try { 181 currentLoggingDirectoryFileLock.release(); 182 currentLoggingDirectoryFileLock = null; 183 } catch (IOException ioe) { 184 throw Assert.failure("Unable to release file lock?", ioe); 185 } 186 } 187 } 188 189 public static final int PROCESS_TYPE_GENERIC = 0; 190 public static final int PROCESS_TYPE_L1 = 1; 191 public static final int PROCESS_TYPE_L2 = 2; 192 193 public static void setLogDirectory(File theDirectory, int processType) { 194 Assert.assertNotNull(theDirectory); 195 196 if (theDirectory.getName().trim().equalsIgnoreCase("stdout:") 197 || theDirectory.getName().trim().equalsIgnoreCase("stderr:")) { 198 if (currentLoggingDirectory != null 199 && currentLoggingDirectory.getName().trim().equalsIgnoreCase(theDirectory.getName())) { 200 return; 202 } 203 204 delegateFileAppender.setDelegate(new NullAppender()); 205 consoleAppender.setLayout(new PatternLayout(CONSOLE_LOGGING_ONLY_PATTERN)); 206 207 Logger.getRootLogger().addAppender(consoleAppender); 209 210 if (buffering) { 211 BufferingAppender realBufferingAppender = (BufferingAppender) delegateBufferingAppender 212 .setDelegate(new NullAppender()); 213 realBufferingAppender.stopAndSendContentsTo(consoleAppender); 214 realBufferingAppender.close(); 215 buffering = false; 216 } 217 218 boolean stdout = theDirectory.getName().trim().equalsIgnoreCase("stdout:"); 219 getConsoleLogger().info("All logging information now output to standard " + (stdout ? "output" : "error") + "."); 220 221 return; 222 } 223 224 synchronized (TCLogging.class) { 225 if (currentLoggingDirectory != null) { 226 try { 227 if (theDirectory.getCanonicalPath().equals(currentLoggingDirectory.getCanonicalPath())) { return; } 228 } catch (IOException ioe) { 229 } 231 } 232 } 233 234 try { 235 FileUtils.forceMkdir(theDirectory); 236 } catch (IOException ioe) { 237 reportLoggingError("We can't create the directory '" + theDirectory.getAbsolutePath() 238 + "' that you specified for your logs.", ioe); 239 return; 240 } 241 242 if (!theDirectory.canWrite()) { 243 reportLoggingError("The log directory, '" + theDirectory.getAbsolutePath() + "', can't be written to.", null); 245 return; 246 } 247 248 FileLock thisDirectoryLock = null; 249 250 if (!lockingDisabled) { 251 File lockFile = new File (theDirectory, LOCK_FILE_NAME); 252 253 try { 254 if (!lockFile.exists()) lockFile.createNewFile(); 255 Assert.eval(lockFile.exists()); 256 FileChannel channel = new RandomAccessFile (lockFile, "rw").getChannel(); 257 thisDirectoryLock = channel.tryLock(); 258 259 if (thisDirectoryLock == null) { 260 reportLoggingError("The log directory, '" + theDirectory.getAbsolutePath() 261 + "', is already in use by another " + "Terracotta process. Logging will proceed to the console only.", 262 null); 263 return; 264 } 265 266 } catch (OverlappingFileLockException ofle) { 267 } catch (IOException ioe) { 269 reportLoggingError("We can't lock the file '" + lockFile.getAbsolutePath() + "', to make sure that only one " 270 + "Terracotta process is using this directory for logging. This may be a permission " 271 + "issue, or some unexpected error. Logging will proceed to the console only.", ioe); 272 return; 273 } 274 } 275 276 RollingFileAppender newFileAppender; 277 278 String logFileName; 279 280 switch (processType) { 281 case PROCESS_TYPE_L1: 282 logFileName = TERRACOTTA_L1_LOG_FILE_NAME; 283 break; 284 285 case PROCESS_TYPE_L2: 286 logFileName = TERRACOTTA_L2_LOG_FILE_NAME; 287 break; 288 289 case PROCESS_TYPE_GENERIC: 290 logFileName = TERRACOTTA_GENERIC_LOG_FILE_NAME; 291 break; 292 293 default: 294 throw Assert.failure("Unknown process type: " + processType); 295 } 296 297 String logFilePath = new File (theDirectory, logFileName).getAbsolutePath(); 298 299 synchronized (TCLogging.class) { 300 try { 301 newFileAppender = new RollingFileAppender(new PatternLayout(FILE_AND_JMX_PATTERN), logFilePath, true); 302 newFileAppender.setName("file appender"); 303 newFileAppender.setMaxFileSize(MAX_LOG_FILE_SIZE); 304 newFileAppender.setMaxBackupIndex(MAX_BACKUPS); 305 306 newFileAppender.rollOver(); 308 309 Appender oldFileAppender = delegateFileAppender.setDelegate(newFileAppender); 312 313 if (oldFileAppender != null) { 314 oldFileAppender.close(); 315 } 316 317 if (buffering) { 318 BufferingAppender realBufferingAppender = (BufferingAppender) delegateBufferingAppender 319 .setDelegate(new NullAppender()); 320 realBufferingAppender.stopAndSendContentsTo(delegateFileAppender); 321 realBufferingAppender.close(); 322 buffering = false; 323 } 324 325 currentLoggingDirectory = theDirectory; 326 327 if (currentLoggingDirectoryFileLock != null) currentLoggingDirectoryFileLock.release(); 328 currentLoggingDirectoryFileLock = thisDirectoryLock; 329 } catch (IOException ioe) { 330 reportLoggingError("We were unable to switch the logging system to log to '" + logFilePath + "'.", ioe); 331 } 332 } 333 334 getConsoleLogger().info("Log file: '" + logFilePath + "'."); 335 writeSystemProperties(); 336 } 337 338 static { 339 ClassLoader prevLoader = Thread.currentThread().getContextClassLoader(); 340 Thread.currentThread().setContextClassLoader(TCLogging.class.getClassLoader()); 341 342 try { 343 currentLoggingDirectory = null; 344 345 Logger jettyLogger = Logger.getLogger("org.mortbay"); 346 jettyLogger.setLevel(Level.OFF); 347 348 Logger internalLogger = Logger.getLogger(INTERNAL_LOGGER_NAMESPACE); 349 Logger customerLogger = Logger.getLogger(CUSTOMER_LOGGER_NAMESPACE); 350 Logger consoleLogger = Logger.getLogger(CONSOLE_LOGGER_NAME); 351 Logger benchLogger = Logger.getLogger(BENCH_LOGGER_NAME); 352 353 allLoggers = new Logger[] { internalLogger, customerLogger, consoleLogger, benchLogger }; 354 355 console = new TCLoggerImpl(CONSOLE_LOGGER_NAME); 356 357 internalLogger.setLevel(Level.INFO); 358 customerLogger.setLevel(Level.INFO); 359 consoleLogger.setLevel(Level.INFO); 360 benchLogger.setLevel(Level.INFO); 361 362 boolean isDev = developmentConfiguration(); 363 364 consoleAppender = new ConsoleAppender(new PatternLayout(CONSOLE_PATTERN), ConsoleAppender.SYSTEM_ERR); 365 366 if (!isDev) { 367 consoleLogger.addAppender(consoleAppender); 369 } else { 370 consoleAppender.setLayout(new PatternLayout(CONSOLE_PATTERN_DEVELOPMENT)); 371 Logger.getRootLogger().addAppender(consoleAppender); 373 } 374 375 delegateFileAppender = new DelegatingAppender(new NullAppender()); 376 addToAllLoggers(delegateFileAppender); 377 378 BufferingAppender realBufferingAppender; 379 realBufferingAppender = new BufferingAppender(MAX_BUFFERED_LOG_MESSAGES); 380 realBufferingAppender.setName("buffering appender"); 381 delegateBufferingAppender = new DelegatingAppender(realBufferingAppender); 382 addToAllLoggers(delegateBufferingAppender); 383 buffering = true; 384 385 jmxAppender = new JMXAppender(); 387 jmxAppender.setLayout(new PatternLayout(FILE_AND_JMX_PATTERN)); 388 jmxAppender.setName("JMX appender"); 389 addToAllLoggers(jmxAppender); 390 391 if (!isDev) { 392 CustomerLogging.getGenericCustomerLogger().info("New logging session started."); 393 } 394 395 writeVersion(); 396 } catch (Exception e) { 397 reportLoggingError(e); 398 } finally { 399 Thread.currentThread().setContextClassLoader(prevLoader); 400 } 401 } 402 403 public static void addAppender(String loggerName, TCAppender appender) { 405 new TCLoggerImpl(loggerName).getLogger().addAppender(new Log4JAappenderToTCAppender(appender)); 406 } 407 408 private static void addToAllLoggers(Appender appender) { 409 for (int i = 0; i < allLoggers.length; ++i) 410 allLoggers[i].addAppender(appender); 411 } 412 413 private static void writeVersion() { 414 ProductInfo info = ProductInfo.getThisProductInfo(); 415 CustomerLogging.getConsoleLogger().info(info.toLongString()); 416 getLogger(TCLogging.class).info(info.toLongString()); 417 } 418 419 private static void writeSystemProperties() { 420 Properties properties = System.getProperties(); 421 int maxKeyLength = 1; 422 423 Iterator iter = properties.keySet().iterator(); 424 while (iter.hasNext()) { 425 String key = (String ) iter.next(); 426 maxKeyLength = Math.max(maxKeyLength, key == null ? 0 : key.length()); 427 } 428 429 StringBuffer data = new StringBuffer (); 430 data.append("========================================================================\n"); 431 data.append("All Java System Properties for this Terracotta instance:\n"); 432 433 String [] keys = (String []) properties.keySet().toArray(new String [properties.size()]); 434 Arrays.sort(keys); 435 for (int i = 0; i < keys.length; ++i) { 436 String key = keys[i]; 437 String value = (String ) properties.get(key); 438 439 while (key.length() < maxKeyLength) 440 key += " "; 441 442 data.append(key + ": " + value + "\n"); 443 } 444 445 data.append("========================================================================\n"); 446 447 getLogger(TCLogging.class).info(data.toString()); 448 } 449 450 public static void closeFileAppender() { 452 if (delegateFileAppender != null) delegateFileAppender.close(); 453 } 454 455 } 456 | Popular Tags |