| /* |
| * 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.nhttp; |
| |
| 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.synapse.transport.nhttp.util.AccessTimeUtil; |
| |
| 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.COMBINED_PATTERN; |
| |
| private static AccessLogger accessLogger; |
| |
| private static ConcurrentLinkedQueue<HttpRequest> requestQueue; |
| private static ConcurrentLinkedQueue<HttpResponse> responseQueue; |
| |
| private static final int LOG_FREQUENCY_IN_SECONDS = 30; |
| |
| /** |
| * 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<HttpRequest>(); |
| responseQueue = new ConcurrentLinkedQueue<HttpResponse>(); |
| logElements = createLogElements(); |
| logAccesses(); |
| } |
| |
| /** |
| * Adds the accesses to the queue. |
| * |
| * @param request - HttpRequest |
| */ |
| public void addAccessToQueue(HttpRequest request) { |
| requestQueue.add(request); |
| } |
| |
| /** |
| * Adds the accesses to the queue. |
| * |
| * @param response - HttpResponse |
| */ |
| public void addAccessToQueue(HttpResponse response) { |
| responseQueue.add(response); |
| } |
| |
| /** |
| * 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()) { |
| HttpRequest req = requestQueue.poll(); |
| log(req, null); |
| } |
| } |
| } |
| |
| private class LogResponses extends TimerTask { |
| public void run() { |
| while (!responseQueue.isEmpty()) { |
| HttpResponse 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 date = AccessTimeUtil.getDate(); |
| 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(); |
| } |
| |
| 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"); |
| } |
| |
| |
| /** |
| * 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 host = ""; |
| try { |
| host = getHostElement(request); |
| } catch (Exception e) { |
| // empty host |
| } |
| buf.append(host); |
| } |
| } |
| |
| /** |
| * 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 static class DateAndTimeElement implements AccessLogElement { |
| |
| public void addElement(StringBuilder buf, Date date, HttpRequest request, |
| HttpResponse response) { |
| String currentDate = ""; |
| if (request != null) { |
| currentDate = getHeaderValues(request, "Date"); |
| } else if (response != null) { |
| currentDate = getHeaderValues(response, "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 |
| try { |
| long 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 + "???"); |
| } |
| } |
| } |