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