View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, 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.CoreConstants;
19  import ch.qos.logback.core.helpers.NOPAppender;
20  import ch.qos.logback.core.joran.spi.JoranException;
21  
22  /**
23   * This appender serves as the base class for actual SiftingAppenders
24   * implemented by the logback-classic and logback-access modules. In a nutshell,
25   * a SiftingAppender contains other appenders which it can build dynamically
26   * depending on discriminating values supplied by event currently being
27   * processed. The built appender is specified as part of a configuration file.
28   * 
29   * @author Ceki Gulcu
30   */
31  public abstract class SiftingAppenderBase<E> extends
32      AppenderBase<E> {
33  
34    protected AppenderTracker<E> appenderTracker = new AppenderTrackerImpl<E>();
35    AppenderFactoryBase<E> appenderFactory;
36  
37    Discriminator<E> discriminator;
38  
39    public void setAppenderFactory(AppenderFactoryBase<E> appenderFactory) {
40      this.appenderFactory = appenderFactory;
41    }
42  
43    @Override
44    public void start() {
45      int errors = 0;
46      if (discriminator == null) {
47        addError("Missing discriminator. Aborting");
48        errors++;
49      }
50      if (!discriminator.isStarted()) {
51        addError("Discriminator has not started successfully. Aborting");
52        errors++;
53      }
54      if (errors == 0) {
55        super.start();
56      }
57    }
58  
59    @Override
60    public void stop() {
61      for (Appender<E> appender : appenderTracker.valueList()) {
62        appender.stop();
63      }
64    }
65  
66    abstract protected long getTimestamp(E event);
67  
68    @Override
69    protected void append(E event) {
70      if (!isStarted()) {
71        return;
72      }
73  
74      String discriminatingValue = discriminator.getDiscriminatingValue(event);
75      long timestamp = getTimestamp(event);
76      
77      Appender<E> appender = appenderTracker.get(discriminatingValue, timestamp);
78      if (appender == null) {
79        try {
80          appender = appenderFactory.buildAppender(context, discriminatingValue);
81          if (appender == null) {
82            appender = buildNOPAppender(discriminatingValue);
83          }
84          appenderTracker.put(discriminatingValue, appender, timestamp);
85  
86        } catch (JoranException e) {
87          addError("Failed to build appender for [" + discriminatingValue + "]",
88              e);
89          return;
90        }
91      }
92      appenderTracker.stopStaleAppenders(timestamp);
93      
94      appender.doAppend(event);
95    }
96  
97    public Discriminator<E> getDiscriminator() {
98      return discriminator;
99    }
100 
101   public void setDiscriminator(Discriminator<E> discriminator) {
102     this.discriminator = discriminator;
103   }
104   
105   
106   int nopaWarningCount = 0;
107   
108   NOPAppender<E> buildNOPAppender(String discriminatingValue) {
109     if(nopaWarningCount < CoreConstants.MAX_ERROR_COUNT) {
110       nopaWarningCount++;
111       addError("Failed to build an appender for discriminating value ["+discriminatingValue+"]");
112     }
113     NOPAppender<E> nopa = new NOPAppender<E>();
114     nopa.setContext(context);
115     nopa.start();
116     return nopa;
117   }
118 
119   // sometime one needs to close a nested appender immediately
120   // for example when executing a command which has its own nested appender
121   // and the command also cleans up after itself. However, an open file appender
122   // will prevent the folder from being deleted
123   // see http://www.qos.ch/pipermail/logback-user/2010-March/001487.html
124   /**
125    * @since 0.9.19
126    */
127   public AppenderTracker getAppenderTracker() {
128     return appenderTracker;
129   }
130 
131   public String getDiscriminatorKey() {
132     if (discriminator != null) {
133       return discriminator.getKey();
134     } else {
135       return null;
136     }
137   }
138 }