001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.filter;
018
019import java.text.ParseException;
020import java.text.SimpleDateFormat;
021import java.util.Calendar;
022import java.util.TimeZone;
023
024import org.apache.logging.log4j.Level;
025import org.apache.logging.log4j.Marker;
026import org.apache.logging.log4j.core.Filter;
027import org.apache.logging.log4j.core.LogEvent;
028import org.apache.logging.log4j.core.Logger;
029import org.apache.logging.log4j.core.config.Node;
030import org.apache.logging.log4j.core.config.plugins.Plugin;
031import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
032import org.apache.logging.log4j.core.config.plugins.PluginFactory;
033import org.apache.logging.log4j.core.util.Clock;
034import org.apache.logging.log4j.core.util.ClockFactory;
035import org.apache.logging.log4j.message.Message;
036import org.apache.logging.log4j.util.PerformanceSensitive;
037
038/**
039 * Filters events that fall within a specified time period in each day.
040 */
041@Plugin(name = "TimeFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
042@PerformanceSensitive("allocation")
043public final class TimeFilter extends AbstractFilter {
044    private static final Clock CLOCK = ClockFactory.getClock();
045
046    /**
047     * Length of hour in milliseconds.
048     */
049    private static final long HOUR_MS = 3600000;
050
051    /**
052     * Length of minute in milliseconds.
053     */
054    private static final long MINUTE_MS = 60000;
055
056    /**
057     * Length of second in milliseconds.
058     */
059    private static final long SECOND_MS = 1000;
060
061    /**
062     * Starting offset from midnight in milliseconds.
063     */
064    private final long start;
065    
066    /**
067     * Ending offset from midnight in milliseconds.
068     */
069    private final long end;
070    
071    /**
072     * Timezone.
073     */
074    private final TimeZone timeZone;
075
076    private long midnightToday;
077    private long midnightTomorrow;
078
079
080    private TimeFilter(final long start, final long end, final TimeZone timeZone, final Result onMatch,
081                       final Result onMismatch) {
082        super(onMatch, onMismatch);
083        this.start = start;
084        this.end = end;
085        this.timeZone = timeZone;
086        initMidnight(start);
087    }
088
089    /**
090     * Initializes the midnight boundaries to midnight in the specified time zone.
091     * @param now a time in milliseconds since the epoch, used to pinpoint the current date
092     */
093    void initMidnight(final long now) {
094        final Calendar calendar = Calendar.getInstance(timeZone);
095        calendar.setTimeInMillis(now);
096        calendar.set(Calendar.HOUR_OF_DAY, 0);
097        calendar.set(Calendar.MINUTE, 0);
098        calendar.set(Calendar.SECOND, 0);
099        calendar.set(Calendar.MILLISECOND, 0);
100        midnightToday = calendar.getTimeInMillis();
101
102        calendar.add(Calendar.DATE, 1);
103        midnightTomorrow = calendar.getTimeInMillis();
104    }
105
106    /**
107     * Package-protected for tests.
108     *
109     * @param currentTimeMillis the time to compare with the boundaries. May re-initialize the cached midnight
110     *          boundary values.
111     * @return the action to perform
112     */
113    Result filter(final long currentTimeMillis) {
114        if (currentTimeMillis >= midnightTomorrow || currentTimeMillis < midnightToday) {
115            initMidnight(currentTimeMillis);
116        }
117        return currentTimeMillis >= midnightToday + start && currentTimeMillis <= midnightToday + end //
118                ? onMatch // within window
119                : onMismatch;
120    }
121
122    @Override
123    public Result filter(final LogEvent event) {
124        return filter(event.getTimeMillis());
125    }
126
127    private Result filter() {
128        return filter(CLOCK.currentTimeMillis());
129    }
130
131    @Override
132    public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
133            final Throwable t) {
134        return filter();
135    }
136
137    @Override
138    public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
139            final Throwable t) {
140        return filter();
141    }
142
143    @Override
144    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
145            final Object... params) {
146        return filter();
147    }
148
149    @Override
150    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
151            final Object p0) {
152        return filter();
153    }
154
155    @Override
156    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
157            final Object p0, final Object p1) {
158        return filter();
159    }
160
161    @Override
162    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
163            final Object p0, final Object p1, final Object p2) {
164        return filter();
165    }
166
167    @Override
168    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
169            final Object p0, final Object p1, final Object p2, final Object p3) {
170        return filter();
171    }
172
173    @Override
174    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
175            final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
176        return filter();
177    }
178
179    @Override
180    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
181            final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
182        return filter();
183    }
184
185    @Override
186    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
187            final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
188            final Object p6) {
189        return filter();
190    }
191
192    @Override
193    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
194            final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
195            final Object p6, final Object p7) {
196        return filter();
197    }
198
199    @Override
200    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
201            final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
202            final Object p6, final Object p7, final Object p8) {
203        return filter();
204    }
205
206    @Override
207    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
208            final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
209            final Object p6, final Object p7, final Object p8, final Object p9) {
210        return filter();
211    }
212
213    @Override
214    public String toString() {
215        final StringBuilder sb = new StringBuilder();
216        sb.append("start=").append(start);
217        sb.append(", end=").append(end);
218        sb.append(", timezone=").append(timeZone.toString());
219        return sb.toString();
220    }
221
222    /**
223     * Creates a TimeFilter.
224     * @param start The start time.
225     * @param end The end time.
226     * @param tz timezone.
227     * @param match Action to perform if the time matches.
228     * @param mismatch Action to perform if the action does not match.
229     * @return A TimeFilter.
230     */
231    // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder
232    @PluginFactory
233    public static TimeFilter createFilter(
234            @PluginAttribute("start") final String start,
235            @PluginAttribute("end") final String end,
236            @PluginAttribute("timezone") final String tz,
237            @PluginAttribute("onMatch") final Result match,
238            @PluginAttribute("onMismatch") final Result mismatch) {
239        final long s = parseTimestamp(start, 0);
240        final long e = parseTimestamp(end, Long.MAX_VALUE);
241        final TimeZone timeZone = tz == null ? TimeZone.getDefault() : TimeZone.getTimeZone(tz);
242        final Result onMatch = match == null ? Result.NEUTRAL : match;
243        final Result onMismatch = mismatch == null ? Result.DENY : mismatch;
244        return new TimeFilter(s, e, timeZone, onMatch, onMismatch);
245    }
246
247    private static long parseTimestamp(final String timestamp, final long defaultValue) {
248        if (timestamp == null) {
249            return defaultValue;
250        }
251        final SimpleDateFormat stf = new SimpleDateFormat("HH:mm:ss");
252        stf.setTimeZone(TimeZone.getTimeZone("UTC"));
253        try {
254            return stf.parse(timestamp).getTime();
255        } catch (final ParseException e) {
256            LOGGER.warn("Error parsing TimeFilter timestamp value {}", timestamp, e);
257            return defaultValue;
258        }
259    }
260
261}