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.tomcat.util.log;
18
19 import org.apache.juli.logging.Log;
20
21 /**
22  * This helper class assists with the logging associated with invalid input
23  * data. A developer may want all instances of invalid input data logged to
24  * assist with debugging whereas in production it is likely to be desirable not
25  * to log anything for invalid data. The following settings may be used:
26  * <ul>
27  * <li>NOTHING: Log nothing.</li>
28  * <li>DEBUG_ALL: Log all problems at DEBUG log level.</li>
29  * <li>INFO_THEN_DEBUG: Log first problem at INFO log level and any further
30  *     issues in the following TBD (configurable) seconds at DEBUG level</li>
31  * <li>INFO_ALL: Log all problems at INFO log level.</li>
32  * </ul>
33  * By default, INFO_THEN_DEBUG is used with a suppression time of 24 hours.
34  *
35  * NOTE: This class is not completely thread-safe. When using INFO_THEN_DEBUG it
36  * is possible that several INFO messages will be logged before dropping to
37  * DEBUG.
38  */

39 public class UserDataHelper {
40
41     private final Log log;
42
43     private final Config config;
44
45     // A value of 0 is equivalent to using INFO_ALL
46     // A negative value will trigger infinite suppression
47     // The value is milliseconds
48     private final long suppressionTime;
49
50     private volatile long lastInfoTime = 0;
51
52
53     public UserDataHelper(Log log) {
54         this.log = log;
55
56         Config tempConfig;
57         String configString = System.getProperty(
58                 "org.apache.juli.logging.UserDataHelper.CONFIG");
59         if (configString == null) {
60             tempConfig = Config.INFO_THEN_DEBUG;
61         } else {
62             try {
63                 tempConfig = Config.valueOf(configString);
64             } catch (IllegalArgumentException iae) {
65                 // Ignore - use default
66                 tempConfig = Config.INFO_THEN_DEBUG;
67             }
68         }
69
70         // Default suppression time of 1 day.
71         suppressionTime = Integer.getInteger(
72                 "org.apache.juli.logging.UserDataHelper.SUPPRESSION_TIME",
73                 60 * 60 * 24).intValue() * 1000L;
74
75         if (suppressionTime == 0) {
76             tempConfig = Config.INFO_ALL;
77         }
78
79         config = tempConfig;
80     }
81
82
83     /**
84      * Returns log mode for the next log message, or <code>null</code> if the
85      * message should not be logged.
86      *
87      * <p>
88      * If <code>INFO_THEN_DEBUG</code> configuration option is enabled, this
89      * method might change internal state of this object.
90      *
91      * @return Log mode, or <code>null</code>
92      */

93     public Mode getNextMode() {
94         if (Config.NONE == config) {
95             return null;
96         } else if (Config.DEBUG_ALL == config) {
97             return log.isDebugEnabled() ? Mode.DEBUG : null;
98         } else if (Config.INFO_THEN_DEBUG == config) {
99             if (logAtInfo()) {
100                 return log.isInfoEnabled() ? Mode.INFO_THEN_DEBUG : null;
101             } else {
102                 return log.isDebugEnabled() ? Mode.DEBUG : null;
103             }
104         } else if (Config.INFO_ALL == config) {
105             return log.isInfoEnabled() ? Mode.INFO : null;
106         }
107         // Should never happen
108         return null;
109     }
110
111
112     /*
113      * Not completely thread-safe but good enough for this use case. I couldn't
114      * see a simple enough way to make it completely thread-safe that was not
115      * likely to compromise performance.
116      */

117     private boolean logAtInfo() {
118
119         if (suppressionTime < 0 && lastInfoTime > 0) {
120             return false;
121         }
122
123         long now = System.currentTimeMillis();
124
125         if (lastInfoTime + suppressionTime > now) {
126             return false;
127         }
128
129         lastInfoTime = now;
130         return true;
131     }
132
133
134     private enum Config {
135         NONE,
136         DEBUG_ALL,
137         INFO_THEN_DEBUG,
138         INFO_ALL
139     }
140
141     /**
142      * Log mode for the next log message.
143      */

144     public enum Mode {
145         DEBUG,
146         INFO_THEN_DEBUG,
147         INFO
148     }
149 }
150