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.internal.StringUtils.*; 016import static org.apache.juneau.rest.RestCallLoggingDetail.*; 017 018import java.util.logging.*; 019 020import org.apache.juneau.*; 021import org.apache.juneau.annotation.*; 022import org.apache.juneau.json.*; 023import org.apache.juneau.pojotools.*; 024 025/** 026 * Represents a logging rule used for determine how detailed requests should be logged at. 027 */ 028public class RestCallLoggerRule { 029 030 private final Matcher codeMatcher; 031 private final Matcher exceptionMatcher; 032 private final boolean debugOnly; 033 private final Level level; 034 private final Enablement disabled; 035 private final RestCallLoggingDetail req, res; 036 037 /** 038 * Constructor. 039 * 040 * @param b Builder 041 */ 042 RestCallLoggerRule(Builder b) { 043 this.codeMatcher = isEmpty(b.codes) || "*".equals(b.codes) ? null : NumberMatcherFactory.DEFAULT.create(b.codes); 044 this.exceptionMatcher = isEmpty(b.exceptions) ? null : StringMatcherFactory.DEFAULT.create(b.exceptions); 045 boolean verbose = b.verbose == null ? false : b.verbose; 046 this.disabled = b.disabled; 047 this.debugOnly = b.debugOnly == null ? false : b.debugOnly; 048 this.level = b.level; 049 this.req = verbose ? LONG : b.req != null ? b.req : SHORT; 050 this.res = verbose ? LONG : b.res != null ? b.res : SHORT; 051 } 052 053 /** 054 * Creates a new builder for this object. 055 * 056 * @return A new builder for this object. 057 */ 058 public static Builder create() { 059 return new Builder(); 060 } 061 062 /** 063 * Builder class for this object. 064 */ 065 @Bean(fluentSetters=true) 066 public static class Builder { 067 String codes, exceptions; 068 Boolean verbose, debugOnly; 069 Enablement disabled; 070 Level level; 071 RestCallLoggingDetail req, res; 072 073 /** 074 * The code ranges that this logging rule applies to. 075 * 076 * <p> 077 * See {@link NumberMatcherFactory} for format of values. 078 * 079 * <p> 080 * <js>"*"</js> can be used to represent all values. 081 * 082 * @param value 083 * The new value for this property. 084 * <br>Can be <jk>null</jk> or an empty string. 085 * @return This object (for method chaining). 086 */ 087 public Builder codes(String value) { 088 this.codes = value; 089 String c = emptyIfNull(trim(codes)); 090 if (c.endsWith("-")) 091 codes += "999"; 092 else if (c.startsWith("-")) 093 codes = "0" + codes; 094 return this; 095 } 096 097 /** 098 * The exception naming pattern that this rule applies to. 099 * 100 * <p> 101 * See {@link StringMatcherFactory} for format of values. 102 * 103 * <p> 104 * The pattern can be against either the fully-qualified or simple class name of the exception. 105 * 106 * @param value 107 * The new value for this property. 108 * <br>Can be <jk>null</jk> or an empty string. 109 * @return This object (for method chaining). 110 */ 111 public Builder exceptions(String value) { 112 this.exceptions = value; 113 return this; 114 } 115 116 /** 117 * Shortcut for specifying {@link RestCallLoggingDetail#LONG} for {@link #req(RestCallLoggingDetail)} and {@link #res(RestCallLoggingDetail)}. 118 * 119 * @param value 120 * The new value for this property. 121 * <br>Can be <jk>null</jk>. 122 * @return This object (for method chaining). 123 */ 124 public Builder verbose(Boolean value) { 125 this.verbose = value; 126 return this; 127 } 128 129 /** 130 * Shortcut for calling <c>verbose(<jk>true</jk>);</c> 131 * 132 * @return This object (for method chaining). 133 */ 134 public Builder verbose() { 135 return this.verbose(true); 136 } 137 138 /** 139 * Shortcut for specifying {@link Level#OFF} for {@link #level(Level)}. 140 * 141 * @param value 142 * The new value for this property. 143 * <br>Can be <jk>null</jk>. 144 * @return This object (for method chaining). 145 */ 146 public Builder disabled(Enablement value) { 147 this.disabled = value; 148 return this; 149 } 150 151 /** 152 * Shortcut for calling <c>disabled(<jk>true</jk>);</c> 153 * 154 * @return This object (for method chaining). 155 */ 156 public Builder disabled() { 157 return this.disabled(Enablement.TRUE); 158 } 159 160 /** 161 * This match only applies when debug is enabled on the request. 162 * 163 * @param value The new value for this property. 164 * @return This object (for method chaining). 165 */ 166 public Builder debugOnly(Boolean value) { 167 this.debugOnly = value; 168 return this; 169 } 170 171 /** 172 * Shortcut for calling <c>debugOnly(<jk>true</jk>);</c> 173 * 174 * @return This object (for method chaining). 175 */ 176 public Builder debugOnly() { 177 return this.debugOnly(true); 178 } 179 180 /** 181 * The level of detail to log on a request. 182 * 183 * <p> 184 * The default value is {@link RestCallLoggingDetail#SHORT}. 185 * 186 * @param value 187 * The new value for this property. 188 * <br>Can be <jk>null</jk> 189 * @return This object (for method chaining). 190 */ 191 public Builder req(RestCallLoggingDetail value) { 192 this.req = value; 193 return this; 194 } 195 196 /** 197 * The level of detail to log on a response. 198 * 199 * <p> 200 * The default value is {@link RestCallLoggingDetail#SHORT}. 201 * 202 * @param value 203 * The new value for this property. 204 * <br>Can be <jk>null</jk> 205 * @return This object (for method chaining). 206 */ 207 public Builder res(RestCallLoggingDetail value) { 208 this.res = value; 209 return this; 210 } 211 212 /** 213 * The logging level to use for logging the request/response. 214 * 215 * <p> 216 * The default value is {@link Level#INFO}. 217 * 218 * @param value 219 * The new value for this property. 220 * <br>Can be <jk>null</jk> 221 * @return This object (for method chaining). 222 */ 223 public Builder level(Level value) { 224 this.level = value; 225 return this; 226 } 227 228 /** 229 * Instantiates a new {@link RestCallLoggerRule} object using the settings in this builder. 230 * 231 * @return A new {@link RestCallLoggerRule} object. 232 */ 233 public RestCallLoggerRule build() { 234 return new RestCallLoggerRule(this); 235 } 236 } 237 238 /** 239 * Returns <jk>true</jk> if this rule matches the specified parameters. 240 * 241 * @param statusCode The HTTP response status code. 242 * @param debug Whether debug is enabled on the request. 243 * @param e Exception thrown while handling the request. 244 * @return <jk>true</jk> if this rule matches the specified parameters. 245 */ 246 public boolean matches(int statusCode, boolean debug, Throwable e) { 247 if (debugOnly && ! debug) 248 return false; 249 if (exceptionMatcher != null) { 250 if (e == null) 251 return false; 252 Class<?> c = e.getClass(); 253 if (! (exceptionMatcher.matches(null, c.getName()) || exceptionMatcher.matches(null, c.getSimpleName()))) 254 return false; 255 } 256 if (codeMatcher == null || codeMatcher.matches(null, statusCode)) 257 return true; 258 return false; 259 } 260 261 /** 262 * Returns the detail level for HTTP requests. 263 * 264 * @return the detail level for HTTP requests. 265 */ 266 public RestCallLoggingDetail getReqDetail() { 267 return req; 268 } 269 270 /** 271 * Returns the detail level for HTTP responses. 272 * 273 * @return the detail level for HTTP responses. 274 */ 275 public RestCallLoggingDetail getResDetail() { 276 return res; 277 } 278 279 /** 280 * Returns the log level. 281 * 282 * @return the log level. 283 */ 284 public Level getLevel() { 285 return level; 286 } 287 288 /** 289 * Returns the disabled flag. 290 * 291 * @return the disabled flag. 292 */ 293 public Enablement getDisabled() { 294 return disabled; 295 } 296 297 private ObjectMap toMap() { 298 return new DefaultFilteringObjectMap() 299 .append("codes", codeMatcher) 300 .append("exceptions", exceptionMatcher) 301 .append("debugOnly", debugOnly) 302 .append("level", level) 303 .append("req", req == SHORT ? null : req) 304 .append("res", res == SHORT ? null : res); 305 } 306 307 @Override /* Object */ 308 public String toString() { 309 return SimpleJsonSerializer.DEFAULT.toString(toMap()); 310 } 311}