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.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
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
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
121
122
123
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 }