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}