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