org.openmainframe.ade.scores.LastSeenLoggingScorerContinuous.java Source code

Java tutorial

Introduction

Here is the source code for org.openmainframe.ade.scores.LastSeenLoggingScorerContinuous.java

Source

/*
     
Copyright IBM Corp. 2010, 2016
This file is part of Anomaly Detection Engine for Linux Logs (ADE).
    
ADE is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
ADE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with ADE.  If not, see <http://www.gnu.org/licenses/>.
     
*/
package org.openmainframe.ade.scores;

import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;

import org.apache.commons.lang3.ArrayUtils;
import org.openmainframe.ade.Ade;
import org.openmainframe.ade.AdeInternal;
import org.openmainframe.ade.data.IAnalyzedInterval;
import org.openmainframe.ade.data.IAnalyzedMessageSummary;
import org.openmainframe.ade.exceptions.AdeException;
import org.openmainframe.ade.exceptions.AdeInternalException;
import org.openmainframe.ade.impl.PropertyAnnotation.Property;
import org.openmainframe.ade.impl.data.TimeSeparator;
import org.openmainframe.ade.impl.dbUtils.DbDictionary;
import org.openmainframe.ade.impl.utils.DateTimeUtils;
import org.openmainframe.ade.scoringApi.StatisticsChart;
import org.openmainframe.ade.summary.SummarizationProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
 * This class retrieves a message's timeline and keeps track of when the message was last seen. 
 */
public class LastSeenLoggingScorerContinuous extends FixedMessageScorer {

    /**
     * Default logger for this class.
     */
    private static final Logger logger = LoggerFactory.getLogger(LastSeenLoggingScorerContinuous.class);
    /**
     * The universal version identifier for serialization. 
     */
    private static final long serialVersionUID = 1L;

    /**
     * Factor to split the timeline resolution.
     */
    private static final long SPLIT_TIMELINE_FACTOR = 2;

    /**
     * Data Factory for GregorianCalendar creation.
     */
    protected transient DatatypeFactory m_dataTypeFactory = null;

    /**
     * GregorianCalendar to store time.
     */
    protected transient GregorianCalendar m_gc = null;

    /**
     * Maps message ID to the end time of the last interval it appeared in and the last tick in that interval. 
     */
    private transient Map<String, TreeSet<Long>> m_prevIntervalTimelineMap = new TreeMap<String, TreeSet<Long>>();

    @Property(key = "verbose", help = "print diffs to stdout", required = false)
    private boolean m_verbose = false;

    @Property(key = "flushMemoryOnGap", help = "triggers clearing of the previous timeline map"
            + "Should never calculate delta between two messages on" + "diffrent sides of a gap", required = false)
    protected boolean m_flushMemoryOnGap = false;

    /**
     * Keep track of all the messages that have been seen already. 
     */
    private transient Set<String> m_alreadySeen;

    /**
     * Time the first message was sent. 
     */
    private long m_firstMsgTime;

    /**
     * Enum value that represents the timeline status of message. 
     */
    private MainStatVal m_mainStat;

    /**
     * List that gives the number of seconds between message occurrences. 
     */
    private List<Long> m_deltasInSeconds;

    /**
     * Class constructor that initializes all variables. 
     * @throws AdeException
     */
    public LastSeenLoggingScorerContinuous() throws AdeException {
        super();
        createUsageVariables();
    }

    /**
     * Creates variables used by this class for tracking last seen messages. 
     * m_prevIntervalTimelineMap contains the previous timeline (milliseconds from epoch time)
     * for each message ID. m_alreadySeen contains the message IDs of those messages that were seen
     * previously. 
     * @throws AdeException
     */
    private final void createUsageVariables() throws AdeException {
        String dataObjectName = getAnalysisGroup() + "." + getName() + ".m_prevIntervalTimelineMap";
        Object tmp = Ade.getAde().getDataStore().models().getModelDataObject(dataObjectName);
        instantiateTimelineAndAlreadySeen(dataObjectName, tmp);

        dataObjectName = getAnalysisGroup() + "." + getName() + ".m_alreadySeen";
        tmp = Ade.getAde().getDataStore().models().getModelDataObject(dataObjectName);
        instantiateTimelineAndAlreadySeen(dataObjectName, tmp);

        if (m_dataTypeFactory == null) {
            try {
                m_dataTypeFactory = DatatypeFactory.newInstance();
            } catch (DatatypeConfigurationException e) {
                throw new AdeInternalException("Failed to instantiate data factory for calendar", e);
            }
        }

        if (m_gc == null) {
            final TimeZone outputTimeZone = Ade.getAde().getConfigProperties().getOutputTimeZone();
            m_gc = new GregorianCalendar(outputTimeZone);
        }
    }

    /**
     * For instantiating the previous timeline map and already seen variables. 
     * @param dataObjectName The name of the data object we are trying to retrieve from datastore.
     * @param tmp The object returned from retrieving the data object name from datastore.
     * @throws AdeException
     */
    private void instantiateTimelineAndAlreadySeen(String dataObjectName, Object tmp) throws AdeException {
        if (dataObjectName.contains("m_prevIntervalTimelineMap")) {

            if (tmp instanceof Map<?, ?>) {
                m_prevIntervalTimelineMap = (Map<String, TreeSet<Long>>) tmp;
            } else {
                m_prevIntervalTimelineMap = new TreeMap<String, TreeSet<Long>>();
                Ade.getAde().getDataStore().models().setModelDataObject(dataObjectName, m_prevIntervalTimelineMap);
            }

        } else if (dataObjectName.contains("m_alreadySeen")) {

            if (tmp instanceof Set<?>) {
                m_alreadySeen = (HashSet<String>) tmp;
            } else {
                final DbDictionary dict = AdeInternal.getAdeImpl().getDictionaries().getMessageIdDictionary();
                m_alreadySeen = new HashSet<String>(dict.getWords());
                Ade.getAde().getDataStore().models().setModelDataObject(dataObjectName, m_alreadySeen);
            }
        }
    }

    /**
     * Create variables for this class after deserialization. 
     * @throws AdeException
     */
    @Override
    public final void wakeUp() throws AdeException {
        super.wakeUp();
        createUsageVariables();
    }

    /**
     * Flushes previous timeline map on incoming time separator if m_flushMemoryOnGap is set to true.
     * @param sep The incoming separator object.
     * @throws AdeException
     */
    @Override
    public final void incomingSeparator(TimeSeparator sep) throws AdeException {
        super.incomingSeparator(sep);
        if (m_flushMemoryOnGap) {
            flushMemory();
        }
    }

    /**
     * Removes all the mappings from the previous interval timeline map.
     */
    private void flushMemory() {
        m_prevIntervalTimelineMap.clear();
    }

    /**
     * Main logic for creating the previous timeline of a particular message. First, it processes
     * the first message. Then, it gets the previous timeline from the map; if the previous time line is not null, 
     * we take the LATEST time this message was sent from the previous time line (right before the first message
     * time in this interval). Then, we take this latest time, the first message time in this interval, 
     * and the times this message occurred after the first time in the same interval is used to create
     * the "new" previous time line. 
     * @param scoredElement The analysis results of a MessageSummary object. Message summaries contain 
     * statistics and information on message instances. i.e. text body message, message id, severity, etc.
     * @param contextElement contains a summary of the interval i.e. information such as time, number of 
     * message ids, etc.
     * @return The StatisticsChart for collecting double and string statistics.
     */
    @Override
    public StatisticsChart getScore(IAnalyzedMessageSummary scoredElement, IAnalyzedInterval contextElement)
            throws AdeException {
        final String messageID = scoredElement.getMessageId();
        final short[] timeLine = scoredElement.getTimeLine();
        final StatisticsChart sc = new StatisticsChart();

        sc.setStat(LOG_PROB, 0);
        sc.setStat(ANOMALY, 0);

        processFirstMessage(contextElement, timeLine);

        final TreeSet<Long> timeLineSet = new TreeSet<Long>();

        processPrevTimeLine(messageID, contextElement, sc, timeLineSet);

        if (!m_alreadySeen.contains(messageID)) {
            m_mainStat = MainStatVal.NEVER_SEEN_BEFORE;
            m_alreadySeen.add(messageID);
        }

        timeLineSet.add(m_firstMsgTime);

        processCurrentTimeLine(timeLine, timeLineSet, contextElement);
        printLastSeenInfo(messageID);

        m_prevIntervalTimelineMap.put(messageID, timeLineSet);
        sc.setStat("res", m_deltasInSeconds.toString());
        sc.setStat(MAIN, m_mainStat.toString());
        return sc;
    }

    /**
     * Prints out last seen information if verbosity is turned on (ie. set to true)
     * @param messageID String value that gives message id. 
     */
    public void printLastSeenInfo(String messageID) {
        if (m_verbose) {
            logger.info(messageID + "\t" + m_deltasInSeconds.toString());
        }
    }

    /**
     * Process to handle the first message.
     * @param contextElement AnalyzedInterval object that contains summary results of interval.
     * @param timeLine Array of Short values with the time line of the message.
     * @param millisPerTick Long value with number of milliseconds per tick. 
     */
    public void processFirstMessage(IAnalyzedInterval contextElement, short[] timeLine) {
        final boolean hasTimeline = !ArrayUtils.isEmpty(timeLine);
        final long millisPerTick = contextElement.getInterval().getIntervalSize()
                / SummarizationProperties.TIMELINE_RESOLUTION;
        if (hasTimeline) {
            m_firstMsgTime = contextElement.getIntervalStartTime() + timeLine[0] * millisPerTick;
            m_mainStat = MainStatVal.REGULAR;
            m_deltasInSeconds = new ArrayList<Long>(timeLine.length);
        } else {
            /**
             * If time line is not available, assume first message occurred in the middle of the
             * interval.
             */
            m_firstMsgTime = contextElement.getIntervalStartTime()
                    + (SummarizationProperties.TIMELINE_RESOLUTION / SPLIT_TIMELINE_FACTOR) * millisPerTick;
            m_mainStat = MainStatVal.NO_TIMELINE;
            m_deltasInSeconds = new ArrayList<Long>(1);
        }
    }

    /**
     * Processes the previous time line of the current message ID. 
     * @param messageID String value that contains the message ID
     * @param contextElement AnalyzedInterval object that contains summary results of interval.
     * @param sc Contains statistics for message ID.
     * @param timeLineSet time line of current message ID. 
     * @throws AdeInternalException
     */
    public void processPrevTimeLine(String messageID, IAnalyzedInterval contextElement, StatisticsChart sc,
            Set<Long> timeLineSet) throws AdeInternalException {
        final TreeSet<Long> prevTimeLine = m_prevIntervalTimelineMap.get(messageID);
        if (prevTimeLine == null) {
            m_mainStat = MainStatVal.NEW;
            m_deltasInSeconds
                    .add((m_firstMsgTime - contextElement.getIntervalStartTime()) / DateTimeUtils.MILLIS_IN_SECOND);
        } else {
            final Long prevLastTime = prevTimeLine.lower(m_firstMsgTime);
            if (prevLastTime != null) {
                m_gc.setTimeInMillis(prevLastTime);
                sc.setStat("LastTime", String.valueOf(m_dataTypeFactory.newXMLGregorianCalendar(m_gc)));
                final long delta = (m_firstMsgTime - prevLastTime) / DateTimeUtils.MILLIS_IN_SECOND;
                m_deltasInSeconds.add(delta);
                timeLineSet.add(prevLastTime);
            }
        }
    }

    /**
     * Processes the current time line. 
     * @param timeLine Array of Short values with the time line of the message.
     * @param timeLineSet time line of current message ID. 
     * @param contextElement AnalyzedInterval object that contains summary results of interval.
     * @param millisPerTick Long value with number of milliseconds per tick. 
     */

    public void processCurrentTimeLine(short[] timeLine, Set<Long> timeLineSet, IAnalyzedInterval contextElement) {
        final boolean hasTimeline = !ArrayUtils.isEmpty(timeLine);
        final long millisPerTick = contextElement.getInterval().getIntervalSize()
                / SummarizationProperties.TIMELINE_RESOLUTION;
        if (hasTimeline) {
            Short prevPos = null;
            for (short pos : timeLine) {
                // ignore first message, as we dealt with it already
                if (prevPos == null) {
                    prevPos = pos;
                    continue;
                }
                Long delta = (pos - prevPos) * millisPerTick;
                m_deltasInSeconds.add(delta / DateTimeUtils.MILLIS_IN_SECOND);
                prevPos = pos;

                timeLineSet.add(contextElement.getIntervalStartTime() + pos * millisPerTick);
            }
        }
    }

    /**
     * Enum class to keep track of timeline status of messages. 
     */
    private enum MainStatVal {
        NEW("new"), REGULAR("regular"), NO_TIMELINE("noTimeline"), NEVER_SEEN_BEFORE("neverSeenBefore");

        private final String m_str;

        private MainStatVal(String str) {
            m_str = str;
        }

        @Override
        public String toString() {
            return m_str;
        }
    }

}