001package com.identityworksllc.iiq.common.logging; 002 003import org.apache.logging.log4j.Level; 004import org.apache.logging.log4j.LogManager; 005import org.apache.logging.log4j.core.Appender; 006import org.apache.logging.log4j.core.Filter; 007import org.apache.logging.log4j.core.Layout; 008import org.apache.logging.log4j.core.LogEvent; 009import org.apache.logging.log4j.core.LoggerContext; 010import org.apache.logging.log4j.core.appender.AbstractAppender; 011import org.apache.logging.log4j.core.config.AppenderRef; 012import org.apache.logging.log4j.core.config.Configuration; 013import org.apache.logging.log4j.core.config.LoggerConfig; 014import org.apache.logging.log4j.core.config.Property; 015import org.apache.logging.log4j.core.layout.PatternLayout; 016 017import java.io.Serializable; 018import java.nio.charset.StandardCharsets; 019import java.util.Date; 020import java.util.Map; 021 022/** 023 * Capture logs in IIQ 8, which uses Log4J 2.x. That version has entirely 024 * different Logger semantics, requiring separate code. 025 */ 026public class CaptureLogs8 implements CaptureLogs { 027 /** 028 * The layout used for captured messages 029 */ 030 private static final PatternLayout formatter = PatternLayout.newBuilder().withPattern(PatternLayout.SIMPLE_CONVERSION_PATTERN).build(); 031 032 /** 033 * The appender that will log messages to either the messages queue 034 * or the listener. 035 */ 036 private static class CapturingAppender extends AbstractAppender { 037 038 /** 039 * @see AbstractAppender#AbstractAppender(String, Filter, Layout, boolean, Property[]) 040 */ 041 protected CapturingAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, Property[] properties) { 042 super(name, filter, layout, ignoreExceptions, properties); 043 } 044 045 /** 046 * @see AbstractAppender#append(LogEvent) 047 */ 048 @Override 049 public void append(LogEvent event) { 050 if (LogCapture.messages.get() != null) { 051 StringBuilder builder = new StringBuilder(); 052 formatter.serialize(event, builder); 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.getTimeMillis()), event.getLevel().toString(), event.getLoggerName(), String.valueOf(event.getMessage()), null); 058 listener.logMessageReceived(message); 059 } 060 } 061 062 } 063 } 064 065 @Override 066 public void captureMost() { 067 captureLogger("org.hibernate.SQL"); 068 captureLogger("hibernate.hql.ast.AST"); 069 captureLogger("sailpoint.api.Aggregator"); 070 captureLogger("sailpoint.api.Identitizer"); 071 captureLogger("sailpoint.api.Workflower"); 072 captureLogger("sailpoint.provisioning.AssignmentExpander"); 073 captureLogger("sailpoint.provisioning.ApplicationPolicyExpander"); 074 captureLogger("sailpoint.provisioning.IIQEvaluator"); 075 captureLogger("sailpoint.provisioning.Provisioner"); 076 captureLogger("sailpoint.provisioning.PlanCompiler"); 077 captureLogger("sailpoint.persistence.hql"); 078 captureLogger("sailpoint.connector.AbstractConnector"); 079 captureLogger("sailpoint.connector.ADLDAPConnector"); 080 captureLogger("sailpoint.connector.LDAPConnector"); 081 captureLogger("sailpoint.connector.AbstractIQServiceConnector"); 082 captureLogger("sailpoint.connector.DelimitedFileConnector"); 083 captureLogger("sailpoint.connector.AbstractFileBasedConnector"); 084 captureLogger("sailpoint.connector.JDBCConnector"); 085 captureLogger("sailpoint.connector.webservices.WebServicesConnector"); 086 captureLogger("sailpoint.connector.webservices.AbstractHTTPRequestBuilder"); 087 captureLogger("sailpoint.connector.webservices.JSONRequestBuilder"); 088 captureLogger("sailpoint.connector.webservices.JSONResponseParser"); 089 captureLogger("sailpoint.connector.webservices.WebServicesClient"); 090 captureLogger("sailpoint.connector.webservices.paging.WebServicesPaginator"); 091 captureLogger("sailpoint.connector.webservices.paging.WebServicesPaginator"); 092 captureLogger("openconnector.connector.okta.OktaConnector"); 093 captureLogger("openconnector.connector.WorkDay"); 094 captureLogger("openconnector.AbstractConnector"); 095 096 final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); 097 final Configuration config = ctx.getConfiguration(); 098 099 config.getLoggerConfig("sailpoint.web").setLevel(Level.ERROR); 100 config.getLoggerConfig("sailpoint.server.Servicer").setLevel(Level.ERROR); 101 102 ctx.updateLoggers(config); 103 } 104 105 @Override 106 public void capture(String... names) { 107 for(String name : names) { 108 captureLogger(name); 109 } 110 } 111 112 /** 113 * Captures the logger with the given name 114 * @param loggerName The logger 115 */ 116 public static void captureLogger(String loggerName) { 117 final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); 118 final Configuration config = ctx.getConfiguration(); 119 PatternLayout layout = 120 PatternLayout.newBuilder().withPattern(PatternLayout.SIMPLE_CONVERSION_PATTERN).withConfiguration(config).withCharset(StandardCharsets.UTF_8).build(); 121 122 Appender appender = config.getAppender("idw-writer-appender"); 123 124 if (appender == null) { 125 appender = new CapturingAppender("idw-writer-appender", null, layout, false, null); 126 //WriterAppender.newBuilder().setName("idw-writer-appender").setTarget(new CapturedLogsWriter()).setLayout(layout).setConfiguration(config).setFilter().build(); 127 appender.start(); 128 config.addAppender(appender); 129 } 130 131 AppenderRef ref = AppenderRef.createAppenderRef("idw-writer-appender", Level.DEBUG, null); 132 AppenderRef[] refs = new AppenderRef[] {ref}; 133 LoggerConfig loggerConfig = config.getLoggerConfig(loggerName); 134 if (loggerConfig == null) { 135 loggerConfig = LoggerConfig.createLogger(false, Level.DEBUG, loggerName, "true", refs, null, config, null); 136 loggerConfig.start(); 137 config.addLogger(loggerName, loggerConfig); 138 } 139 140 Level originalLevel = loggerConfig.getLevel(); 141 142 if (!loggerConfig.getName().equals(loggerName)) { 143 LoggerConfig specificConfig = new LoggerConfig(loggerName, Level.DEBUG, false); 144 specificConfig.setParent(loggerConfig); 145 config.addLogger(loggerName, specificConfig); 146 loggerConfig = specificConfig; 147 } 148 149 wrapAppenders(loggerConfig, loggerConfig, originalLevel); 150 151 loggerConfig.setAdditive(false); 152 153 if (!loggerConfig.getAppenders().containsKey("idw-writer-appender")) { 154 loggerConfig.addAppender(appender, Level.DEBUG, null); 155 } 156 157 ctx.updateLoggers(config); 158 } 159 160 /** 161 * Wraps the appenders for the logger and any of its parents, preventing 162 * the default appenders from logging at a strange level. 163 * 164 * @param toModify The LoggerConfig to modify (the one we're hooking) 165 * @param current The LoggerConfig to consider (may be a parent) 166 * @param originalLevel The original level to set on the appenders 167 */ 168 private static void wrapAppenders(LoggerConfig toModify, LoggerConfig current, Level originalLevel) { 169 Map<String, Appender> appenders = current.getAppenders(); 170 for(String name : appenders.keySet()) { 171 if (!name.equals("idw-writer-appender")) { 172 Appender existing = appenders.get(name); 173 toModify.removeAppender(name); 174 toModify.addAppender(existing, originalLevel, null); 175 } 176 } 177 if (current.getParent() != null) { 178 wrapAppenders(toModify, current.getParent(), originalLevel); 179 } 180 } 181}