[Yanel-dev] Access logging

Cedric Staub cedric.staub at wyona.com
Thu Sep 2 11:56:51 CEST 2010


Hello

Here is a first version of my patch.

Resources can now use appendFieldToLog to append a new field to the log
entry generated by the current request. I noticed that the contact-form
resourced already does something like this, but by simply generating a
second log entry, which is not very nice. So I modified the contact form
to use the the new functions.

Example: In a resource, you can call

  appendFieldToLog("email", resource.getParameter("email"))

which will result in a log entry with e.g.

  email:cedric.staub%40wyona.com

in the access log file. 

Feedback is welcome. By the way, I also improved the performance of the
access log by replacing string appends with a StringBuilder and I
inserted some Javadocs along the way as well.

Cheers
Cedric
-------------- next part --------------
Index: src/webapp/src/java/org/wyona/yanel/servlet/YanelServlet.java
===================================================================
--- src/webapp/src/java/org/wyona/yanel/servlet/YanelServlet.java	(revision 52939)
+++ src/webapp/src/java/org/wyona/yanel/servlet/YanelServlet.java	(working copy)
@@ -1121,7 +1121,7 @@
                         - Or authentication was successful and web authenticator sends a redirect
                 */
                 if(logAccessEnabled) {
-                    doLogAccess(request, response);
+                    doLogAccess(request, response, null);
                 }
                 return response;
             } else {
@@ -1872,7 +1872,7 @@
                 if (view.getMimeType() != null) {
                     // TODO: Add more mime types or rather make it configurable
                     if (view.getMimeType().indexOf("html") > 0 || view.getMimeType().indexOf("pdf") > 0 || view.getMimeType().indexOf("video") >= 0) {
-                        doLogAccess(request, response);
+                        doLogAccess(request, response, res);
                     }
                 }
             }
@@ -1908,7 +1908,7 @@
         if(logAccessEnabled) {
             if (mimeType != null) {
                 if (mimeType.indexOf("html") > 0 || mimeType.indexOf("pdf") > 0) { // INFO: Only HTML pages and PDFs etc. should be logged, but no images, CSS, etc. Check the mime-type instead the suffix or use JavaScript or Pixel
-                    doLogAccess(request, response);
+                    doLogAccess(request, response, res);
                 }
             }
         }
@@ -2358,7 +2358,7 @@
     /**
      * Log browser history of each user
      */
-    private void doLogAccess(HttpServletRequest request, HttpServletResponse response) {
+    private void doLogAccess(HttpServletRequest request, HttpServletResponse response, Resource res) {
         Cookie cookie = AccessLog.getYanelAnalyticsCookie(request, response);
         // TBD: What about a cluster, performance/scalability? See for example http://www.oreillynet.com/cs/user/view/cs_msg/17399 (also see Tomcat conf/server.xml <Valve className="AccessLogValve" and className="FastCommonAccessLogValve")
         // See apache-tomcat-5.5.20/logs/localhost_access_log.2009-11-07.txt
@@ -2388,14 +2388,16 @@
                  - Log analysis (no special tracking required)
 */
 
-                String requestURL = request.getRequestURL().toString();
-                logAccess.info(requestURL + " r:" + realm.getID() + " c:" + cookie.getValue() + " u:" + identity.getUsername() + " ref:" + request.getHeader("referer") + " ua:" + request.getHeader("User-Agent")); // INFO: See a list of user agents at http://www.user-agents.org/
+                java.util.Map<String, String> extra = null;
+                if(res != null) extra = res.getLogFields(); 
+                if(extra == null) extra = new HashMap<String, String>();
+                extra.put("u", identity.getUsername());
+                logAccess.info(AccessLog.getLogMessage(request, response, realm.getID(), extra));
             } else {
                 // INFO: Log access of anonymous user
-                String requestURL = request.getRequestURL().toString();
-                // TODO: Also log referer as entry point
-                //logAccess.info(requestURL + " r:" + realm.getID() + " c:" + cookie.getValue() + " ref:" + request.getHeader("referer") + " ua:" + request.getHeader("User-Agent"));
-                logAccess.info(AccessLog.getLogMessage(request, response, realm.getID()));
+                java.util.Map<String, String> extra = null;
+                if(res != null) extra = res.getLogFields(); 
+                logAccess.info(AccessLog.getLogMessage(request, response, realm.getID(), extra));
             }
             //log.warn("DEBUG: Referer: " + request.getHeader(HTTP_REFERRER));
         } catch(Exception e) { // Catch all exceptions, because we do not want to throw exceptions because of logging browser history
Index: src/core/java/org/wyona/yanel/core/Resource.java
===================================================================
--- src/core/java/org/wyona/yanel/core/Resource.java	(revision 52939)
+++ src/core/java/org/wyona/yanel/core/Resource.java	(working copy)
@@ -27,7 +27,7 @@
 import org.wyona.yanel.core.map.Realm;
 
 /**
- *
+ * The mother of all resources.
  */
 public abstract class Resource {
 
@@ -44,78 +44,92 @@
     protected HttpServletResponse response;
     private Environment environment;
     protected Map parameters;
+    private Map<String, String> logFields;
 
     public static final String DEFAULT_LANGUAGE = "en";
 
     /**
-     *
+     * Constructor.
      */
     public Resource() {
         rtd = null;
         this.parameters = new HashMap();
+        this.logFields = new HashMap<String, String>();
     }
 
     /**
-     *
+     * Set resource type definition.
      */
     public void setRTD(ResourceTypeDefinition rtd) {
         this.rtd = rtd;
     }
 
     /**
-     *
+     * Get resource type definition.
      */
     public ResourceTypeDefinition getRTD() {
         return rtd;
     }
 
     /**
-     *
+     * Set Yanel.
      */
     public void setYanel(Yanel yanel) {
         this.yanel = yanel;
     }
 
     /**
-     *
+     * Get Yanel.
      */
     public Yanel getYanel() {
         return yanel;
     }
 
     /**
-     *
+     * Get universal resource type name.
      */
     public String getResourceTypeUniversalName() {
         return rtd.getResourceTypeUniversalName();
     }
 
     /**
-     *
+     * Get local resource type name.
      */
     public String getResourceTypeLocalName() {
         return rtd.getResourceTypeLocalName();
     }
 
     /**
-     *
+     * Get resource type namespace.
      */
     public String getResourceTypeNamespace() {
         return rtd.getResourceTypeNamespace();
     }
     
+    /**
+     * Get path.
+     */
     public String getPath() {
         return path;
     }
 
+    /**
+     * Set path.
+     */
     public void setPath(String path) {
         this.path = path;
     }
 
+    /**
+     * Get realm.
+     */
     public Realm getRealm() {
         return realm;
     }
 
+    /**
+     * Set realm.
+     */
     public void setRealm(Realm realm) {
         this.realm = realm;
     }
@@ -313,13 +327,42 @@
         return getRTI().getProperties(name);
     }
 
+    /**
+     * Get environment.
+     */
     public Environment getEnvironment() {
         return environment;
     }
 
+    /**
+     * Set environment.
+     */
     public void setEnvironment(Environment environment) {
         this.environment = environment;
         this.request = environment.getRequest();
         this.response = environment.getResponse();
     }
+
+    /**
+     * Append a field to the access log.
+     * For example, if you call this function with the
+     * parameters "source" and "xml", then "source:xml"
+     * will be appended to the access log line of the
+     * current request.
+     *
+     * @param key The name of the field.
+     * @param value The value of the field.
+     */
+    public void appendFieldToLog(String key, String value) {
+        this.logFields.put(key, value);
+    }
+
+    /**
+     * Get additional log fields.
+     * @return Additional a set of key:value pairs that 
+     * should be appended to the access log.
+     */
+    public Map<String, String> getLogFields() {
+        return this.logFields;
+    }
 }
Index: src/webapp/src/java/org/wyona/yanel/servlet/AccessLog.java
===================================================================
--- src/webapp/src/java/org/wyona/yanel/servlet/AccessLog.java	(revision 52939)
+++ src/webapp/src/java/org/wyona/yanel/servlet/AccessLog.java	(working copy)
@@ -4,8 +4,10 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import java.util.Date;
+import java.util.Map;
+import java.util.UUID;
 import java.net.URLEncoder;
+import java.lang.StringBuilder;
 import java.io.UnsupportedEncodingException;
 
 import org.apache.log4j.Logger;
@@ -34,16 +36,23 @@
      * @param cookieValue Value/UUID of unique persistent cookie
      * @param referer Referer
      * @param userAgent User agent, e.g. Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.0.19) Gecko/2010031218 Firefox/3.0.19
+     * @param extra Additional fields to be appended.
      */
-    public static String getLogMessage(String requestURL, String realmID, String cookieValue, String referer, String userAgent) {
-        String result =
-            encodeLogField("url", requestURL) +
-            encodeLogField("r", realmID) +
-            encodeLogField("c", cookieValue) +
-            encodeLogField("ref", referer) +
-            encodeLogField("ua", userAgent);
+    public static String getLogMessage(String requestURL, String realmID, String cookieValue, String referer, String userAgent, Map<String, String> extra) {
+        StringBuilder result = new StringBuilder();
+        result.append(encodeLogField("url", requestURL));
+        result.append(encodeLogField("r", realmID));
+        result.append(encodeLogField("c", cookieValue));
+        result.append(encodeLogField("ref", referer));
+        result.append(encodeLogField("ua", userAgent));
 
-        return result;
+        if(extra != null) {
+            for(Map.Entry<String, String> e : extra.entrySet()) {
+                result.append(encodeLogField(e.getKey(), e.getValue()));
+            }
+        }
+
+        return result.toString();
     }
 
     /**
@@ -68,22 +77,29 @@
 
     /**
      * Get log message
+     * @param request The request.
+     * @param response The response.
+     * @param realmID The id of the current realm.
+     * @param extra Additional fields to be appended to the log.
      */
-    public static String getLogMessage(HttpServletRequest request, HttpServletResponse response, String realmID) {
+    public static String getLogMessage(HttpServletRequest request, HttpServletResponse response, String realmID, Map<String, String> extra) {
         Cookie cookie = getYanelAnalyticsCookie(request, response);
-        return getLogMessage(request.getRequestURL().toString(), realmID, cookie.getValue(), request.getHeader("referer"), request.getHeader("User-Agent"));
+        return getLogMessage(request.getRequestURL().toString(), realmID, cookie.getValue(), request.getHeader("referer"), request.getHeader("User-Agent"), extra);
     }
 
     /**
      * Get log message
+     * @param request The request.
+     * @param realmID The id of the current realm.
+     * @param extra Additional fields to be appended to the log.
      */
-    public static String getLogMessage(HttpServletRequest request, String realmID) {
+    public static String getLogMessage(HttpServletRequest request, String realmID, Map<String, String> extra) {
         Cookie cookie = getYanelAnalyticsCookie(request);
         String cookieValue = null;
         if (cookie != null) {
             cookieValue = cookie.getValue();
         }
-        return getLogMessage(request.getRequestURL().toString(), realmID, cookieValue, request.getHeader("referer"), request.getHeader("User-Agent"));
+        return getLogMessage(request.getRequestURL().toString(), realmID, cookieValue, request.getHeader("referer"), request.getHeader("User-Agent"), extra);
     }
 
     /**
@@ -112,7 +128,8 @@
         Cookie c = getYanelAnalyticsCookie(request);
         if (c != null) return c;
 
-        Cookie analyticsCookie = new Cookie(ANALYTICS_COOKIE_NAME, "YA-" + new Date().getTime()); // TODO: getTime() is not unique!
+        String value = "YA-" + UUID.randomUUID();
+        Cookie analyticsCookie = new Cookie(ANALYTICS_COOKIE_NAME, value);
         analyticsCookie.setMaxAge(31536000); // 1 year
         //analyticsCookie.setMaxAge(86400); // 1 day
         analyticsCookie.setPath(request.getContextPath());
Index: src/contributions/resources/contact-form/src/java/org/wyona/yanel/impl/resources/contactform/ContactResource.java
===================================================================
--- src/contributions/resources/contact-form/src/java/org/wyona/yanel/impl/resources/contactform/ContactResource.java	(revision 52939)
+++ src/contributions/resources/contact-form/src/java/org/wyona/yanel/impl/resources/contactform/ContactResource.java	(working copy)
@@ -117,9 +117,7 @@
                     throw new Exception("there is no spamblock implemented in the form.");
                 }
                 if (request.getParameter("spamblock_hidden").equals("TRyAg41n") && request.getParameter("spamblock_input").equals("8989890")) {    
-                    logAccess.info(
-                        org.wyona.yanel.servlet.AccessLog.getLogMessage(request, getRealm().getID()) + 
-                        org.wyona.yanel.servlet.AccessLog.encodeLogField("e-mail", request.getParameter("email")));
+                    appendFieldToLog("email", request.getParameter("email"));
                     javax.servlet.http.Cookie cookie = org.wyona.yanel.servlet.AccessLog.getYanelAnalyticsCookie(request);
                     String cookieValue = null;
                     if (cookie != null) {


More information about the Yanel-development mailing list