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}