001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2002, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.core.sift;
015
016import ch.qos.logback.core.Appender;
017import ch.qos.logback.core.AppenderBase;
018import ch.qos.logback.core.model.SiftModel;
019import ch.qos.logback.core.util.Duration;
020
021/**
022 * This appender serves as the base class for actual SiftingAppenders
023 * implemented by the logback-classic and logback-access modules. In a nutshell,
024 * a SiftingAppender contains other appenders which it can build dynamically
025 * depending on discriminating values supplied by the event currently being
026 * processed. The appender to build (dynamically) is specified as part of a
027 * configuration file.
028 *
029 * @author Ceki Gülcü
030 */
031public abstract class SiftingAppenderBase<E> extends AppenderBase<E> {
032
033    protected AppenderTracker<E> appenderTracker;
034    AppenderFactory<E> appenderFactory;
035    Duration timeout = new Duration(AppenderTracker.DEFAULT_TIMEOUT);
036    int maxAppenderCount = AppenderTracker.DEFAULT_MAX_COMPONENTS;
037
038    SiftModel siftModel;
039    Discriminator<E> discriminator;
040
041    public Duration getTimeout() {
042        return timeout;
043    }
044
045    public void setTimeout(Duration timeout) {
046        this.timeout = timeout;
047    }
048
049    public SiftModel getSiftModel() {
050        return siftModel;
051    }
052
053    public void setSiftModel(SiftModel siftModel) {
054        this.siftModel = siftModel;
055    }
056    
057    public int getMaxAppenderCount() {
058        return maxAppenderCount;
059    }
060
061    public void setMaxAppenderCount(int maxAppenderCount) {
062        this.maxAppenderCount = maxAppenderCount;
063    }
064
065    /**
066     * This setter is intended to be invoked by SiftModelHandler. Users have no 
067     * reason to invoke this method directly.
068     */
069    public void setAppenderFactory(AppenderFactory<E> appenderFactory) {
070        this.appenderFactory = appenderFactory;
071    }
072
073    @Override
074    public void start() {
075        int errors = 0;
076        if (discriminator == null) {
077            addError("Missing discriminator. Aborting");
078            errors++;
079        }
080        if (!discriminator.isStarted()) {
081            addError("Discriminator has not started successfully. Aborting");
082            errors++;
083        }
084        if (appenderFactory == null) {
085            addError("AppenderFactory has not been set. Aborting");
086            errors++;
087        } else {
088            appenderTracker = new AppenderTracker<E>(context, appenderFactory);
089            appenderTracker.setMaxComponents(maxAppenderCount);
090            appenderTracker.setTimeout(timeout.getMilliseconds());
091        }
092        if (errors == 0) {
093            super.start();
094        }
095    }
096
097    @Override
098    public void stop() {
099        if(!isStarted())
100            return;
101        for (Appender<E> appender : appenderTracker.allComponents()) {
102            appender.stop();
103        }
104    }
105
106    abstract protected long getTimestamp(E event);
107
108    @Override
109    protected void append(E event) {
110        if (!isStarted()) {
111            return;
112        }
113        String discriminatingValue = discriminator.getDiscriminatingValue(event);
114        long timestamp = getTimestamp(event);
115
116        Appender<E> appender = appenderTracker.getOrCreate(discriminatingValue, timestamp);
117        // marks the appender for removal as specified by the user
118        if (eventMarksEndOfLife(event)) {
119            appenderTracker.endOfLife(discriminatingValue);
120        }
121        appenderTracker.removeStaleComponents(timestamp);
122        appender.doAppend(event);
123    }
124
125    protected abstract boolean eventMarksEndOfLife(E event);
126
127    public Discriminator<E> getDiscriminator() {
128        return discriminator;
129    }
130
131    public void setDiscriminator(Discriminator<E> discriminator) {
132        this.discriminator = discriminator;
133    }
134
135    // sometimes one needs to close a nested appender immediately
136    // for example when executing a command which has its own nested appender
137    // and the command also cleans up after itself. However, an open file appender
138    // will prevent the folder from being deleted
139    // see http://www.qos.ch/pipermail/logback-user/2010-March/001487.html
140
141    /**
142     * @since 0.9.19
143     */
144    public AppenderTracker<E> getAppenderTracker() {
145        return appenderTracker;
146    }
147
148    public String getDiscriminatorKey() {
149        if (discriminator != null) {
150            return discriminator.getKey();
151        } else {
152            return null;
153        }
154    }
155}