View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.appender.routing;
18  
19  import java.util.Map.Entry;
20  import java.util.concurrent.ConcurrentHashMap;
21  import java.util.concurrent.ConcurrentMap;
22  import java.util.concurrent.ScheduledFuture;
23  import java.util.concurrent.TimeUnit;
24  
25  import org.apache.logging.log4j.Logger;
26  import org.apache.logging.log4j.core.AbstractLifeCycle;
27  import org.apache.logging.log4j.core.LogEvent;
28  import org.apache.logging.log4j.core.config.Configuration;
29  import org.apache.logging.log4j.core.config.ConfigurationScheduler;
30  import org.apache.logging.log4j.core.config.Scheduled;
31  import org.apache.logging.log4j.core.config.plugins.Plugin;
32  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
33  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
34  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
35  import org.apache.logging.log4j.status.StatusLogger;
36  
37  /**
38   * 
39   * Policy is purging appenders that were not in use specified time in minutes
40   *
41   */
42  @Plugin(name = "IdlePurgePolicy", category = "Core", printObject = true)
43  @Scheduled
44  public class IdlePurgePolicy extends AbstractLifeCycle implements PurgePolicy, Runnable {
45  
46      private static final Logger LOGGER = StatusLogger.getLogger();
47      private static final long serialVersionUID = 7481062062560624564L;
48      private final long timeToLive;
49  	private final ConcurrentMap<String, Long> appendersUsage = new ConcurrentHashMap<>();
50  	private RoutingAppender routingAppender;
51      private final ConfigurationScheduler scheduler;
52      private volatile ScheduledFuture<?> future = null;
53      
54  	public IdlePurgePolicy(long timeToLive, ConfigurationScheduler scheduler) {
55  		this.timeToLive = timeToLive;
56          this.scheduler = scheduler;
57  	}	
58  
59      @Override
60  	public void initialize(RoutingAppender routingAppender) {
61  		this.routingAppender = routingAppender;
62  	}
63  
64      @Override
65      public void stop() {
66          super.stop();
67          future.cancel(true);
68      }
69  
70  	/**
71  	 * Purging appenders that were not in use specified time
72  	 * 
73  	 */
74  	@Override
75  	public void purge() {
76  		long createTime = System.currentTimeMillis() - timeToLive;
77      	for (Entry<String, Long> entry : appendersUsage.entrySet()) {
78  			if (entry.getValue() < createTime) {
79                  LOGGER.debug("Removing appender " + entry.getKey());
80  				appendersUsage.remove(entry.getKey());
81  		       	routingAppender.deleteAppender(entry.getKey());
82  			}
83  		}
84  	}
85  
86  	@Override
87  	public void update(String key, LogEvent event) {
88          long now = System.currentTimeMillis();
89  		appendersUsage.put(key, now);
90          if (future == null) {
91              synchronized(this) {
92                  if (future == null) {
93                      scheduleNext();
94                  }
95              }
96          }
97  
98  	}
99  
100     @Override
101     public void run() {
102         purge();
103         scheduleNext();
104     }
105 
106     private void scheduleNext() {
107         long createTime = Long.MAX_VALUE;
108         for (Entry<String, Long> entry : appendersUsage.entrySet()) {
109             if (entry.getValue() < createTime) {
110                 createTime = entry.getValue();
111             }
112         }
113         if (createTime < Long.MAX_VALUE) {
114             long interval = timeToLive - (System.currentTimeMillis() - createTime);
115             future = scheduler.schedule(this, interval, TimeUnit.MILLISECONDS);
116         }
117     }
118 
119 	/**
120      * Create the PurgePolicy
121      * @param timeToLive the number of increments of timeUnit before the Appender should be purged.
122      * @param timeUnit the unit of time the timeToLive is expressed in.
123      * @return The Routes container.
124      */
125     @PluginFactory
126     public static PurgePolicy createPurgePolicy(
127             @PluginAttribute("timeToLive") final String timeToLive,
128 			@PluginAttribute("timeUnit") final String timeUnit,
129             @PluginConfiguration Configuration configuration) {
130     	
131         if (timeToLive == null) {
132             LOGGER.error("A timeToLive  value is required");
133             return null;
134         }
135 		TimeUnit units;
136 		if (timeUnit == null) {
137 			units = TimeUnit.MINUTES;
138 		} else {
139 			try {
140 				units = TimeUnit.valueOf(timeUnit.toUpperCase());
141 			} catch(Exception ex) {
142 				LOGGER.error("Invalid time unit {}", timeUnit);
143 				units = TimeUnit.MINUTES;
144 			}
145 		}
146         
147         final long ttl = units.toMillis(Long.parseLong(timeToLive));
148 
149         
150         return new IdlePurgePolicy(ttl, configuration.getScheduler());
151     }
152 
153     @Override
154     public String toString() {
155         return "timeToLive=" + timeToLive;
156     }
157 
158 }