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}