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.filter;
18  
19  import java.text.ParseException;
20  import java.text.SimpleDateFormat;
21  import java.util.Calendar;
22  import java.util.TimeZone;
23  
24  import org.apache.logging.log4j.Level;
25  import org.apache.logging.log4j.Marker;
26  import org.apache.logging.log4j.core.Filter;
27  import org.apache.logging.log4j.core.LogEvent;
28  import org.apache.logging.log4j.core.Logger;
29  import org.apache.logging.log4j.core.config.Node;
30  import org.apache.logging.log4j.core.config.plugins.Plugin;
31  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
32  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
33  import org.apache.logging.log4j.core.util.Clock;
34  import org.apache.logging.log4j.core.util.ClockFactory;
35  import org.apache.logging.log4j.message.Message;
36  import org.apache.logging.log4j.util.PerformanceSensitive;
37  
38  /**
39   * Filters events that fall within a specified time period in each day.
40   */
41  @Plugin(name = "TimeFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
42  @PerformanceSensitive("allocation")
43  public final class TimeFilter extends AbstractFilter {
44      private static final Clock CLOCK = ClockFactory.getClock();
45  
46      /**
47       * Length of hour in milliseconds.
48       */
49      private static final long HOUR_MS = 3600000;
50  
51      /**
52       * Length of minute in milliseconds.
53       */
54      private static final long MINUTE_MS = 60000;
55  
56      /**
57       * Length of second in milliseconds.
58       */
59      private static final long SECOND_MS = 1000;
60  
61      /**
62       * Starting offset from midnight in milliseconds.
63       */
64      private final long start;
65      /**
66       * Ending offset from midnight in milliseconds.
67       */
68      private final long end;
69      /**
70       * Timezone.
71       */
72      private final TimeZone timezone;
73  
74      private long midnightToday;
75      private long midnightTomorrow;
76  
77  
78      private TimeFilter(final long start, final long end, final TimeZone tz, final Result onMatch,
79                         final Result onMismatch) {
80          super(onMatch, onMismatch);
81          this.start = start;
82          this.end = end;
83          timezone = tz;
84          initMidnight(start);
85      }
86  
87      /**
88       * Initializes the midnight boundaries to midnight in the specified time zone.
89       * @param now a time in milliseconds since the epoch, used to pinpoint the current date
90       */
91      void initMidnight(final long now) {
92          final Calendar calendar = Calendar.getInstance(timezone);
93          calendar.setTimeInMillis(now);
94          calendar.set(Calendar.HOUR_OF_DAY, 0);
95          calendar.set(Calendar.MINUTE, 0);
96          calendar.set(Calendar.SECOND, 0);
97          calendar.set(Calendar.MILLISECOND, 0);
98          midnightToday = calendar.getTimeInMillis();
99  
100         calendar.add(Calendar.DATE, 1);
101         midnightTomorrow = calendar.getTimeInMillis();
102     }
103 
104     /**
105      * Package-protected for tests.
106      *
107      * @param currentTimeMillis the time to compare with the boundaries. May re-initialize the cached midnight
108      *          boundary values.
109      * @return the action to perform
110      */
111     Result filter(final long currentTimeMillis) {
112         if (currentTimeMillis >= midnightTomorrow || currentTimeMillis < midnightToday) {
113             initMidnight(currentTimeMillis);
114         }
115         return currentTimeMillis >= midnightToday + start && currentTimeMillis <= midnightToday + end //
116                 ? onMatch // within window
117                 : onMismatch;
118     }
119 
120     @Override
121     public Result filter(final LogEvent event) {
122         return filter(event.getTimeMillis());
123     }
124 
125     private Result filter() {
126         return filter(CLOCK.currentTimeMillis());
127     }
128 
129     @Override
130     public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
131             final Throwable t) {
132         return filter();
133     }
134 
135     @Override
136     public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
137             final Throwable t) {
138         return filter();
139     }
140 
141     @Override
142     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
143             final Object... params) {
144         return filter();
145     }
146 
147     @Override
148     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
149             final Object p0) {
150         return filter();
151     }
152 
153     @Override
154     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
155             final Object p0, final Object p1) {
156         return filter();
157     }
158 
159     @Override
160     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
161             final Object p0, final Object p1, final Object p2) {
162         return filter();
163     }
164 
165     @Override
166     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
167             final Object p0, final Object p1, final Object p2, final Object p3) {
168         return filter();
169     }
170 
171     @Override
172     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
173             final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
174         return filter();
175     }
176 
177     @Override
178     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
179             final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
180         return filter();
181     }
182 
183     @Override
184     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
185             final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
186             final Object p6) {
187         return filter();
188     }
189 
190     @Override
191     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
192             final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
193             final Object p6, final Object p7) {
194         return filter();
195     }
196 
197     @Override
198     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
199             final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
200             final Object p6, final Object p7, final Object p8) {
201         return filter();
202     }
203 
204     @Override
205     public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
206             final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
207             final Object p6, final Object p7, final Object p8, final Object p9) {
208         return filter();
209     }
210 
211     @Override
212     public String toString() {
213         final StringBuilder sb = new StringBuilder();
214         sb.append("start=").append(start);
215         sb.append(", end=").append(end);
216         sb.append(", timezone=").append(timezone.toString());
217         return sb.toString();
218     }
219 
220     /**
221      * Creates a TimeFilter.
222      * @param start The start time.
223      * @param end The end time.
224      * @param tz timezone.
225      * @param match Action to perform if the time matches.
226      * @param mismatch Action to perform if the action does not match.
227      * @return A TimeFilter.
228      */
229     // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder
230     @PluginFactory
231     public static TimeFilter createFilter(
232             @PluginAttribute("start") final String start,
233             @PluginAttribute("end") final String end,
234             @PluginAttribute("timezone") final String tz,
235             @PluginAttribute("onMatch") final Result match,
236             @PluginAttribute("onMismatch") final Result mismatch) {
237         final long s = parseTimestamp(start, 0);
238         final long e = parseTimestamp(end, Long.MAX_VALUE);
239         final TimeZone timezone = tz == null ? TimeZone.getDefault() : TimeZone.getTimeZone(tz);
240         final Result onMatch = match == null ? Result.NEUTRAL : match;
241         final Result onMismatch = mismatch == null ? Result.DENY : mismatch;
242         return new TimeFilter(s, e, timezone, onMatch, onMismatch);
243     }
244 
245     private static long parseTimestamp(final String timestamp, final long defaultValue) {
246         if (timestamp == null) {
247             return defaultValue;
248         }
249         final SimpleDateFormat stf = new SimpleDateFormat("HH:mm:ss");
250         stf.setTimeZone(TimeZone.getTimeZone("UTC"));
251         try {
252             return stf.parse(timestamp).getTime();
253         } catch (final ParseException e) {
254             LOGGER.warn("Error parsing TimeFilter timestamp value {}", timestamp, e);
255             return defaultValue;
256         }
257     }
258 
259 }