001package com.identityworksllc.iiq.common.logging; 002 003import java.util.ArrayList; 004import java.util.Date; 005import java.util.Enumeration; 006import java.util.List; 007 008import org.apache.log4j.Appender; 009import org.apache.log4j.AppenderSkeleton; 010import org.apache.log4j.Category; 011import org.apache.log4j.ConsoleAppender; 012import org.apache.log4j.Layout; 013import org.apache.log4j.Level; 014import org.apache.log4j.Logger; 015import org.apache.log4j.PatternLayout; 016import org.apache.log4j.Priority; 017import org.apache.log4j.spi.LoggingEvent; 018 019/** 020 * Captures the most interesting logs for IIQ 7.x by hooking into Log4j 1.x 021 */ 022public class CaptureLogs7 implements CaptureLogs { 023 024 /** 025 * The new appender that will log the messages if we're tracking them and also pass 026 * them on to the original logger if we're higher than the original level. 027 */ 028 @SuppressWarnings("javadoc") 029 public static class CaptureLogs7Appender extends AppenderSkeleton { 030 031 private PatternLayout formatter; 032 private List<Appender> internalAppenders; 033 034 public CaptureLogs7Appender(Level level, List<Appender> appenders) { 035 this.internalAppenders = appenders; 036 this.formatter = new PatternLayout("%d{ISO8601} %5p %t %c{4}:%L - %m%n"); 037 super.setThreshold(level); 038 } 039 040 @Override 041 protected void append(LoggingEvent event) { 042 if (LogCapture.messages.get() != null) { 043 StringBuilder builder = new StringBuilder(); 044 builder.append(formatter.format(event).replace(Layout.LINE_SEP, "")); 045 if (formatter.ignoresThrowable()) { 046 String[] lines = event.getThrowableStrRep(); 047 if (lines != null) { 048 for(String l : lines) { 049 builder.append(Layout.LINE_SEP + l.replace(Layout.LINE_SEP, "")); 050 } 051 } 052 } 053 LogCapture.messages.get().add(builder.toString()); 054 } else if (LogCapture.listener.get() != null) { 055 LogListener listener = LogCapture.listener.get(); 056 if (listener != null) { 057 LogListener.LogMessage message = new LogListener.LogMessage(new Date(event.getTimeStamp()), event.getLevel().toString(), event.getLoggerName(), String.valueOf(event.getMessage()), event.getThrowableStrRep()); 058 listener.logMessageReceived(message); 059 } 060 } 061 if (event.getLevel().isGreaterOrEqual(threshold)) { 062 if (internalAppenders != null) { 063 for(Appender a : internalAppenders) { 064 a.doAppend(event); 065 } 066 } 067 } 068 } 069 070 @Override 071 public void close() { 072 for(Appender a : internalAppenders) { 073 if (!(a instanceof ConsoleAppender)) { 074 a.close(); 075 } 076 } 077 } 078 079 @Override 080 public boolean isAsSevereAsThreshold(Priority priority) { 081 // No filtering by level! 082 return true; 083 } 084 085 @Override 086 public boolean requiresLayout() { 087 return true; 088 } 089 } 090 091 @Override 092 public void capture(String... names) { 093 for(String name : names) { 094 captureLogger(name); 095 } 096 } 097 098 /** 099 * Captures the named logger, constructing it if it doesn't already exist 100 * @param loggerName The logger name to capture 101 */ 102 private void captureLogger(String loggerName) { 103 // We want to construct the logger by name if it doesn't already exist 104 // Otherwise, we might end up with weird defaults later due to logger inheritance 105 Logger logger = Logger.getLogger(loggerName); 106 List<Appender> existingAppenders = new ArrayList<>(); 107 boolean alreadyCaptured = getEffectiveAppenders(logger, existingAppenders); 108 if (!alreadyCaptured) { 109 CaptureLogs7Appender appender = new CaptureLogs7Appender(logger.getEffectiveLevel(), existingAppenders); 110 // This is all required to prevent the logger from emitting messages on its own 111 logger.addAppender(appender); 112 // We have to remove the appenders one at a time. Logger.removeAllAppenders also closes the appenders, which is a problem. 113 for(Appender a : existingAppenders) { 114 logger.removeAppender(a); 115 } 116 logger.setLevel(Level.DEBUG); 117 logger.setAdditivity(false); 118 } 119 } 120 121 @Override 122 public void captureMost() { 123 captureLogger("org.hibernate.SQL"); 124 captureLogger("hibernate.hql.ast.AST"); 125 captureLogger("sailpoint.api.Aggregator"); 126 captureLogger("sailpoint.api.Identitizer"); 127 captureLogger("sailpoint.api.Workflower"); 128 captureLogger("sailpoint.provisioning.AssignmentExpander"); 129 captureLogger("sailpoint.provisioning.ApplicationPolicyExpander"); 130 captureLogger("sailpoint.provisioning.IIQEvaluator"); 131 captureLogger("sailpoint.provisioning.Provisioner"); 132 captureLogger("sailpoint.provisioning.PlanCompiler"); 133 captureLogger("sailpoint.persistence.hql"); 134 captureLogger("sailpoint.connector.AbstractConnector"); 135 captureLogger("sailpoint.connector.ADLDAPConnector"); 136 captureLogger("sailpoint.connector.LDAPConnector"); 137 captureLogger("sailpoint.connector.AbstractIQServiceConnector"); 138 captureLogger("sailpoint.connector.DelimitedFileConnector"); 139 captureLogger("sailpoint.connector.AbstractFileBasedConnector"); 140 captureLogger("sailpoint.connector.JDBCConnector"); 141 captureLogger("sailpoint.connector.webservices.WebServicesConnector"); 142 captureLogger("sailpoint.connector.webservices.AbstractHTTPRequestBuilder"); 143 captureLogger("sailpoint.connector.webservices.JSONRequestBuilder"); 144 captureLogger("sailpoint.connector.webservices.JSONResponseParser"); 145 captureLogger("sailpoint.connector.webservices.WebServicesClient"); 146 captureLogger("sailpoint.connector.webservices.paging.WebServicesPaginator"); 147 captureLogger("sailpoint.connector.webservices.paging.WebServicesPaginator"); 148 captureLogger("openconnector.connector.okta.OktaConnector"); 149 captureLogger("openconnector.connector.WorkDay"); 150 captureLogger("openconnector.AbstractConnector"); 151 // Prevent spam 152 Logger.getLogger("sailpoint.web").setLevel(Level.ERROR); 153 Logger.getLogger("sailpoint.server.Servicer").setLevel(Level.ERROR); 154 } 155 156 /** 157 * Gets the appenders for this logger by walking up the hierarchy of loggers 158 * @param logger The logger to check 159 * @param existingAppenders The list to which appenders should be added 160 * @return True if this logger or any of its parents has already been captured 161 */ 162 private boolean getEffectiveAppenders(Category logger, List<Appender> existingAppenders) { 163 @SuppressWarnings("unchecked") 164 Enumeration<Appender> appenders = logger.getAllAppenders(); 165 boolean alreadyCaptured = false; 166 while(appenders.hasMoreElements()) { 167 Appender appender = appenders.nextElement(); 168 if (appender.getClass().equals(CaptureLogs7Appender.class)) { 169 alreadyCaptured = true; 170 } 171 existingAppenders.add(appender); 172 } 173 174 if (logger.getParent() != null) { 175 boolean parentCaptured = getEffectiveAppenders(logger.getParent(), existingAppenders); 176 if (!alreadyCaptured && parentCaptured) { 177 alreadyCaptured = true; 178 } 179 } 180 181 return alreadyCaptured; 182 } 183 184}