/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.synapse.transport.http.access; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.Header; import org.apache.http.HttpMessage; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.params.HttpParams; import org.apache.synapse.transport.http.wrapper.HttpRequestWrapper; import org.apache.synapse.transport.http.wrapper.HttpResponseWrapper; import java.net.InetAddress; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentLinkedQueue; /** * The class to handle the HTTP Access Logs, patterns and the major functionality. * Major Code segment borrowed from Apache Tomcat's * org.apache.catalina.valves.AccessLogValve with thanks. */ public class Access { private static Log log = LogFactory.getLog(Access.class); /** * Array of AccessLogElement, they will be used to make log message. */ protected AccessLogElement[] logElements = null; protected String pattern = AccessConstants.getLogPattern(); private static AccessLogger accessLogger; private static ConcurrentLinkedQueue<HttpRequestWrapper> requestQueue; private static ConcurrentLinkedQueue<HttpResponseWrapper> responseQueue; private static final int LOG_FREQUENCY_IN_SECONDS = 30; private Date date; /** * Constructor of AccessLog. AccessHandler has a static object of Access. * * @param log - Log passed as a param. Default is Log of the same class. * @param accessLogger - AccessLogger Object */ public Access(final Log log, AccessLogger accessLogger) { super(); Access.log = log; Access.accessLogger = accessLogger; requestQueue = new ConcurrentLinkedQueue<HttpRequestWrapper>(); responseQueue = new ConcurrentLinkedQueue<HttpResponseWrapper>(); logElements = createLogElements(); logAccesses(); } /** * Adds the accesses to the queue. * * @param request - HttpRequest */ public void addAccessToQueue(HttpRequest request) { HttpRequestWrapper requestWrapper = new HttpRequestWrapper(); requestWrapper.setHttpRequest(request); requestWrapper.setDate(new Date(AccessTimeUtil.getDate().getTime())); requestQueue.add(requestWrapper); } /** * Adds the accesses to the queue. * * @param response - HttpResponse */ public void addAccessToQueue(HttpResponse response) { HttpResponseWrapper responseWrapper = new HttpResponseWrapper(); responseWrapper.setHttpResponse(response); responseWrapper.setDate(new Date(AccessTimeUtil.getDate().getTime())); responseQueue.add(responseWrapper); } /** * logs the request and response accesses. */ public void logAccesses() { TimerTask logRequests = new LogRequests(); TimerTask logResponses = new LogResponses(); Timer requestTimer = new Timer(); Timer responseTimer = new Timer(); // Retry in 30 seconds long retryIn = 1000 * LOG_FREQUENCY_IN_SECONDS; requestTimer.schedule(logRequests, 0, retryIn); responseTimer.schedule(logResponses, 0, retryIn); } private class LogRequests extends TimerTask { public void run() { while (!requestQueue.isEmpty()) { HttpRequestWrapper req = requestQueue.poll(); log(req, null); } } } private class LogResponses extends TimerTask { public void run() { while (!responseQueue.isEmpty()) { HttpResponseWrapper res = responseQueue.poll(); log(null, res); } } } /** * The log method that is called from the NHttpClient and Server connection classes. * * @param request - HttpRequest * @param response - HttpResponse */ public void log(HttpRequest request, HttpResponse response) { date = AccessTimeUtil.getDate(); //String commonLogFormatDate = AccessTimeUtil.getAccessDate(date); StringBuilder result = new StringBuilder(128); for (AccessLogElement logElement : logElements) { logElement.addElement(result, date, request, response); } String logString = result.toString(); log.debug(logString); //log to the console accessLogger.log(logString); //log to the file } /** * gets the header values from the given message, with the given name. * * @param message - The message, HttpRequest or HttpResponse * @param name - The header, which we need to get the value of. * @return - The header value. */ protected static String getHeaderValues(HttpMessage message, String name) { int length = 0; Header[] header = new Header[0]; StringBuffer headerValue = new StringBuffer(); try { header = message.getHeaders(name); length = header.length; } catch (Exception e) { // The header doesn't exist } if (length == 0) { return "-"; } else if (length == 1) { return header[0].getValue(); } else { headerValue.append(header[0].getValue()); for (int i = 1; i < length; i++) { headerValue.append(" - ").append(header[i].getValue()); } } return headerValue.toString(); } protected static String getParam(HttpMessage message, String paramName) { HttpParams params = message.getParams(); String param = (String) params.getParameter(paramName); if (param == null) { param = "-"; } return param; } public static String getHostElement(HttpMessage message) { return getHeaderValues(message, "Host"); //%h; } public static String getLogicalUserNameElement(HttpMessage message) { if (message != null) { //%l return "-"; } return ""; } public static String getUserNameElement(HttpMessage message) { return getHeaderValues(message, "From"); //%u } public static String getCookieElement(HttpMessage message) { return getHeaderValues(message, "Cookie"); // %c } public static String getRefererElement(HttpMessage message) { return getHeaderValues(message, "Referer"); //%{Referer}i; %f } public static String getUserAgentElement(HttpMessage message) { return getHeaderValues(message, "User-Agent"); //%{User-Agent} %a } public static String getAcceptElement(HttpMessage message) { return getHeaderValues(message, "Accept"); } public static String getAcceptLanguageElement(HttpMessage message) { return getHeaderValues(message, "Accept-Language"); } public static String getAcceptEncodingElement(HttpMessage message) { return getHeaderValues(message, "Accept-Encoding"); } public static String getAcceptCharSetElement(HttpMessage message) { return getHeaderValues(message, "Accept-Charset"); } public static String getConnectionElement(HttpMessage message) { return getHeaderValues(message, "Connection"); //Keep-Alive } public static String getContentTypeElement(HttpMessage message) { return getHeaderValues(message, "Content-Type"); } public static String getKeepAliveElement(HttpMessage message) { return getHeaderValues(message, "Keep-Alive"); } public static String getTransferEncodingElement(HttpMessage message) { return getHeaderValues(message, "Transfer-Encoding"); } public static String getContentEncodingElement(HttpMessage message) { return getHeaderValues(message, "Content-Encoding"); } public static String getVaryElement(HttpMessage message) { return getHeaderValues(message, "Vary"); } public static String getServerElement(HttpMessage message) { return getHeaderValues(message, "Server"); } public static String getRemoteAddr(HttpMessage message) { return getParam(message, "http.remote.addr"); } /** * AccessLogElement writes the partial message into the buffer. */ protected interface AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response); } /** * write local IP address - %A */ protected static class LocalAddrElement implements AccessLogElement { private static final String LOCAL_ADDR_VALUE; static { String init; try { init = InetAddress.getLocalHost().getHostAddress(); } catch (Throwable e) { AccessTimeUtil.handleThrowable(e); init = "127.0.0.1"; } LOCAL_ADDR_VALUE = init; } public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(LOCAL_ADDR_VALUE); } } /** * write remote host name - %h */ protected static class HostElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { String remoteHost = "-"; try { if (request != null) { remoteHost = getRemoteAddr(request); } else if (response != null) { remoteHost = getRemoteAddr(response); } } catch (Exception e) { // empty host } buf.append(remoteHost); } } /** * write remote logical username from identd (always returns '-') - %l */ protected static class LogicalUserNameElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { String logicalUserName = ""; try { logicalUserName = getLogicalUserNameElement(request); } catch (Exception e) { // empty logicalUserName } buf.append(logicalUserName); } } /** * write remote user that was authenticated (if any), else '-' - %u */ protected static class UserElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { String userElement = ""; try { userElement = getUserNameElement(request); } catch (Exception e) { // empty UserName } buf.append(userElement); } } /** * write date and time, in Common Log Format - %t */ protected class DateAndTimeElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { String currentDate = AccessTimeUtil.getAccessDate(date); buf.append(currentDate); } } /** * write first line of the request (method and request URI) - %r */ protected static class RequestElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { if (request != null) { String requestLine = request.getRequestLine().toString(); buf.append(requestLine); } else { buf.append("- - "); } } } /** * write HTTP status code of the response - %s */ protected static class HttpStatusCodeElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { if (response != null) { int statusCode = response.getStatusLine().getStatusCode(); buf.append(statusCode); //getStatus } else { buf.append('-'); } } } /** * write bytes sent, excluding HTTP headers - %b, %B */ protected static class ByteSentElement implements AccessLogElement { private boolean conversion; /** * if conversion is true, write '-' instead of 0 - %b * * @param conversion - To be conversed. */ public ByteSentElement(boolean conversion) { this.conversion = conversion; } public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { // Don't need to flush since trigger for log message is after the // response has been committed long length = -1; try { if (request != null) { String sLength = getHeaderValues(request, "Content-Length"); if(null != sLength && !"".equals(sLength)) { length = Long.valueOf(sLength); } } else if (response != null) { length = response.getEntity().getContentLength(); //getBytesWritten(false); } if (length <= 0 && conversion) { buf.append('-'); //%b } else { buf.append(length); } } catch (Exception e) { buf.append('-'); //No entity found. } } } /** * write request method (GET, POST, etc.) - %m */ protected static class MethodElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { if (request != null) { buf.append(request.getRequestLine().getMethod()); } } } /** * write requested URL path - %U */ protected static class RequestURIElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { if (request != null) { buf.append(request.getRequestLine().getUri()); } else { buf.append('-'); } } } /** * write local server name - %v */ protected static class LocalServerNameElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getHeaderValues(request, "server")); } } /** * write any string */ protected static class StringElement implements AccessLogElement { private String str; public StringElement(String str) { this.str = str; } public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(str); } } /** * write incoming headers - %{xxx}i */ protected static class HeaderElement implements AccessLogElement { private String header; public HeaderElement(String header) { this.header = header; } public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { try { String value = getHeaderValues(request, header); if (value == null) { buf.append('-'); } else { buf.append(value); } } catch (Exception e) { buf.append('-'); //Header is null } } } /** * write a specific cookie - %{xxx}c */ protected static class CookieElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getCookieElement(request)); } } /** * write the referer - %f */ protected static class RefererElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getRefererElement(request)); } } /** * write the user agent - %a */ protected static class UserAgentElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getUserAgentElement(request)); } } /** * write the Accept Element - %C */ protected static class AcceptElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getAcceptElement(request)); } } /** * write the Accept Language Element - %L */ protected static class AcceptLanguageElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getAcceptLanguageElement(request)); } } /** * write the Accept Encoding Element - %e */ protected static class AcceptEncodingElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getAcceptEncodingElement(request)); } } /** * write the Accept Character Set Element - %S */ protected static class AcceptCharSetElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getAcceptCharSetElement(request)); } } /** * write the Connection Element - %x */ protected static class ConnectionElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getConnectionElement(request)); } } /** * write the Content Type Element - %T */ protected static class ContentTypeElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getContentTypeElement(request)); } } /** * write the Keep Alive Element - %k */ protected static class KeepAliveElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getKeepAliveElement(request)); } } /** * write the Transfer Encoding Element - %E */ protected static class TransferEncodingElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getTransferEncodingElement(request)); } } /** * write the Content Encoding Element - %n */ protected static class ContentEncodingElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getContentEncodingElement(request)); } } /** * write the Vary Element - %V */ protected static class VaryElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getVaryElement(request)); } } /** * write the Server Element - %Z */ protected static class ServerElement implements AccessLogElement { public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { buf.append(getServerElement(request)); } } /** * write a specific response header - %{xxx}o */ protected static class ResponseHeaderElement implements AccessLogElement { private String header; public ResponseHeaderElement(String header) { this.header = header; } public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { if (null != response) { buf.append(getHeaderValues(response, header)); } buf.append("-"); } } /** * write an attribute in the ServletRequest - %{xxx}r %R */ protected static class RequestAttributeElement implements AccessLogElement { public RequestAttributeElement() { } public void addElement(StringBuilder buf, Date date, HttpRequest request, HttpResponse response) { Object value; if (request != null) { value = request.getLastHeader(buf.toString()); //gets the attribute header } else { value = "??"; } if (value != null) { if (value instanceof String) { buf.append((String) value); } else { buf.append(value.toString()); } } else { buf.append('-'); } } } /** * parse pattern string and create the array of AccessLogElement * * @return Array of AccessLogElement */ protected AccessLogElement[] createLogElements() { List<AccessLogElement> list = new ArrayList<AccessLogElement>(); boolean replace = false; StringBuilder buf = new StringBuilder(); for (int i = 0; i < pattern.length(); i++) { char ch = pattern.charAt(i); if (replace) { /* * For code that processes {, the behavior will be ... if I do * not encounter a closing } - then I ignore the { */ if ('{' == ch) { StringBuilder name = new StringBuilder(); int j = i + 1; for (; j < pattern.length() && '}' != pattern.charAt(j); j++) { name.append(pattern.charAt(j)); } if (j + 1 < pattern.length()) { /* the +1 was to account for } which we increment now */ j++; list.add(createAccessLogElement(name.toString(), pattern.charAt(j))); i = j; /* Since we walked more than one character */ } else { // D'oh - end of string - pretend we never did this // and do processing the "old way" list.add(createAccessLogElement(ch)); } } else { list.add(createAccessLogElement(ch)); } replace = false; } else if (ch == '%') { replace = true; list.add(new StringElement(buf.toString())); buf = new StringBuilder(); } else { buf.append(ch); } } if (buf.length() > 0) { list.add(new StringElement(buf.toString())); } return list.toArray(new AccessLogElement[list.size()]); } /** * create an AccessLogElement implementation which needs header string * * @param header - header of the request/response * @param pattern - pattern character given for the input element * @return AccessLogElement - accessLogElement */ private AccessLogElement createAccessLogElement(String header, char pattern) { switch (pattern) { case 'i': return new HeaderElement(header); //%{xxx}i case 'o': return new ResponseHeaderElement(header); case 'R': return new RequestAttributeElement(); default: return new StringElement("???"); } } /** * create an AccessLogElement implementation * * @param pattern - pattern character given for the input element * @return AccessLogElement acceessLogElement */ private AccessLogElement createAccessLogElement(char pattern) { switch (pattern) { case 'A': return new LocalAddrElement(); case 'a': return new UserAgentElement(); case 'b': return new ByteSentElement(true); //%b case 'B': return new ByteSentElement(false); case 'c': return new CookieElement(); // %c case 'C': return new AcceptElement(); case 'e': return new AcceptEncodingElement(); case 'E': return new TransferEncodingElement(); case 'f': return new RefererElement(); case 'h': return new HostElement(); //%h case 'k': return new KeepAliveElement(); case 'l': return new LogicalUserNameElement(); //%l case 'L': return new AcceptLanguageElement(); case 'm': return new MethodElement(); case 'n': return new ContentEncodingElement(); case 'r': return new RequestElement(); //%r case 'S': return new AcceptCharSetElement(); case 's': return new HttpStatusCodeElement(); // %s case 'T': return new ContentTypeElement(); case 't': return new DateAndTimeElement(); //%t case 'u': return new UserElement(); //%u case 'U': return new RequestURIElement(); case 'V': return new VaryElement(); case 'v': return new LocalServerNameElement(); case 'x': return new ConnectionElement(); case 'Z': return new ServerElement(); default: return new StringElement("???" + pattern + "???"); } } /** * The log method that is called from the NHttpClient and Server connection classes. * This method is used to log the request/response with the actual date of operation. * * @param request - HttpRequestWrapper * @param response - HttpResponseWrapper */ public void log(HttpRequestWrapper request, HttpResponseWrapper response) { Date actualDateOfOperation = null; if(request != null) { actualDateOfOperation = request.getDate(); } else if (response != null){ actualDateOfOperation = response.getDate(); } StringBuilder result = new StringBuilder(128); for (AccessLogElement logElement : logElements) { if (request != null) { logElement.addElement(result, actualDateOfOperation, request.getHttpRequest(), null); } else if (response != null) { logElement.addElement(result, actualDateOfOperation, null, response.getHttpResponse()); } } String logString = result.toString(); log.debug(logString); //log to the console accessLogger.log(logString); //log to the file } }