001package com.identityworksllc.iiq.common;
002
003import lombok.Builder;
004import lombok.Getter;
005import lombok.Setter;
006import sailpoint.api.SailPointContext;
007import sailpoint.api.SailPointFactory;
008import sailpoint.api.logging.SyslogThreadLocal;
009import sailpoint.object.SyslogEvent;
010import sailpoint.persistence.Sequencer;
011import sailpoint.tools.GeneralException;
012import sailpoint.tools.Util;
013
014import javax.validation.constraints.NotNull;
015import java.io.PrintWriter;
016import java.io.StringWriter;
017import java.util.concurrent.atomic.AtomicReference;
018
019/**
020 * A utility for generating and committing Syslog events, even where IIQ would not
021 * produce them. The events are saved via an autonomous transaction.
022 */
023public class Syslogger {
024    /**
025     * Argument VO for syslog events
026     */
027    @Setter
028    @Getter
029    @Builder
030    public static final class SyslogArgs {
031        /**
032         * The error, optionally
033         */
034        private Throwable error;
035
036        /**
037         * The event level (ERROR, INFO, WARN)
038         */
039        private String eventLevel;
040
041        /**
042         * The logged in user, optionally
043         */
044        private String loggedInUser;
045
046        /**
047         * The message to log (not null)
048         */
049        @NotNull
050        private String message;
051
052        /**
053         * The owning class, optionally
054         */
055        private Class<?> owningClass;
056    }
057
058    /**
059     * Event level error
060     */
061    public static final String EVENT_LEVEL_ERROR = "ERROR";
062
063    /**
064     * Event level info
065     */
066    public static final String EVENT_LEVEL_INFO = "INFO";
067
068    /**
069     * Event level warn
070     */
071    public static final String EVENT_LEVEL_WARN = "WARN";
072
073    /**
074     * Private utility constructor
075     */
076    private Syslogger() {
077
078    }
079
080    /**
081     * @see #logEvent(Class, String, Throwable)
082     */
083    public static String logEvent(Class<?> owningClass, String message) throws GeneralException {
084        return logEvent(owningClass, message, null);
085    }
086
087    /**
088     * @see #logEvent(Class, String, Throwable)
089     */
090    public static String logEvent(String message) throws GeneralException {
091        return logEvent(null, message, null);
092    }
093
094    /**
095     * @see #logEvent(Class, String, Throwable)
096     */
097    public static String logEvent(String message, Throwable error) throws GeneralException {
098        return logEvent(null, message, error);
099    }
100
101    public static String logEvent(final Class<?> owningClass, final String message, final Throwable error) throws GeneralException {
102        return logEvent(owningClass, message, error, EVENT_LEVEL_ERROR);
103    }
104
105    /**
106     * Logs a SyslogEvent of ERROR type with a sequential QuickKey, returning that key
107     *
108     * @param owningClass The class invoking this method, which will be logged as the class name, optionally
109     * @param message The message to log (not null)
110     * @param error The error, optionally
111     * @return The quick key generated for this event
112     * @throws GeneralException if any failures occur during logging or creating the private context
113     */
114    public static String logEvent(final Class<?> owningClass, final String message, final Throwable error, final String eventLevel) throws GeneralException {
115        final String authenticatedUser;
116        final SailPointContext currentContext = SailPointFactory.getCurrentContext();
117        if (currentContext != null && currentContext.getUserName() != null) {
118            authenticatedUser = currentContext.getUserName();
119        } else {
120            authenticatedUser = "???";
121        }
122        final AtomicReference<String> quickKeyRef = new AtomicReference<>();
123        Utilities.withPrivateContext((context) -> {
124            Sequencer sequencer = new Sequencer();
125            String quickKey = sequencer.generateId(context, new SyslogEvent());
126            quickKeyRef.set(quickKey);
127            SyslogThreadLocal.set(Util.stripLeadingChar(quickKey, '0'));
128            SyslogEvent event = new SyslogEvent();
129            event.setQuickKey(quickKey);
130            event.setUsername(authenticatedUser);
131            event.setServer(Util.getHostName());
132            event.setEventLevel(eventLevel);
133            event.setThread(Thread.currentThread().getName());
134            event.setMessage(message);
135            if (owningClass != null) {
136                event.setClassname(owningClass.getName());
137            }
138            if (error != null) {
139                try (StringWriter writer = new StringWriter()) {
140                    try (PrintWriter printWriter = new PrintWriter(writer)) {
141                        error.printStackTrace(printWriter);
142                    }
143                    writer.flush();
144                    event.setStacktrace(writer.toString());
145                }
146            }
147
148            context.saveObject(event);
149            context.commitTransaction();
150        });
151        return quickKeyRef.get();
152    }
153}