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}