1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.core.AbstractLifeCycle;
26 import org.apache.logging.log4j.core.Core;
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
36
37
38
39 @Plugin(name = "IdlePurgePolicy", category = Core.CATEGORY_NAME, printObject = true)
40 @Scheduled
41 public class IdlePurgePolicy extends AbstractLifeCycle implements PurgePolicy, Runnable {
42
43 private final long timeToLive;
44 private final long checkInterval;
45 private final ConcurrentMap<String, Long> appendersUsage = new ConcurrentHashMap<>();
46 private RoutingAppender routingAppender;
47 private final ConfigurationScheduler scheduler;
48 private volatile ScheduledFuture<?> future;
49
50 public IdlePurgePolicy(final long timeToLive, final long checkInterval, final ConfigurationScheduler scheduler) {
51 this.timeToLive = timeToLive;
52 this.checkInterval = checkInterval;
53 this.scheduler = scheduler;
54 }
55
56 @Override
57 public void initialize(@SuppressWarnings("hiding") final RoutingAppender routingAppender) {
58 this.routingAppender = routingAppender;
59 }
60
61 @Override
62 public boolean stop(final long timeout, final TimeUnit timeUnit) {
63 setStopping();
64 final boolean stopped = stop(future);
65 setStopped();
66 return stopped;
67 }
68
69
70
71
72 @Override
73 public void purge() {
74 final long createTime = System.currentTimeMillis() - timeToLive;
75 for (final Entry<String, Long> entry : appendersUsage.entrySet()) {
76 long entryValue = entry.getValue();
77 if (entryValue < createTime) {
78 if (appendersUsage.remove(entry.getKey(), entryValue)) {
79 LOGGER.debug("Removing appender {}", entry.getKey());
80 routingAppender.deleteAppender(entry.getKey());
81 }
82 }
83 }
84 }
85
86 @Override
87 public void update(final String key, final LogEvent event) {
88 final 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 updateTime = Long.MAX_VALUE;
108 for (final Entry<String, Long> entry : appendersUsage.entrySet()) {
109 if (entry.getValue() < updateTime) {
110 updateTime = entry.getValue();
111 }
112 }
113
114 if (updateTime < Long.MAX_VALUE) {
115 final long interval = timeToLive - (System.currentTimeMillis() - updateTime);
116 future = scheduler.schedule(this, interval, TimeUnit.MILLISECONDS);
117 } else {
118
119 future = scheduler.schedule(this, checkInterval, TimeUnit.MILLISECONDS);
120 }
121 }
122
123
124
125
126
127
128
129
130
131 @PluginFactory
132 public static PurgePolicy createPurgePolicy(
133 @PluginAttribute("timeToLive") final String timeToLive,
134 @PluginAttribute("checkInterval") final String checkInterval,
135 @PluginAttribute("timeUnit") final String timeUnit,
136 @PluginConfiguration final Configuration configuration) {
137
138 if (timeToLive == null) {
139 LOGGER.error("A timeToLive value is required");
140 return null;
141 }
142 TimeUnit units;
143 if (timeUnit == null) {
144 units = TimeUnit.MINUTES;
145 } else {
146 try {
147 units = TimeUnit.valueOf(timeUnit.toUpperCase());
148 } catch (final Exception ex) {
149 LOGGER.error("Invalid timeUnit value {}. timeUnit set to MINUTES", timeUnit, ex);
150 units = TimeUnit.MINUTES;
151 }
152 }
153
154 long ttl = units.toMillis(Long.parseLong(timeToLive));
155 if (ttl < 0) {
156 LOGGER.error("timeToLive must be positive. timeToLive set to 0");
157 ttl = 0;
158 }
159
160 long ci;
161 if (checkInterval == null) {
162 ci = ttl;
163 } else {
164 ci = units.toMillis(Long.parseLong(checkInterval));
165 if (ci < 0) {
166 LOGGER.error("checkInterval must be positive. checkInterval set equal to timeToLive = {}", ttl);
167 ci = ttl;
168 }
169 }
170
171 return new IdlePurgePolicy(ttl, ci, configuration.getScheduler());
172 }
173
174 @Override
175 public String toString() {
176 return "timeToLive=" + timeToLive;
177 }
178
179 }