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     */
017    package org.apache.logging.log4j.core.filter;
018    
019    import org.apache.logging.log4j.core.LogEvent;
020    import org.apache.logging.log4j.core.config.plugins.Plugin;
021    import org.apache.logging.log4j.core.config.plugins.PluginAttr;
022    import org.apache.logging.log4j.core.config.plugins.PluginFactory;
023    
024    import java.text.ParseException;
025    import java.text.SimpleDateFormat;
026    import java.util.Calendar;
027    import java.util.Locale;
028    import java.util.TimeZone;
029    
030    /**
031     * Filters events that fall within a specified time period in each day.
032     */
033    @Plugin(name = "TimeFilter", type = "Core", elementType = "filter", printObject = true)
034    public final class TimeFilter extends AbstractFilter {
035        /**
036         * Length of hour in milliseconds.
037         */
038        private static final long HOUR_MS = 3600000;
039    
040        /**
041         * Length of minute in milliseconds.
042         */
043        private static final long MINUTE_MS = 60000;
044    
045        /**
046         * Length of second in milliseconds.
047         */
048        private static final long SECOND_MS = 1000;
049    
050        /**
051         * Starting offset from midnight in milliseconds.
052         */
053        private final long start;
054        /**
055         * Ending offset from midnight in milliseconds.
056         */
057        private final long end;
058        /**
059         * Timezone.
060         */
061        private final TimeZone timezone;
062    
063    
064        private TimeFilter(long start, long end, TimeZone tz, Result onMatch, Result onMismatch) {
065            super(onMatch, onMismatch);
066            this.start = start;
067            this.end = end;
068            timezone = tz;
069        }
070    
071        @Override
072        public Result filter(LogEvent event) {
073            Calendar calendar = Calendar.getInstance(timezone);
074            calendar.setTimeInMillis(event.getMillis());
075            //
076            //   get apparent number of milliseconds since midnight
077            //      (ignores extra or missing hour on daylight time changes).
078            //
079            long apparentOffset = calendar.get(Calendar.HOUR_OF_DAY) * HOUR_MS +
080                calendar.get(Calendar.MINUTE) * MINUTE_MS +
081                calendar.get(Calendar.SECOND) * SECOND_MS +
082                calendar.get(Calendar.MILLISECOND);
083            return (apparentOffset >= start && apparentOffset < end) ? onMatch : onMismatch;
084        }
085    
086        @Override
087        public String toString() {
088            StringBuilder sb = new StringBuilder();
089            sb.append("start=").append(start);
090            sb.append(", end=").append(end);
091            sb.append(", timezone=").append(timezone.toString());
092            return sb.toString();
093        }
094    
095        /**
096         * Create a TimeFilter.
097         * @param start The start time.
098         * @param end The end time.
099         * @param tz timezone.
100         * @param match Action to perform if the time matches.
101         * @param mismatch Action to perform if the action does not match.
102         * @return A TimeFilter.
103         */
104        @PluginFactory
105        public static TimeFilter createFilter(@PluginAttr("start") String start,
106                                              @PluginAttr("end") String end,
107                                              @PluginAttr("timezone") String tz,
108                                              @PluginAttr("onMatch") String match,
109                                              @PluginAttr("onMismatch") String mismatch) {
110            SimpleDateFormat stf = new SimpleDateFormat("HH:mm:ss");
111            long s = 0;
112            if (start != null) {
113                stf.setTimeZone(TimeZone.getTimeZone("UTC"));
114                try {
115                    s = stf.parse(start).getTime();
116                } catch (ParseException ex) {
117                    LOGGER.warn("Error parsing start value " + start, ex);
118                }
119            }
120            long e = Long.MAX_VALUE;
121            if (end != null) {
122                stf.setTimeZone(TimeZone.getTimeZone("UTC"));
123                try {
124                    e = stf.parse(end).getTime();
125                } catch (ParseException ex) {
126                    LOGGER.warn("Error parsing start value " + end, ex);
127                }
128            }
129            TimeZone timezone = (tz == null) ? TimeZone.getDefault() : TimeZone.getTimeZone(tz);
130            Result onMatch = Result.toResult(match, Result.NEUTRAL);
131            Result onMismatch = Result.toResult(mismatch, Result.DENY);
132            return new TimeFilter(s, e, timezone, onMatch, onMismatch);
133        }
134    
135    }