You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by fr...@apache.org on 2018/10/26 12:01:29 UTC

svn commit: r1844899 - in /jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment: SegmentNodeStore.java SegmentNodeStoreRegistrar.java tool/LoggingHook.java

Author: frm
Date: Fri Oct 26 12:01:29 2018
New Revision: 1844899

URL: http://svn.apache.org/viewvc?rev=1844899&view=rev
Log:
OAK-7849 - Implement a CommitHook to log commit activity

Contribution by Axel Hanikel

Added:
    jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/LoggingHook.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java
    jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java

Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java?rev=1844899&r1=1844898&r2=1844899&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java Fri Oct 26 12:01:29 2018
@@ -29,6 +29,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collections;
 import java.util.Map;
+import java.util.function.Consumer;
 
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
@@ -37,9 +38,11 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.segment.scheduler.Commit;
 import org.apache.jackrabbit.oak.segment.scheduler.LockBasedScheduler;
 import org.apache.jackrabbit.oak.segment.scheduler.Scheduler;
+import org.apache.jackrabbit.oak.segment.tool.LoggingHook;
 import org.apache.jackrabbit.oak.spi.blob.BlobStore;
 import org.apache.jackrabbit.oak.spi.commit.CommitHook;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.CompositeHook;
 import org.apache.jackrabbit.oak.spi.commit.Observable;
 import org.apache.jackrabbit.oak.spi.commit.Observer;
 import org.apache.jackrabbit.oak.spi.state.ConflictAnnotatingRebaseDiff;
@@ -81,7 +84,9 @@ public class SegmentNodeStore implements
 
         @NotNull
         private StatisticsProvider statsProvider = StatisticsProvider.NOOP;
-        
+
+        private LoggingHook loggingHook;
+
         private SegmentNodeStoreBuilder(
                 @NotNull Revisions revisions,
                 @NotNull SegmentReader reader,
@@ -110,7 +115,18 @@ public class SegmentNodeStore implements
             this.statsProvider = checkNotNull(statisticsProvider);
             return this;
         }
-        
+
+        /**
+         * {@link LoggingHook} for recording write operations to a log file
+         *
+         * @return this instance
+         */
+        @NotNull
+        public SegmentNodeStoreBuilder withLoggingHook(Consumer<String> writer) {
+            this.loggingHook = LoggingHook.newLoggingHook(writer);
+            return this;
+        }
+
         @NotNull
         public SegmentNodeStore build() {
             checkState(!isCreated);
@@ -123,7 +139,7 @@ public class SegmentNodeStore implements
         private static String getString(@Nullable BlobStore blobStore) {
             return "blobStore=" + (blobStore == null ? "inline" : blobStore);
         }
-        
+
         @Override
         public String toString() {
             return "SegmentNodeStoreBuilder{" +
@@ -157,6 +173,8 @@ public class SegmentNodeStore implements
     
     private final SegmentNodeStoreStats stats;
 
+    private final LoggingHook loggingHook;
+
     private SegmentNodeStore(SegmentNodeStoreBuilder builder) {
         this.writer = builder.writer;
         this.blobStore = builder.blobStore;
@@ -164,6 +182,7 @@ public class SegmentNodeStore implements
         this.scheduler = LockBasedScheduler.builder(builder.revisions, builder.reader, stats)
                 .dispatchChanges(builder.dispatchChanges)
                 .build();
+        this.loggingHook = builder.loggingHook;
     }
 
     @Override
@@ -187,6 +206,9 @@ public class SegmentNodeStore implements
             @NotNull CommitInfo info) throws CommitFailedException {
         checkArgument(builder instanceof SegmentNodeBuilder);
         checkArgument(((SegmentNodeBuilder) builder).isRootBuilder());
+        if (loggingHook != null) {
+            commitHook = new CompositeHook(commitHook, loggingHook);
+        }
         return scheduler.schedule(new Commit(builder, commitHook, info));
     }
 

Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java?rev=1844899&r1=1844898&r2=1844899&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java Fri Oct 26 12:01:29 2018
@@ -61,6 +61,7 @@ import org.apache.jackrabbit.oak.segment
 import org.apache.jackrabbit.oak.segment.file.tar.TarPersistence;
 import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence;
 import org.apache.jackrabbit.oak.segment.split.SplitPersistence;
+import org.apache.jackrabbit.oak.segment.tool.LoggingHook;
 import org.apache.jackrabbit.oak.spi.blob.BlobStore;
 import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
 import org.apache.jackrabbit.oak.spi.cluster.ClusterRepositoryInfo;
@@ -81,6 +82,7 @@ import org.apache.jackrabbit.oak.stats.S
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 class SegmentNodeStoreRegistrar {
 
@@ -364,6 +366,11 @@ class SegmentNodeStoreRegistrar {
         SegmentNodeStore.SegmentNodeStoreBuilder segmentNodeStoreBuilder = SegmentNodeStoreBuilders.builder(store).withStatisticsProvider(cfg.getStatisticsProvider());
         segmentNodeStoreBuilder.dispatchChanges(cfg.dispatchChanges());
 
+        Logger log = LoggerFactory.getLogger(LoggingHook.class.getName() + ".writer");
+        if (log.isTraceEnabled()) {
+            segmentNodeStoreBuilder.withLoggingHook(log::trace);
+        }
+
         SegmentNodeStore segmentNodeStore = segmentNodeStoreBuilder.build();
 
         if (cfg.isPrimarySegmentStore()) {

Added: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/LoggingHook.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/LoggingHook.java?rev=1844899&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/LoggingHook.java (added)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/LoggingHook.java Fri Oct 26 12:01:29 2018
@@ -0,0 +1,177 @@
+/*
+ * 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.jackrabbit.oak.segment.tool;
+
+import static org.apache.jackrabbit.oak.api.Type.BINARIES;
+import static org.apache.jackrabbit.oak.api.Type.BINARY;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.function.Consumer;
+
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.spi.commit.CommitHook;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+import org.jetbrains.annotations.NotNull;
+
+public class LoggingHook implements CommitHook, NodeStateDiff {
+
+    private final Consumer<String> writer;
+
+    private LoggingHook(final Consumer<String> writer) {
+        this.writer = writer;
+    }
+
+    public static LoggingHook newLoggingHook(final Consumer<String> writer) {
+        return new LoggingHook(writer);
+    }
+
+    public void enter(NodeState before, NodeState after) {
+        // do nothing
+    }
+
+    public void leave(NodeState before, NodeState after) {
+        log("n!");
+    }
+
+    @Override
+    public boolean propertyAdded(PropertyState after) {
+        log("p+ " + toString(after));
+        return true;
+    }
+
+    @Override
+    public boolean propertyChanged(PropertyState before, PropertyState after) {
+        log("p^ " + toString(after));
+        return true;
+    }
+
+    @Override
+    public boolean propertyDeleted(PropertyState before) {
+        log("p- " + toString(before));
+        return true;
+    }
+
+    @Override
+    public boolean childNodeAdded(String name, NodeState after) {
+        log("n+ " + urlEncode(name));
+        this.enter(null, after);
+        boolean ret = after.compareAgainstBaseState(EmptyNodeState.EMPTY_NODE, this);
+        this.leave(null, after);
+        return ret;
+    }
+
+    @Override
+    public boolean childNodeChanged(String name, NodeState before, NodeState after) {
+        log("n^ " + urlEncode(name));
+        this.enter(before, after);
+        boolean ret = after.compareAgainstBaseState(before, this);
+        this.leave(before, after);
+        return ret;
+    }
+
+    @Override
+    public boolean childNodeDeleted(String name, NodeState before) {
+        log("n- " + urlEncode(name));
+        return true;
+    }
+
+    private static String toString(final PropertyState ps) {
+        final StringBuilder val = new StringBuilder(); // TODO: an output stream would certainly be better
+        val.append(urlEncode(ps.getName()));
+        val.append(" <");
+        val.append(ps.getType());
+        val.append("> ");
+        if (ps.getType() == BINARY) {
+            val.append("= ");
+            final Blob blob = ps.getValue(BINARY);
+            appendBlob(val, blob);
+        } else if (ps.getType() == BINARIES) {
+            val.append("= [");
+            ps.getValue(BINARIES).forEach((Blob b) -> {
+                appendBlob(val, b);
+                val.append(',');
+            });
+            replaceOrAppendLastChar(val, ',', ']');
+        } else if (ps.isArray()) {
+            val.append("= [");
+            ps.getValue(STRINGS).forEach((String s) -> {
+                val.append(urlEncode(s));
+                val.append(',');
+            });
+            replaceOrAppendLastChar(val, ',', ']');
+        } else {
+            val.append("= ").append(urlEncode(ps.getValue(STRING)));
+        }
+        return val.toString();
+    }
+
+    private static String urlEncode(String s) {
+        String ret;
+        try {
+            ret = URLEncoder.encode(s, "UTF-8").replace("%2F", "/").replace("%3A", ":");
+        } catch (UnsupportedEncodingException ex) {
+            ret = "ERROR: " + ex.toString();
+        }
+        return ret;
+    }
+
+    private static void replaceOrAppendLastChar(StringBuilder b, char oldChar, char newChar) {
+        if (b.charAt(b.length() - 1) == oldChar) {
+            b.setCharAt(b.length() - 1, newChar);
+        } else {
+            b.append(newChar);
+        }
+    }
+
+    private void log(String s) {
+        writer.accept(System.currentTimeMillis() + " " + urlEncode(Thread.currentThread().getName()) + " " + s);
+    }
+
+    private static void appendBlob(StringBuilder sb, Blob blob) {
+        final InputStream is = blob.getNewStream();
+        final char[] hex = "0123456789ABCDEF".toCharArray();
+        int b;
+        try {
+            while ((b = is.read()) >= 0) {
+                sb.append(hex[b >> 4]);
+                sb.append(hex[b & 0x0f]);
+            }
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @NotNull
+    @Override
+    public NodeState processCommit(NodeState before, NodeState after, CommitInfo info) throws CommitFailedException {
+        this.enter(before, after);
+        after.compareAgainstBaseState(before, this);
+        this.leave(before, after);
+        return after;
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/LoggingHook.java
------------------------------------------------------------------------------
    svn:eol-style = native