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

joerghoh pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-resource.git


The following commit(s) were added to refs/heads/master by this push:
     new 0ce0fb8  SLING-11654 implement AccessLogger (#38)
0ce0fb8 is described below

commit 0ce0fb850b3a71d800bc6d9c1e3a869fc5748cb1
Author: Jörg Hoh <[email protected]>
AuthorDate: Mon Nov 7 14:23:52 2022 +0100

    SLING-11654 implement AccessLogger (#38)
---
 .../jcr/resource/internal/helper/AccessLogger.java | 129 +++++++++++++++++++++
 .../internal/helper/jcr/JcrNodeResource.java       |   3 +
 2 files changed, 132 insertions(+)

diff --git 
a/src/main/java/org/apache/sling/jcr/resource/internal/helper/AccessLogger.java 
b/src/main/java/org/apache/sling/jcr/resource/internal/helper/AccessLogger.java
new file mode 100644
index 0000000..3ce9672
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/jcr/resource/internal/helper/AccessLogger.java
@@ -0,0 +1,129 @@
+/*
+ * 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.sling.jcr.resource.internal.helper;
+
+import java.io.Closeable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *  Helper class to report on repository access.
+ *  
+ *  The focus is to have a less overhead as possible if it is not used; in 
case it is enabled and access
+ *  is logged, the performance overhead does not matter.
+ *  
+ *  In the current implementation it has 2 features:
+ *  * Write a stacktrace on every recorded operation; this comes with a 
massive overhead and it is definitely
+ *    not recommended to turn this on on production, since it can easily 
create 100s of megabytes of log and will slow
+ *    down processing massively. To use this turn on TRACE logging on 
'org.apache.sling.jcr.resource.AccessLogger.operation'
+ *  * When a resource resolver is closed, dump a single log statement about 
the number of recorded operations. The overhead is
+ *    very small and it can be turned on for a longer period of time also in 
production. To use it enable DEBUG logging on
+ *    'org.apache.sling.jcr.resource.AccessLogger.statistics'.
+ *  
+ *
+ */
+public class AccessLogger implements Closeable {
+    
+    
+    Map<String,AtomicLong> metrics = new HashMap<>();
+    
+    private static final String SELF_NAME= AccessLogger.class.getName();
+    private static final Logger STATISTICS_LOG = 
LoggerFactory.getLogger("org.apache.sling.jcr.resource.AccessLogger.statistics");
+    private static final Logger OPERATION_LOG  = 
LoggerFactory.getLogger("org.apache.sling.jcr.resource.AccessLogger.operation");
+    
+    private final ResourceResolver resolver;
+    
+    // public
+    
+    
+    public static void incrementUsage(ResourceResolver resolver, String 
operation, String path) {
+        incrementUsage(resolver,operation, path, 1);
+    }
+    
+    public static void incrementUsage(Resource resource, String operation) {
+        incrementUsage(resource.getResourceResolver(),operation, 
resource.getPath(), 1);
+    }
+    
+    public static void incrementUsage(Resource resource, String operation, 
long count) {
+        incrementUsage(resource.getResourceResolver(), operation, 
resource.getPath(), count);
+    }
+    
+    public static void incrementUsage(ResourceResolver resolver, String 
operation, String path, long count) {
+
+        if (STATISTICS_LOG.isDebugEnabled()) {
+            AccessLogger am = (AccessLogger) 
resolver.getPropertyMap().get(SELF_NAME);
+            if (am == null) {
+                am = new AccessLogger(resolver);
+            }
+            am.incrementUsage(operation,count);
+        }
+        if (OPERATION_LOG.isTraceEnabled()) {
+            try {
+                String msg = String.format("invoked %s on [%s]", operation, 
path);
+                throw new Exception(msg);
+            } catch (Exception e) {
+                OPERATION_LOG.trace ("AccessLogger recording", e);
+            }
+        }
+    }
+    
+    // private
+    
+    private AccessLogger (ResourceResolver resolver) {
+        this.resolver = resolver;
+        resolver.getPropertyMap().put(SELF_NAME,this);
+    }
+    
+    
+    private void incrementUsage(String operation, long count) {
+        AtomicLong meter = metrics.get(operation);
+        if (meter == null) {
+            metrics.put(operation, new AtomicLong(count));
+        } else {
+            meter.addAndGet(count);
+        }
+    }
+    
+    @Override
+    public String toString() {
+        String values = metrics.keySet().stream()
+            .map(key -> key + "=" + metrics.get(key).get())
+            .collect(Collectors.joining(","));
+        
+        return "AccessLogger (" + values + ")";
+    }
+
+
+    @Override
+    public void close() {
+        STATISTICS_LOG.debug("AccessLogger dump for ResourceResolver 
(userid={},tostring={}): {}", resolver.getUserID(), resolver, this);
+    }
+    
+    
+    
+    
+    
+    
+
+}
diff --git 
a/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java
 
b/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java
index 9371aa5..57b58b4 100644
--- 
a/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java
+++ 
b/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java
@@ -42,6 +42,7 @@ import org.apache.sling.jcr.resource.internal.HelperData;
 import org.apache.sling.jcr.resource.internal.JcrModifiableValueMap;
 import org.apache.sling.jcr.resource.internal.JcrValueMap;
 import org.apache.sling.jcr.resource.internal.NodeUtil;
+import org.apache.sling.jcr.resource.internal.helper.AccessLogger;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
@@ -82,6 +83,7 @@ class JcrNodeResource extends JcrItemResource<Node> { // this 
should be package
         super(resourceResolver, path, version, node, new 
JcrNodeResourceMetadata(node));
         this.helper = helper;
         this.resourceSuperType = UNSET_RESOURCE_SUPER_TYPE;
+        AccessLogger.incrementUsage(resourceResolver, "newJcrNodeResource", 
path);
     }
 
     /**
@@ -130,6 +132,7 @@ class JcrNodeResource extends JcrItemResource<Node> { // 
this should be package
         } else if (type == InputStream.class) {
             return (Type) getInputStream(); // unchecked cast
         } else if (type == Map.class || type == ValueMap.class) {
+            AccessLogger.incrementUsage(this.getResourceResolver(), 
"adaptToValueMap", path);
             return (Type) new JcrValueMap(getNode(), this.helper);
         } else if (type == ModifiableValueMap.class) {
             // check write

Reply via email to