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     * Ending offset from midnight in milliseconds.
067     */
068    private final long end;
069    /**
070     * Timezone.
071     */
072    private final TimeZone timezone;
073
074    private long midnightToday;
075    private long midnightTomorrow;
076
077
078    private TimeFilter(final long start, final long end, final TimeZone tz, final Result onMatch,
079                       final Result onMismatch) {
080        super(onMatch, onMismatch);
081        this.start = start;
082        this.end = end;
083        timezone = tz;
084        initMidnight(start);
085    }
086
087    /**
088     * Initializes the midnight boundaries to midnight in the specified time zone.
089     * @param now a time in milliseconds since the epoch, used to pinpoint the current date
090     */
091    void initMidnight(final long now) {
092        final Calendar calendar = Calendar.getInstance(timezone);
093        calendar.setTimeInMillis(now);
094        calendar.set(Calendar.HOUR_OF_DAY, 0);
095        calendar.set(Calendar.MINUTE, 0);
096        calendar.set(Calendar.SECOND, 0);
097        calendar.set(Calendar.MILLISECOND, 0);
098        midnightToday = calendar.getTimeInMillis();
099
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     * Create 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    @PluginFactory
230    public static TimeFilter createFilter(
231            @PluginAttribute("start") final String start,
232            @PluginAttribute("end") final String end,
233            @PluginAttribute("timezone") final String tz,
234            @PluginAttribute("onMatch") final Result match,
235            @PluginAttribute("onMismatch") final Result mismatch) {
236        final long s = parseTimestamp(start, 0);
237        final long e = parseTimestamp(end, Long.MAX_VALUE);
238        final TimeZone timezone = tz == null ? TimeZone.getDefault() : TimeZone.getTimeZone(tz);
239        final Result onMatch = match == null ? Result.NEUTRAL : match;
240        final Result onMismatch = mismatch == null ? Result.DENY : mismatch;
241        return new TimeFilter(s, e, timezone, onMatch, onMismatch);
242    }
243
244    private static long parseTimestamp(final String timestamp, final long defaultValue) {
245        if (timestamp == null) {
246            return defaultValue;
247        }
248        final SimpleDateFormat stf = new SimpleDateFormat("HH:mm:ss");
249        stf.setTimeZone(TimeZone.getTimeZone("UTC"));
250        try {
251            return stf.parse(timestamp).getTime();
252        } catch (final ParseException e) {
253            LOGGER.warn("Error parsing TimeFilter timestamp value {}", timestamp, e);
254            return defaultValue;
255        }
256    }
257
258}