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}