001package com.identityworksllc.iiq.common.logging; 002 003import java.util.ArrayList; 004import java.util.Date; 005import java.util.List; 006import java.util.Objects; 007 008import sailpoint.VersionConstants; 009import sailpoint.tools.GeneralException; 010 011/** 012 * A class to start and stop log capture as needed. Start interception at any point 013 * by calling {@link LogCapture#startInterception(String...)}, passing one or more logger 014 * names. If no logger names are passed, a predefined set of common ones will be used. 015 * 016 * All messages down to DEBUG level will be captured, even if the logger is set to a 017 * higher level. Existing appenders will be copied and adjusted so that they continue 018 * to log at the previous level. In other words, you'll get DEBUG messages intercepted 019 * but they won't end up in your actual log file. 020 * 021 * When you're done, make sure to call {@link LogCapture#stopInterception()} or you will 022 * have a definite memory leak. You will get back your log messages as a list of strings. 023 * 024 * For a streaming experience, you can register a LogListener with the other version 025 * of {@link #startInterception(LogListener, String...)}. This listener will receive 026 * all log messages as instances of LogListener.LogMessage. 027 */ 028public class LogCapture { 029 030 /** 031 * The log message listener for this thread; if a listener is defined, no messages will be stored 032 */ 033 public static ThreadLocal<LogListener> listener = new InheritableThreadLocal<>(); 034 035 /** 036 * The log messages captured for this thread 037 */ 038 public static ThreadLocal<List<String>> messages = new InheritableThreadLocal<>(); 039 040 /** 041 * Adds logger interceptors for the given loggers if they don't already have any 042 * @param loggerNames The logger names to intercept 043 * @throws GeneralException If a reflection exception occurs 044 */ 045 public static void addLoggers(String... loggerNames) throws GeneralException { 046 if (VersionConstants.VERSION.startsWith("7")) { 047 try { 048 CaptureLogs capturer = (CaptureLogs)Class.forName("com.identityworksllc.iiq.common.logging.CaptureLogs7", true, LogCapture.class.getClassLoader()).newInstance(); 049 capturer.capture(loggerNames); 050 } catch(Exception e) { 051 throw new GeneralException(e); 052 } 053 } else if (VersionConstants.VERSION.startsWith("8")) { 054 try { 055 CaptureLogs capturer = (CaptureLogs)Class.forName("com.identityworksllc.iiq.common.logging.CaptureLogs8", true, LogCapture.class.getClassLoader()).newInstance(); 056 capturer.capture(loggerNames); 057 } catch(Exception e) { 058 throw new GeneralException(e); 059 } 060 } 061 } 062 063 /** 064 * Sets the log listener for the current thread to the given listener object. The existing message queue 065 * will be retrieved, dumped to the listener, then disabled. 066 * @param _listener The listener to assign 067 */ 068 public static void setListener(LogListener _listener) { 069 listener.set(_listener); 070 if (messages.get() != null) { 071 List<String> existingMessages = messages.get(); 072 messages.set(null); 073 for(String message: existingMessages) { 074 LogListener.LogMessage messageObject = new LogListener.LogMessage(new Date(), null, null, message, null); 075 _listener.logMessageReceived(messageObject); 076 } 077 } 078 } 079 080 /** 081 * Starts capture of the given loggers 082 * @param loggers Specific loggers to capture, if desired 083 * @throws GeneralException if any failures occur 084 */ 085 private static void startCapture(String... loggers) throws GeneralException { 086 CaptureLogs capturer = null; 087 try { 088 if (VersionConstants.VERSION.startsWith("7")) { 089 capturer = (CaptureLogs) Class.forName("com.identityworksllc.iiq.common.logging.CaptureLogs7", true, LogCapture.class.getClassLoader()).newInstance(); 090 } else if (VersionConstants.VERSION.startsWith("8")) { 091 capturer = (CaptureLogs) Class.forName("com.identityworksllc.iiq.common.logging.CaptureLogs8", true, LogCapture.class.getClassLoader()).newInstance(); 092 } 093 } catch(Exception e) { 094 throw new GeneralException(e); 095 } 096 if (capturer != null) { 097 if (loggers == null || loggers.length == 0) { 098 capturer.captureMost(); 099 } else { 100 capturer.capture(loggers); 101 } 102 } 103 } 104 105 /** 106 * Starts log interception if it hasn't already been started 107 * @throws GeneralException on reflection failure setting up the logging interceptor 108 */ 109 public static void startInterception(LogListener _listener, String... loggers) throws GeneralException { 110 if (listener.get() != null) { 111 return; 112 } 113 Objects.requireNonNull(_listener); 114 startCapture(loggers); 115 messages.set(null); 116 listener.set(_listener); 117 } 118 119 /** 120 * Starts log interception if it hasn't already been started 121 * @throws GeneralException on reflection failure setting up the logging interceptor 122 */ 123 public static void startInterception(String... loggers) throws GeneralException { 124 if (messages.get() != null) { 125 return; 126 } 127 startCapture(loggers); 128 messages.set(new ArrayList<>()); 129 listener.set(null); 130 } 131 132 /** 133 * Stops interception for this thread, clears the message queue, and returns the list of messages 134 * @return The list of log messages received 135 */ 136 public static List<String> stopInterception() { 137 List<String> messageStrings = messages.get(); 138 messages.set(null); 139 listener.set(null); 140 return messageStrings; 141 } 142 143 /** 144 * Private utility constructor 145 */ 146 private LogCapture() { 147 148 } 149}