You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 10:24:16 UTC

[sling-org-apache-sling-tracer] 16/30: SLING-5504 - Reduce memory footprint of stored recording data

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.tracer-1.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-tracer.git

commit 9d3d4dcb79db89b18766a6f3283f94e32d0fa6e0
Author: Chetan Mehrotra <ch...@apache.org>
AuthorDate: Wed Feb 10 06:52:58 2016 +0000

    SLING-5504 - Reduce memory footprint of stored recording data
    
    -- Make cache size by memory configurable. Defaults to 50MB and expiry in 15 mins
    -- Change the recording logic such that recording is added to cache at end. As Guava calculates the entry size at time of put and recording size is only calculated at end. So now recorder would be called at end of request processing
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/tracer@1729535 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            |  2 +-
 .../sling/tracer/internal/JSONRecording.java       |  5 +-
 .../apache/sling/tracer/internal/LogTracer.java    | 33 +++++++++-
 .../apache/sling/tracer/internal/Recording.java    | 10 ---
 .../sling/tracer/internal/TraceLogRecorder.java    |  7 ++
 .../sling/tracer/internal/TracerLogServlet.java    | 76 +++++++++++++++++-----
 .../sling/tracer/internal/LogTracerTest.java       | 25 +++++++
 .../tracer/internal/TracerLogServletTest.java      |  5 +-
 8 files changed, 131 insertions(+), 32 deletions(-)

diff --git a/pom.xml b/pom.xml
index 1a0bf46..185cfb2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,7 +74,7 @@
       <!-- the used logback version is only compatible with SLF4J 1.6 -->
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
-      <version>1.6.0</version>
+      <version>1.7.5</version>
       <scope>provided</scope>
     </dependency>
     <dependency>
diff --git a/src/main/java/org/apache/sling/tracer/internal/JSONRecording.java b/src/main/java/org/apache/sling/tracer/internal/JSONRecording.java
index 507d130..35f4dfe 100644
--- a/src/main/java/org/apache/sling/tracer/internal/JSONRecording.java
+++ b/src/main/java/org/apache/sling/tracer/internal/JSONRecording.java
@@ -95,6 +95,10 @@ class JSONRecording implements Recording {
         return uri;
     }
 
+    public String getRequestId() {
+        return requestId;
+    }
+
     //~---------------------------------------< Recording >
 
     @Override
@@ -112,7 +116,6 @@ class JSONRecording implements Recording {
         this.tracker = tracker;
     }
 
-    @Override
     public void done() {
         try {
             if (json == null) {
diff --git a/src/main/java/org/apache/sling/tracer/internal/LogTracer.java b/src/main/java/org/apache/sling/tracer/internal/LogTracer.java
index 94c220c..2f6940b 100644
--- a/src/main/java/org/apache/sling/tracer/internal/LogTracer.java
+++ b/src/main/java/org/apache/sling/tracer/internal/LogTracer.java
@@ -117,6 +117,27 @@ public class LogTracer {
     )
     private static final String PROP_TRACER_SERVLET_ENABLED = "servletEnabled";
 
+    static final int PROP_TRACER_SERVLET_CACHE_SIZE_DEFAULT = 50;
+    @Property(label = "Recording Cache Size",
+            description = "Recording cache size in MB which would be used to temporary cache the recording data",
+            intValue = PROP_TRACER_SERVLET_CACHE_SIZE_DEFAULT
+    )
+    private static final String PROP_TRACER_SERVLET_CACHE_SIZE = "recordingCacheSizeInMB";
+
+    static final long PROP_TRACER_SERVLET_CACHE_DURATION_DEFAULT = 60 * 15;
+    @Property(label = "Recording Cache Duration",
+            description = "Time in seconds upto which the recording data would be held in memory before expiry",
+            longValue = PROP_TRACER_SERVLET_CACHE_DURATION_DEFAULT
+    )
+    private static final String PROP_TRACER_SERVLET_CACHE_DURATION = "recordingCacheDurationInSecs";
+
+    static final boolean PROP_TRACER_SERVLET_COMPRESS_DEFAULT = true;
+    @Property(label = "Compress Recording",
+            description = "Enable compression for recoding held in memory",
+            boolValue = PROP_TRACER_SERVLET_COMPRESS_DEFAULT
+    )
+    private static final String PROP_TRACER_SERVLET_COMPRESS = "recordingCompressionEnabled";
+
     private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(LogTracer.class);
 
     private final Map<String, TracerSet> tracers = new HashMap<String, TracerSet>();
@@ -150,8 +171,16 @@ public class LogTracer {
                     PROP_TRACER_SERVLET_ENABLED_DEFAULT);
 
             if (servletEnabled) {
-                this.logServlet = new TracerLogServlet(context);
+                int cacheSize = PropertiesUtil.toInteger(config.get(PROP_TRACER_SERVLET_CACHE_SIZE),
+                        PROP_TRACER_SERVLET_CACHE_SIZE_DEFAULT);
+                long cacheDuration = PropertiesUtil.toLong(config.get(PROP_TRACER_SERVLET_CACHE_DURATION),
+                        PROP_TRACER_SERVLET_CACHE_DURATION_DEFAULT);
+                boolean compressionEnabled = PropertiesUtil.toBoolean(config.get(PROP_TRACER_SERVLET_COMPRESS),
+                        PROP_TRACER_SERVLET_COMPRESS_DEFAULT);
+                this.logServlet = new TracerLogServlet(context, cacheSize, cacheDuration, compressionEnabled);
                 recorder = logServlet;
+                LOG.info("Tracer recoding enabled with cacheSize {} MB, expiry {} secs, compression {}",
+                        cacheSize, cacheDuration, compressionEnabled);
             }
             LOG.info("Log tracer enabled. Required filters registered. Tracer servlet enabled {}", servletEnabled);
         }
@@ -318,7 +347,7 @@ public class LogTracer {
                 if (tracerContext != null) {
                     disableCollector();
                 }
-                recording.done();
+                recorder.endRecording(recording);
             }
         }
 
diff --git a/src/main/java/org/apache/sling/tracer/internal/Recording.java b/src/main/java/org/apache/sling/tracer/internal/Recording.java
index d915474..9e0f88e 100644
--- a/src/main/java/org/apache/sling/tracer/internal/Recording.java
+++ b/src/main/java/org/apache/sling/tracer/internal/Recording.java
@@ -34,11 +34,6 @@ interface Recording {
         public void registerTracker(RequestProgressTracker tracker) {
 
         }
-
-        @Override
-        public void done() {
-
-        }
     };
 
     void log(Level level, String logger, FormattingTuple tuple);
@@ -49,9 +44,4 @@ interface Recording {
      * @param tracker from current request
      */
     void registerTracker(RequestProgressTracker tracker);
-
-    /**
-     * Invoked at the end of request processing
-     */
-    void done();
 }
diff --git a/src/main/java/org/apache/sling/tracer/internal/TraceLogRecorder.java b/src/main/java/org/apache/sling/tracer/internal/TraceLogRecorder.java
index 11e347a..d00e617 100644
--- a/src/main/java/org/apache/sling/tracer/internal/TraceLogRecorder.java
+++ b/src/main/java/org/apache/sling/tracer/internal/TraceLogRecorder.java
@@ -33,9 +33,16 @@ interface TraceLogRecorder {
         public Recording getRecordingForRequest(HttpServletRequest request) {
             return Recording.NOOP;
         }
+
+        @Override
+        public void endRecording(Recording recording) {
+
+        }
     };
 
     Recording startRecording(HttpServletRequest request, HttpServletResponse response);
 
     Recording getRecordingForRequest(HttpServletRequest request);
+
+    void endRecording(Recording recording);
 }
diff --git a/src/main/java/org/apache/sling/tracer/internal/TracerLogServlet.java b/src/main/java/org/apache/sling/tracer/internal/TracerLogServlet.java
index fa31a30..fc126d0 100644
--- a/src/main/java/org/apache/sling/tracer/internal/TracerLogServlet.java
+++ b/src/main/java/org/apache/sling/tracer/internal/TracerLogServlet.java
@@ -25,19 +25,22 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
+import javax.annotation.Nonnull;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.Weigher;
+import org.apache.commons.io.FileUtils;
 import org.apache.felix.webconsole.SimpleWebConsolePlugin;
 import org.apache.sling.commons.json.JSONException;
 import org.apache.sling.commons.json.io.JSONWriter;
 import org.osgi.framework.BundleContext;
 
 class TracerLogServlet extends SimpleWebConsolePlugin implements TraceLogRecorder {
-    static final String ATTR_REQUEST_ID = TracerLogServlet.class.getName();
+    static final String ATTR_RECORDING = TracerLogServlet.class.getName();
 
     public static final String CLEAR = "clear";
 
@@ -53,19 +56,51 @@ class TracerLogServlet extends SimpleWebConsolePlugin implements TraceLogRecorde
 
     private final Cache<String, JSONRecording> cache;
 
-    private boolean compressRecording = true;
+    private final boolean compressRecording;
 
-    public TracerLogServlet(BundleContext context) {
+    private final int cacheSizeInMB;
+
+    private final long cacheDurationInSecs;
+
+    public TracerLogServlet(BundleContext context){
+        this(context,
+                LogTracer.PROP_TRACER_SERVLET_CACHE_SIZE_DEFAULT,
+                LogTracer.PROP_TRACER_SERVLET_CACHE_DURATION_DEFAULT,
+                LogTracer.PROP_TRACER_SERVLET_COMPRESS_DEFAULT
+        );
+    }
+
+    public TracerLogServlet(BundleContext context, int cacheSizeInMB, long cacheDurationInSecs, boolean compressionEnabled) {
         super(LABEL, "Sling Tracer", "Sling", null);
-        //TODO Make things configurable
+        this.compressRecording = compressionEnabled;
+        this.cacheDurationInSecs = cacheDurationInSecs;
+        this.cacheSizeInMB = cacheSizeInMB;
         this.cache = CacheBuilder.newBuilder()
-                .maximumSize(100)
-                .expireAfterAccess(10, TimeUnit.MINUTES)
+                .maximumWeight(cacheSizeInMB * FileUtils.ONE_MB)
+                .weigher(new Weigher<String, JSONRecording>() {
+                    @Override
+                    public int weigh(@Nonnull  String key, @Nonnull JSONRecording value) {
+                        return (int)value.size();
+                    }
+                })
+                .expireAfterAccess(cacheDurationInSecs, TimeUnit.SECONDS)
                 .recordStats()
                 .build();
         register(context);
     }
 
+    boolean isCompressRecording() {
+        return compressRecording;
+    }
+
+    int getCacheSizeInMB() {
+        return cacheSizeInMB;
+    }
+
+    long getCacheDurationInSecs() {
+        return cacheDurationInSecs;
+    }
+
     //~-----------------------------------------------< WebConsole Plugin >
 
     @Override
@@ -119,8 +154,9 @@ class TracerLogServlet extends SimpleWebConsolePlugin implements TraceLogRecorde
     }
 
     private void renderStatus(PrintWriter pw) {
-        pw.printf("<p class='statline'>Log Tracer Recordings: %d recordings, %s memory</p>%n", cache.size(),
-                memorySize());
+        pw.printf("<p class='statline'>Log Tracer Recordings: %d recordings, %s memory " +
+                "(Max %dMB, Expired in %d secs)</p>%n", cache.size(),
+                memorySize(), cacheSizeInMB, cacheDurationInSecs);
 
         pw.println("<div class='ui-widget-header ui-corner-top buttonGroup'>");
         pw.println("<span style='float: left; margin-left: 1em'>Tracer Recordings</span>");
@@ -167,7 +203,7 @@ class TracerLogServlet extends SimpleWebConsolePlugin implements TraceLogRecorde
             return Recording.NOOP;
         }
 
-        if (request.getAttribute(ATTR_REQUEST_ID) != null){
+        if (request.getAttribute(ATTR_RECORDING) != null){
             //Already processed
             return getRecordingForRequest(request);
         }
@@ -175,8 +211,6 @@ class TracerLogServlet extends SimpleWebConsolePlugin implements TraceLogRecorde
         String requestId = generateRequestId();
         JSONRecording recording = record(requestId, request);
 
-        request.setAttribute(ATTR_REQUEST_ID, requestId);
-
         response.setHeader(HEADER_TRACER_REQUEST_ID, requestId);
         response.setHeader(HEADER_TRACER_PROTOCOL_VERSION, String.valueOf(TRACER_PROTOCOL_VERSION));
 
@@ -185,11 +219,20 @@ class TracerLogServlet extends SimpleWebConsolePlugin implements TraceLogRecorde
 
     @Override
     public Recording getRecordingForRequest(HttpServletRequest request) {
-        String requestId = (String) request.getAttribute(ATTR_REQUEST_ID);
-        if (requestId != null){
-            return getRecording(requestId);
+        Recording recording = (Recording) request.getAttribute(ATTR_RECORDING);
+        if (recording == null){
+            recording = Recording.NOOP;
+        }
+        return recording;
+    }
+
+    @Override
+    public void endRecording(Recording recording) {
+        if (recording instanceof JSONRecording) {
+            JSONRecording r = (JSONRecording) recording;
+            r.done();
+            cache.put(r.getRequestId(), r);
         }
-        return Recording.NOOP;
     }
 
     Recording getRecording(String requestId) {
@@ -199,7 +242,7 @@ class TracerLogServlet extends SimpleWebConsolePlugin implements TraceLogRecorde
 
     private JSONRecording record(String requestId, HttpServletRequest request) {
         JSONRecording data = new JSONRecording(requestId, request, compressRecording);
-        cache.put(requestId, data);
+        request.setAttribute(ATTR_RECORDING, data);
         return data;
     }
 
@@ -211,6 +254,7 @@ class TracerLogServlet extends SimpleWebConsolePlugin implements TraceLogRecorde
      * Returns a human-readable version of the file size, where the input represents
      * a specific number of bytes. Based on http://stackoverflow.com/a/3758880/1035417
      */
+    @SuppressWarnings("Duplicates")
     private static String humanReadableByteCount(long bytes) {
         if (bytes < 0) {
             return "0";
diff --git a/src/test/java/org/apache/sling/tracer/internal/LogTracerTest.java b/src/test/java/org/apache/sling/tracer/internal/LogTracerTest.java
index 416b8e2..296c168 100644
--- a/src/test/java/org/apache/sling/tracer/internal/LogTracerTest.java
+++ b/src/test/java/org/apache/sling/tracer/internal/LogTracerTest.java
@@ -110,12 +110,37 @@ public class LogTracerTest {
         assertEquals(2, context.getServices(Filter.class, null).length);
         assertNotNull(context.getService(Servlet.class));
 
+        TracerLogServlet logServlet = (TracerLogServlet) context.getService(Servlet.class);
+        assertEquals(true, logServlet.isCompressRecording());
+        assertEquals(LogTracer.PROP_TRACER_SERVLET_CACHE_SIZE_DEFAULT, logServlet.getCacheSizeInMB());
+        assertEquals(LogTracer.PROP_TRACER_SERVLET_CACHE_DURATION_DEFAULT, logServlet.getCacheDurationInSecs());
+
         MockOsgi.deactivate(tracer);
         assertNull(context.getService(Filter.class));
         assertNull(context.getService(Servlet.class));
     }
 
     @Test
+    public void enableTracerLogServletWithConfig() throws Exception {
+        LogTracer tracer = context.registerInjectActivateService(new LogTracer(),
+                ImmutableMap.<String, Object>of(
+                        "enabled", "true",
+                        "servletEnabled", "true",
+                        "recordingCacheSizeInMB", "17",
+                        "recordingCacheDurationInSecs", "100",
+                        "recordingCompressionEnabled", "false"
+                ));
+        assertEquals(2, context.getServices(Filter.class, null).length);
+        assertNotNull(context.getService(Servlet.class));
+
+        TracerLogServlet logServlet = (TracerLogServlet) context.getService(Servlet.class);
+        assertEquals(false, logServlet.isCompressRecording());
+        assertEquals(17, logServlet.getCacheSizeInMB());
+        assertEquals(100, logServlet.getCacheDurationInSecs());
+    }
+
+
+    @Test
     public void noTurboFilterRegisteredUnlessTracingRequested() throws Exception {
         HttpServletRequest request = mock(HttpServletRequest.class);
         HttpServletResponse response = mock(HttpServletResponse.class);
diff --git a/src/test/java/org/apache/sling/tracer/internal/TracerLogServletTest.java b/src/test/java/org/apache/sling/tracer/internal/TracerLogServletTest.java
index 7866544..5404380 100644
--- a/src/test/java/org/apache/sling/tracer/internal/TracerLogServletTest.java
+++ b/src/test/java/org/apache/sling/tracer/internal/TracerLogServletTest.java
@@ -24,6 +24,7 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 
+import javax.annotation.Nonnull;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -98,7 +99,7 @@ public class TracerLogServletTest {
 
         Recording recording = logServlet.startRecording(request, response);
         recording.registerTracker(createTracker("x" ,"y"));
-        recording.done();
+        logServlet.endRecording(recording);
 
         ArgumentCaptor<String> requestIdCaptor = ArgumentCaptor.forClass(String.class);
         verify(response).setHeader(eq(TracerLogServlet.HEADER_TRACER_REQUEST_ID), requestIdCaptor.capture());
@@ -135,7 +136,7 @@ public class TracerLogServletTest {
         }
 
         @Override
-        public void write(byte[] b, int off, int len) throws IOException {
+        public void write(@Nonnull byte[] b, int off, int len) throws IOException {
             baos.write(b, off, len);
         }
     }

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.