001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.logger;
018
019import static java.util.logging.Level.*;
020import static org.apache.juneau.Enablement.*;
021import static org.apache.juneau.commons.utils.Utils.*;
022import static org.apache.juneau.rest.logger.CallLoggingDetail.*;
023
024import org.apache.juneau.cp.*;
025import org.apache.juneau.rest.*;
026
027import jakarta.servlet.http.*;
028
029/**
030 * Default implementation of a {@link CallLogger} that only logs REST call errors unless no-log is enabled on the request.
031 *
032 * <p>
033 * Useful for REST tests where you know that a particular call is going to produce an error response and you don't want that
034 * response to be logged producing noisy test output.
035 *
036 * <p>
037 * Requests can be tagged as no-log (meaning don't log if there's an error) in any of the following ways:
038 * <ul>
039 *    <li>A <js>"No-Trace: true"</js> header.
040 *    <li>A <js>"noTrace=true"</js> query parameter.
041 *    <li>A <js>"NoTrace"</js> request attribute with a string value of <js>"true"</js>.
042 * </ul>
043 *
044 * <h5 class='section'>Configured Settings:</h5>
045 * <ul>
046 *    <li>Logs to the {@link RestContext#getLogger() context logger}.
047 *    <li>Only calls with status code &gt;=400 will be logged.
048 *    <li>Logs full request and response entity.
049 * </ul>
050 *
051 * <h5 class='section'>See Also:</h5><ul>
052 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/RestServerLoggingAndDebugging">Logging / Debugging</a>
053 * </ul>
054 */
055public class BasicTestCallLogger extends CallLogger {
056
057   private static boolean isNoTrace(HttpServletRequest req) {
058      var o = req.getAttribute("NoTrace");
059      if (nn(o))
060         return eqic("true", o.toString());
061      var s = req.getHeader("No-Trace");
062      if (nn(s))
063         return eqic("true", s);
064      return emptyIfNull(req.getQueryString()).contains("noTrace=true");
065   }
066
067   /**
068    * Constructor.
069    *
070    * @param beanStore The bean store containing injectable beans for this logger.
071    */
072   public BasicTestCallLogger(BeanStore beanStore) {
073      super(beanStore);
074   }
075
076   @Override
077   protected Builder init(BeanStore beanStore) {
078      // @formatter:off
079      return super.init(beanStore)
080         .normalRules(  // Rules when debugging is not enabled.
081            CallLoggerRule.create(beanStore)  // Log 500+ errors with status-line and header information.
082               .statusFilter(x -> x >= 400)
083               .level(SEVERE)
084               .requestDetail(HEADER)
085               .responseDetail(HEADER)
086               .enabled(CONDITIONAL)
087               .enabledPredicate(x -> ! isNoTrace(x))  // Only log if it's not a no-trace request.
088               .logStackTrace()
089               .build(),
090            CallLoggerRule.create(beanStore)  // Log 400-500 errors with just status-line information.
091               .statusFilter(x -> x >= 400)
092               .level(WARNING)
093               .requestDetail(STATUS_LINE)
094               .responseDetail(STATUS_LINE)
095               .enabled(CONDITIONAL)
096               .enabledPredicate(x -> ! isNoTrace(x))  // Only log if it's not a no-trace request.
097               .logStackTrace()
098               .build()
099         )
100         .debugRules(  // Rules when debugging is enabled.
101            CallLoggerRule.create(beanStore)  // Log everything with full details.
102               .level(SEVERE)
103               .requestDetail(ENTITY)
104               .responseDetail(ENTITY)
105               .logStackTrace()
106               .build()
107         )
108      ;
109      // @formatter:on
110   }
111}