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   * Policy is purging appenders that were not in use specified time in minutes
39   */
40  @Plugin(name = "IdlePurgePolicy", category = "Core", printObject = true)
41  @Scheduled
42  public class IdlePurgePolicy extends AbstractLifeCycle implements PurgePolicy, Runnable {
43  
44      private static final Logger LOGGER = StatusLogger.getLogger();
45      private final long timeToLive;
46      private final ConcurrentMap<String, Long> appendersUsage = new ConcurrentHashMap<>();
47      private RoutingAppender routingAppender;
48      private final ConfigurationScheduler scheduler;
49      private volatile ScheduledFuture<?> future = null;
50  
51      public IdlePurgePolicy(final long timeToLive, final ConfigurationScheduler scheduler) {
52          this.timeToLive = timeToLive;
53          this.scheduler = scheduler;
54      }
55  
56      @Override
57      public void initialize(final RoutingAppender routingAppender) {
58          this.routingAppender = routingAppender;
59      }
60  
61      @Override
62      public void stop() {
63          super.stop();
64          future.cancel(true);
65      }
66  
67      /**
68       * Purging appenders that were not in use specified time
69       */
70      @Override
71      public void purge() {
72          final long createTime = System.currentTimeMillis() - timeToLive;
73          for (final Entry<String, Long> entry : appendersUsage.entrySet()) {
74              if (entry.getValue() < createTime) {
75                  LOGGER.debug("Removing appender " + entry.getKey());
76                  appendersUsage.remove(entry.getKey());
77                  routingAppender.deleteAppender(entry.getKey());
78              }
79          }
80      }
81  
82      @Override
83      public void update(final String key, final LogEvent event) {
84          final long now = System.currentTimeMillis();
85          appendersUsage.put(key, now);
86          if (future == null) {
87              synchronized (this) {
88                  if (future == null) {
89                      scheduleNext();
90                  }
91              }
92          }
93  
94      }
95  
96      @Override
97      public void run() {
98          purge();
99          scheduleNext();
100     }
101 
102     private void scheduleNext() {
103         long createTime = Long.MAX_VALUE;
104         for (final Entry<String, Long> entry : appendersUsage.entrySet()) {
105             if (entry.getValue() < createTime) {
106                 createTime = entry.getValue();
107             }
108         }
109         if (createTime < Long.MAX_VALUE) {
110             final long interval = timeToLive - (System.currentTimeMillis() - createTime);
111             future = scheduler.schedule(this, interval, TimeUnit.MILLISECONDS);
112         }
113     }
114 
115     /**
116      * Create the PurgePolicy
117      *
118      * @param timeToLive the number of increments of timeUnit before the Appender should be purged.
119      * @param timeUnit   the unit of time the timeToLive is expressed in.
120      * @return The Routes container.
121      */
122     @PluginFactory
123     public static PurgePolicy createPurgePolicy(
124         @PluginAttribute("timeToLive") final String timeToLive,
125         @PluginAttribute("timeUnit") final String timeUnit,
126         @PluginConfiguration final Configuration configuration) {
127 
128         if (timeToLive == null) {
129             LOGGER.error("A timeToLive  value is required");
130             return null;
131         }
132         TimeUnit units;
133         if (timeUnit == null) {
134             units = TimeUnit.MINUTES;
135         } else {
136             try {
137                 units = TimeUnit.valueOf(timeUnit.toUpperCase());
138             } catch (final Exception ex) {
139                 LOGGER.error("Invalid time unit {}", timeUnit);
140                 units = TimeUnit.MINUTES;
141             }
142         }
143 
144         final long ttl = units.toMillis(Long.parseLong(timeToLive));
145 
146 
147         return new IdlePurgePolicy(ttl, configuration.getScheduler());
148     }
149 
150     @Override
151     public String toString() {
152         return "timeToLive=" + timeToLive;
153     }
154 
155 }