View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.sift;
15  
16  import ch.qos.logback.core.Appender;
17  import ch.qos.logback.core.AppenderBase;
18  import ch.qos.logback.core.util.Duration;
19  
20  /**
21   * This appender serves as the base class for actual SiftingAppenders
22   * implemented by the logback-classic and logback-access modules. In a nutshell,
23   * a SiftingAppender contains other appenders which it can build dynamically
24   * depending on discriminating values supplied by the event currently being
25   * processed. The appender to build (dynamically) is specified as part of a
26   * configuration file.
27   *
28   * @author Ceki Gulcu
29   */
30  public abstract class SiftingAppenderBase<E> extends AppenderBase<E> {
31  
32      protected AppenderTracker<E> appenderTracker;
33      AppenderFactory<E> appenderFactory;
34      Duration timeout = new Duration(AppenderTracker.DEFAULT_TIMEOUT);
35      int maxAppenderCount = AppenderTracker.DEFAULT_MAX_COMPONENTS;
36  
37      Discriminator<E> discriminator;
38  
39      public Duration getTimeout() {
40          return timeout;
41      }
42  
43      public void setTimeout(Duration timeout) {
44          this.timeout = timeout;
45      }
46  
47      public int getMaxAppenderCount() {
48          return maxAppenderCount;
49      }
50  
51      public void setMaxAppenderCount(int maxAppenderCount) {
52          this.maxAppenderCount = maxAppenderCount;
53      }
54  
55      /**
56       * This setter is intended to be invoked by SiftAction. Customers have no reason to invoke
57       * this method directly.
58       */
59      public void setAppenderFactory(AppenderFactory<E> appenderFactory) {
60          this.appenderFactory = appenderFactory;
61      }
62  
63      @Override
64      public void start() {
65          int errors = 0;
66          if (discriminator == null) {
67              addError("Missing discriminator. Aborting");
68              errors++;
69          }
70          if (!discriminator.isStarted()) {
71              addError("Discriminator has not started successfully. Aborting");
72              errors++;
73          }
74          if (appenderFactory == null) {
75              addError("AppenderFactory has not been set. Aborting");
76              errors++;
77          } else {
78              appenderTracker = new AppenderTracker<E>(context, appenderFactory);
79              appenderTracker.setMaxComponents(maxAppenderCount);
80              appenderTracker.setTimeout(timeout.getMilliseconds());
81          }
82          if (errors == 0) {
83              super.start();
84          }
85      }
86  
87      @Override
88      public void stop() {
89          for (Appender<E> appender : appenderTracker.allComponents()) {
90              appender.stop();
91          }
92      }
93  
94      abstract protected long getTimestamp(E event);
95  
96      @Override
97      protected void append(E event) {
98          if (!isStarted()) {
99              return;
100         }
101         String discriminatingValue = discriminator.getDiscriminatingValue(event);
102         long timestamp = getTimestamp(event);
103 
104         Appender<E> appender = appenderTracker.getOrCreate(discriminatingValue, timestamp);
105         // marks the appender for removal as specified by the user
106         if (eventMarksEndOfLife(event)) {
107             appenderTracker.endOfLife(discriminatingValue);
108         }
109         appenderTracker.removeStaleComponents(timestamp);
110         appender.doAppend(event);
111     }
112 
113     protected abstract boolean eventMarksEndOfLife(E event);
114 
115     public Discriminator<E> getDiscriminator() {
116         return discriminator;
117     }
118 
119     public void setDiscriminator(Discriminator<E> discriminator) {
120         this.discriminator = discriminator;
121     }
122 
123     // sometimes one needs to close a nested appender immediately
124     // for example when executing a command which has its own nested appender
125     // and the command also cleans up after itself. However, an open file appender
126     // will prevent the folder from being deleted
127     // see http://www.qos.ch/pipermail/logback-user/2010-March/001487.html
128 
129     /**
130      * @since 0.9.19
131      */
132     public AppenderTracker<E> getAppenderTracker() {
133         return appenderTracker;
134     }
135 
136     public String getDiscriminatorKey() {
137         if (discriminator != null) {
138             return discriminator.getKey();
139         } else {
140             return null;
141         }
142     }
143 }