/* * Copyright 1998-2014 University Corporation for Atmospheric Research/Unidata * * Portions of this software were developed by the Unidata Program at the * University Corporation for Atmospheric Research. * * Access and use of this software shall impose the following obligations * and understandings on the user. The user is granted the right, without * any fee or cost, to use, copy, modify, alter, enhance and distribute * this software, and any derivative works thereof, and its supporting * documentation for any purpose whatsoever, provided that this entire * notice appears in all copies of the software, derivative works and * supporting documentation. Further, UCAR requests that the user credit * UCAR/Unidata in any publications that result from the use of this * software or in any product that includes this software. The names UCAR * and/or Unidata, however, may not be used in any advertising or publicity * to endorse or promote any products or commercial entity unless specific * written permission is obtained from UCAR/Unidata. The user also * understands that UCAR/Unidata is not obligated to provide the user with * any support, consulting, training or assistance of any kind with regard * to the use, operation and performance of this software nor to provide * the user with any updates, revisions, new versions or "bug fixes." * * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. */ package thredds.servlet; import org.slf4j.MDC; import javax.servlet.http.HttpServletRequest; import java.util.Formatter; import java.util.concurrent.atomic.AtomicLong; /** * Utility class for gathering context information for use in log messages. * Includes methods appropriate when the context thread is an individual * HTTP request and when the context thread is an initialization thread. * The context information is contained in a key/value map. * <p/> * <p>Uses the SLF4J MDC framework (see @link{org.slf4j.MDC} for more details). * <p/> * <p>If properly configured, each log entry within the context thread * will include the gathered context information. For instance, in log4j * and slf4j, the appender pattern would contain strings with the form * "%X{<contextKey>}", where "<contextKey>" is a context key * value. The context key strings are given in each setup method below. * * @author caron * @see org.slf4j.MDC * @since Jan 9, 2009 */ public class UsageLog { private static final AtomicLong logServerAccessId = new AtomicLong(0); /** * Gather context information for the given HTTP request and return * a log message appropriate for logging at the start of the request. * <p/> * <p>The following context information is gathered: * <ul> * <li>"ID" - an identifier for the current thread;</li> * <li>"host" - the remote host (IP address or host name);</li> * <li>"userid" - the id of the remote user;</li> * <li>"startTime" - the system time in millis when this request is started (i.e., when this method is called); and</li> * <li>"request" - The HTTP request, e.g., "GET /index.html HTTP/1.1".</li> * </ul> * <p/> * <p>Call this method at the start of each HttpServlet doXXX() method * (e.g., doGet(), doPut()) or Spring MVC Controller handle() method. * * @param req the current request * @return a log message appropriate for the start of the request. */ public static String setupRequestContext(HttpServletRequest req) { // Setup context. //HttpSession session = req.getSession(false); /* MDC.put("host", req.getRemoteHost()); MDC.put("ident", (session == null) ? "-" : session.getId()); MDC.put("userid", req.getRemoteUser() != null ? req.getRemoteUser() : "-"); */ MDC.put("ID", Long.toString(logServerAccessId.incrementAndGet())); MDC.put("startTime", Long.toString(System.currentTimeMillis())); String query = req.getQueryString(); query = (query != null) ? "?" + query : ""; Formatter request = new Formatter(); request.format("\"%s %s%s %s\"", req.getMethod(), req.getRequestURI(), query, req.getProtocol()); MDC.put("request", request.toString()); return "Remote host: " + req.getRemoteHost() + " - Request: " + request.toString(); } /** * Return a log message appropriate for logging at the completion of * the contexts HTTP request. * <p/> * <p>Call this method at every exit point in each HttpServlet doXXX() method * (e.g., doGet(), doPut()) or Spring MVC Controller handle() method. * * @param resCode - the result code for this request. * @param resSizeInBytes - the number of bytes returned in this result, -1 if unknown. * @return closing log message */ public static String closingMessageForRequestContext(int resCode, long resSizeInBytes) { long duration = calculateElapsedTime(); return "Request Completed - " + resCode + " - " + resSizeInBytes + " - " + duration; } /** * Gather context information for the current non-request thread and * return a log message appropriate for logging. * <p/> * <p>The following context information is gathered: * <ul> * <li>"ID" - an identifier for the current thread; and</li> * <li>"startTime" - the system time in millis when this method is called.</li> * </ul> * <p/> * <p>Call this method only for non-request servlet activities, e.g., * during init() or destroy(). * @return starting log message */ public static String setupNonRequestContext() { // Setup context. MDC.put("ID", Long.toString(logServerAccessId.incrementAndGet())); MDC.put("startTime", Long.toString(System.currentTimeMillis())); return "Non-request thread opening."; } /** * Return a log message appropriate for logging at the close of * the non-request context. * * @return a log message appropriate for logging at the close of the non-request context. */ public static String closingMessageNonRequestContext() { long duration = calculateElapsedTime(); return "Non-request thread closing - " + duration; } private static long calculateElapsedTime() { long endTime = System.currentTimeMillis(); String startTimeS = MDC.get("startTime"); if (startTimeS == null) return -1; long startTime = Long.parseLong(startTimeS); return endTime - startTime; } public void add2map(String key, String value) { MDC.put(key, value); } }