001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest; 014 015import static org.apache.juneau.rest.Enablement.*; 016 017import java.util.*; 018import java.util.logging.*; 019 020import javax.servlet.http.*; 021 022import org.apache.juneau.*; 023import org.apache.juneau.json.*; 024 025/** 026 * Represents a set of logging rules for how to handle logging of HTTP requests/responses. 027 */ 028public class RestCallLoggerConfig { 029 030 /** 031 * Default empty logging config. 032 */ 033 public static final RestCallLoggerConfig DEFAULT = RestCallLoggerConfig.create().build(); 034 035 /** 036 * Default debug logging config. 037 */ 038 public static final RestCallLoggerConfig DEFAULT_DEBUG = 039 RestCallLoggerConfig 040 .create() 041 .useStackTraceHashing(false) 042 .level(Level.WARNING) 043 .rules( 044 RestCallLoggerRule 045 .create() 046 .codes("*") 047 .verbose() 048 .build() 049 ) 050 .build(); 051 052 private final RestCallLoggerRule[] rules; 053 private final boolean useStackTraceHashing; 054 private final Enablement disabled; 055 private final int stackTraceHashingTimeout; 056 private final Level level; 057 058 RestCallLoggerConfig(Builder b) { 059 RestCallLoggerConfig p = b.parent; 060 061 this.disabled = b.disabled != null ? b.disabled : p != null ? p.disabled : FALSE; 062 this.useStackTraceHashing = b.useStackTraceHashing != null ? b.useStackTraceHashing : p != null ? p.useStackTraceHashing : false; 063 this.stackTraceHashingTimeout = b.stackTraceHashingTimeout != null ? b.stackTraceHashingTimeout : p != null ? p.stackTraceHashingTimeout : Integer.MAX_VALUE; 064 this.level = b.level != null ? b.level : p != null ? p.level : Level.INFO; 065 066 ArrayList<RestCallLoggerRule> rules = new ArrayList<>(); 067 rules.addAll(b.rules); 068 if (p != null) 069 rules.addAll(Arrays.asList(p.rules)); 070 this.rules = rules.toArray(new RestCallLoggerRule[rules.size()]); 071 } 072 073 /** 074 * Creates a builder for this class. 075 * 076 * @return A new builder for this class. 077 */ 078 public static Builder create() { 079 return new Builder(); 080 } 081 082 /** 083 * Builder for {@link RestCallLoggerConfig} objects. 084 */ 085 public static class Builder { 086 List<RestCallLoggerRule> rules = new ArrayList<>(); 087 RestCallLoggerConfig parent; 088 Level level; 089 Boolean useStackTraceHashing; 090 Enablement disabled; 091 Integer stackTraceHashingTimeout; 092 093 /** 094 * Sets the parent logging config. 095 * 096 * @param parent 097 * The parent logging config. 098 * <br>Can be <jk>null</jk>. 099 * @return This object (for method chaining). 100 */ 101 public Builder parent(RestCallLoggerConfig parent) { 102 this.parent = parent; 103 return this; 104 } 105 106 /** 107 * Adds a new logging rule to this config. 108 * 109 * <p> 110 * The rule will be added to the END of list of current rules and thus checked last in the current list but 111 * before any parent rules. 112 * 113 * @param rule The logging rule to add to this config. 114 * @return This object (for method chaining). 115 */ 116 public Builder rule(RestCallLoggerRule rule) { 117 this.rules.add(rule); 118 return this; 119 } 120 121 /** 122 * Adds new logging rules to this config. 123 * 124 * <p> 125 * The rules will be added in order to the END of list of current rules and thus checked last in the current list but 126 * before any parent rules. 127 * 128 * @param rules The logging rules to add to this config. 129 * @return This object (for method chaining). 130 */ 131 public Builder rules(RestCallLoggerRule...rules) { 132 for (RestCallLoggerRule rule : rules) 133 this.rules.add(rule); 134 return this; 135 } 136 137 /** 138 * Enables no-trace mode on this config. 139 * 140 * <p> 141 * No-trace mode prevents logging of messages to the log file. 142 * 143 * <p> 144 * Possible values (case-insensitive): 145 * <ul> 146 * <li>{@link Enablement#TRUE TRUE} - No-trace mode enabled for all requests. 147 * <li>{@link Enablement#FALSE FALSE} - No-trace mode disabled for all requests. 148 * <li>{@link Enablement#PER_REQUEST PER_REQUEST} - No-trace mode enabled for requests that have a <js>"X-NoTrace: true"</js> header. 149 * </ul> 150 * 151 * @param value 152 * The value for this property. 153 * <br>Can be <jk>null</jk> (inherit from parent or default to {@link Enablement#FALSE NEVER}). 154 * @return This object (for method chaining). 155 */ 156 public Builder disabled(Enablement value) { 157 this.disabled = value; 158 return this; 159 } 160 161 /** 162 * Shortcut for calling <c>disabled(<jsf>TRUE</jsf>)</c>. 163 * 164 * @return This object (for method chaining). 165 */ 166 public Builder disabled() { 167 return disabled(TRUE); 168 } 169 170 /** 171 * Enables the use of stacktrace hashing. 172 * 173 * <p> 174 * When enabled, stacktraces will be replaced with hashes in the log file. 175 * 176 * @param value 177 * The value for this property. 178 * <br>Can be <jk>null</jk> (inherit from parent or default to <jk>false</jk>). 179 * @return This object (for method chaining). 180 */ 181 public Builder useStackTraceHashing(Boolean value) { 182 this.useStackTraceHashing = value; 183 return this; 184 } 185 186 /** 187 * Shortcut for calling <c>useStackTraceHashing(<jk>true</jk>);</c>. 188 * 189 * @return This object (for method chaining). 190 */ 191 public Builder useStackTraceHashing() { 192 this.useStackTraceHashing = true; 193 return this; 194 } 195 196 /** 197 * Enables a timeout after which stack traces hashes are flushed. 198 * 199 * @param timeout 200 * Time in milliseconds to hash stack traces for. 201 * <br>Can be <jk>null</jk> (inherit from parent or default to {@link Integer#MAX_VALUE MAX_VALUE}). 202 * @return This object (for method chaining). 203 */ 204 public Builder stackTraceHashingTimeout(Integer timeout) { 205 this.stackTraceHashingTimeout = timeout; 206 return this; 207 } 208 209 /** 210 * The default logging level. 211 * 212 * <p> 213 * This defines the logging level for messages if they're not already defined on the matched rule. 214 * 215 * @param value 216 * The value for this property. 217 * <br>Can be <jk>null</jk> (inherit from parent or default to {@link Level#INFO INFO}). 218 * @return This object (for method chaining). 219 */ 220 public Builder level(Level value) { 221 this.level = value; 222 return this; 223 } 224 225 /** 226 * Applies the properties in the specified object map to this builder. 227 * 228 * @param m The map containing properties to apply. 229 * @return This object (for method chaining). 230 */ 231 public Builder apply(ObjectMap m) { 232 for (String key : m.keySet()) { 233 if ("useStackTraceHashing".equals(key)) 234 useStackTraceHashing(m.getBoolean("useStackTraceHashing")); 235 else if ("stackTraceHashingTimeout".equals(key)) 236 stackTraceHashingTimeout(m.getInt("stackTraceHashingTimeout")); 237 else if ("disabled".equals(key)) 238 disabled(m.get("disabled", Enablement.class)); 239 else if ("rules".equals(key)) 240 rules(m.get("rules", RestCallLoggerRule[].class)); 241 else if ("level".equals(key)) 242 level(m.get("level", Level.class)); 243 } 244 return this; 245 } 246 247 /** 248 * Creates the {@link RestCallLoggerConfig} object based on settings on this builder. 249 * 250 * @return A new {@link RestCallLoggerConfig} object. 251 */ 252 public RestCallLoggerConfig build() { 253 return new RestCallLoggerConfig(this); 254 } 255 } 256 257 /** 258 * Given the specified servlet request/response, find the rule that applies to it. 259 * 260 * @param req The servlet request. 261 * @param res The servlet response. 262 * @return The applicable logging rule, or <jk>null</jk> if a match could not be found. 263 */ 264 public RestCallLoggerRule getRule(HttpServletRequest req, HttpServletResponse res) { 265 266 int status = res.getStatus(); 267 Throwable e = (Throwable)req.getAttribute("Exception"); 268 boolean debug = isDebug(req); 269 270 for (RestCallLoggerRule r : rules) { 271 if (r.matches(status, debug, e)) { 272 Enablement disabled = r.getDisabled(); 273 if (disabled == null) 274 disabled = this.disabled; 275 if (disabled == TRUE) 276 return null; 277 if (isNoTraceAttr(req)) 278 return null; 279 if (disabled == FALSE) 280 return r; 281 if (isNoTraceHeader(req)) 282 return null; 283 return r; 284 } 285 } 286 287 return null; 288 } 289 290 /** 291 * Returns <jk>true</jk> if logging is disabled for this request. 292 * 293 * @param req The HTTP request. 294 * @return <jk>true</jk> if logging is disabled for this request. 295 */ 296 public boolean isDisabled(HttpServletRequest req) { 297 if (disabled == TRUE) 298 return true; 299 if (disabled == FALSE) 300 return false; 301 return isNoTraceAttr(req); 302 } 303 304 private boolean isDebug(HttpServletRequest req) { 305 Boolean b = boolAttr(req, "Debug"); 306 return (b != null && b == true); 307 } 308 309 private boolean isNoTraceAttr(HttpServletRequest req) { 310 Boolean b = boolAttr(req, "NoTrace"); 311 return (b != null && b == true); 312 } 313 314 private boolean isNoTraceHeader(HttpServletRequest req) { 315 return "true".equalsIgnoreCase(req.getHeader("X-NoTrace")); 316 } 317 318 /** 319 * Returns the default logging level. 320 * 321 * @return The default logging level. 322 */ 323 public Level getLevel() { 324 return level; 325 } 326 327 /** 328 * Returns <jk>true</jk> if stack traces should be hashed. 329 * 330 * @return <jk>true</jk> if stack traces should be hashed. 331 */ 332 public boolean isUseStackTraceHashing() { 333 return useStackTraceHashing; 334 } 335 336 /** 337 * Returns the time in milliseconds that stacktrace hashes should be persisted. 338 * 339 * @return The time in milliseconds that stacktrace hashes should be persisted. 340 */ 341 public int getStackTraceHashingTimeout() { 342 return stackTraceHashingTimeout; 343 } 344 345 /** 346 * Returns the rules defined in this config. 347 * 348 * @return Thew rules defined in this config. 349 */ 350 public List<RestCallLoggerRule> getRules() { 351 return Collections.unmodifiableList(Arrays.asList(rules)); 352 } 353 354 //----------------------------------------------------------------------------------------------------------------- 355 // Other methods 356 //----------------------------------------------------------------------------------------------------------------- 357 358 private Boolean boolAttr(HttpServletRequest req, String name) { 359 Object o = req.getAttribute(name); 360 if (o == null || ! (o instanceof Boolean)) 361 return null; 362 return (Boolean)o; 363 } 364 365 @Override /* Object */ 366 public String toString() { 367 return SimpleJsonSerializer.DEFAULT_READABLE.toString(toMap()); 368 } 369 370 /** 371 * Returns the properties defined on this bean context as a simple map for debugging purposes. 372 * 373 * @return A new map containing the properties defined on this context. 374 */ 375 public ObjectMap toMap() { 376 return new DefaultFilteringObjectMap() 377 .append("useStackTraceHashing", useStackTraceHashing) 378 .append("disabled", disabled == FALSE ? null : disabled) 379 .append("stackTraceHashingTimeout", stackTraceHashingTimeout == Integer.MAX_VALUE ? null : stackTraceHashingTimeout) 380 .append("level", level == Level.INFO ? null : level) 381 .append("rules", rules.length == 0 ? null : rules) 382 ; 383 } 384}