Author: justin
Date: Mon Aug  7 13:30:08 2017
New Revision: 1804334

URL: http://svn.apache.org/viewvc?rev=1804334&view=rev
Log:
SLING-7031 - configurable component to write subset of metrics to log file on a 
recurring basis.

Added:
    
sling/trunk/bundles/commons/metrics/src/main/java/org/apache/sling/commons/metrics/internal/LogReporter.java
    
sling/trunk/bundles/commons/metrics/src/test/java/org/apache/sling/commons/metrics/internal/LogReporterTest.java
Modified:
    sling/trunk/bundles/commons/metrics/pom.xml

Modified: sling/trunk/bundles/commons/metrics/pom.xml
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics/pom.xml?rev=1804334&r1=1804333&r2=1804334&view=diff
==============================================================================
--- sling/trunk/bundles/commons/metrics/pom.xml (original)
+++ sling/trunk/bundles/commons/metrics/pom.xml Mon Aug  7 13:30:08 2017
@@ -238,6 +238,12 @@
             <scope>test</scope>
          </dependency>
         <dependency>
+            <groupId>junit-addons</groupId>
+            <artifactId>junit-addons</artifactId>
+            <version>1.4</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.testing.paxexam</artifactId>
             <version>0.0.4</version>

Added: 
sling/trunk/bundles/commons/metrics/src/main/java/org/apache/sling/commons/metrics/internal/LogReporter.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics/src/main/java/org/apache/sling/commons/metrics/internal/LogReporter.java?rev=1804334&view=auto
==============================================================================
--- 
sling/trunk/bundles/commons/metrics/src/main/java/org/apache/sling/commons/metrics/internal/LogReporter.java
 (added)
+++ 
sling/trunk/bundles/commons/metrics/src/main/java/org/apache/sling/commons/metrics/internal/LogReporter.java
 Mon Aug  7 13:30:08 2017
@@ -0,0 +1,150 @@
+/*
+ * 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.commons.metrics.internal;
+
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Slf4jReporter;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+@Component(service = {}, configurationPolicy = ConfigurationPolicy.REQUIRE)
+@Designate(ocd = LogReporter.Config.class, factory = true)
+public class LogReporter implements ServiceTrackerCustomizer<MetricRegistry, 
Slf4jReporter> {
+
+    private BundleContext context;
+    private ServiceTracker<MetricRegistry, Slf4jReporter> tracker;
+    private Config config;
+
+    @Activate
+    protected void activate(Config config, BundleContext context) {
+        this.config = config;
+        this.context = context;
+        tracker = new ServiceTracker<>(context, MetricRegistry.class, this);
+        tracker.open();
+    }
+
+    @Deactivate
+    protected void deactivate(BundleContext context) {
+        tracker.close();
+    }
+
+    //~---------------------------------------------< ServiceTracker >
+
+    @Override
+    public Slf4jReporter addingService(ServiceReference<MetricRegistry> 
serviceReference) {
+        MetricRegistry registry = context.getService(serviceReference);
+        String metricRegistryName = (String) 
serviceReference.getProperty(MetricWebConsolePlugin.METRIC_REGISTRY_NAME);
+
+        if (config.registryName() == null || config.registryName().length() == 0
+                || config.registryName().equals(metricRegistryName)) {
+            Slf4jReporter.Builder builder = 
Slf4jReporter.forRegistry(registry).
+                    outputTo(LoggerFactory.getLogger(config.loggerName())).
+                    withLoggingLevel(config.level());
+
+            if (config.prefix() != null && config.prefix().length() > 0) {
+                builder.filter(new PrefixFilter(config.prefix()));
+            } else if (config.pattern() != null && config.pattern().length() > 
0) {
+                builder.filter(new PatternFilter(config.pattern()));
+            }
+
+            Slf4jReporter reporter = builder.build();
+            reporter.start(config.period(), config.timeUnit());
+            return reporter;
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void modifiedService(ServiceReference<MetricRegistry> 
serviceReference, Slf4jReporter reporter) {
+        // NO OP
+    }
+
+    @Override
+    public void removedService(ServiceReference<MetricRegistry> 
serviceReference, Slf4jReporter reporter) {
+        if (reporter != null) {
+            reporter.close();
+        }
+    }
+
+    private class PrefixFilter implements MetricFilter {
+        private final String prefix;
+
+        private PrefixFilter(String prefix) {
+            this.prefix = prefix;
+        }
+
+        @Override
+        public boolean matches(String s, Metric metric) {
+            return s.startsWith(prefix);
+        }
+    }
+
+    private class PatternFilter implements MetricFilter {
+        private final Pattern pattern;
+
+        private PatternFilter(String pattern) {
+            this.pattern = Pattern.compile(pattern);
+        }
+
+        @Override
+        public boolean matches(String s, Metric metric) {
+            return pattern.matcher(s).matches();
+        }
+    }
+
+    @ObjectClassDefinition(name = "Apache Sling Metrics Log Reporter 
Configuration")
+    @interface Config {
+
+        @AttributeDefinition(description = "Period at which the metrics data 
will be logged")
+        long period() default 5;
+
+        @AttributeDefinition(description = "Unit of time for evaluating the 
period")
+        TimeUnit timeUnit() default TimeUnit.MINUTES;
+
+        @AttributeDefinition(description = "The log level to log at.")
+        Slf4jReporter.LoggingLevel level() default 
Slf4jReporter.LoggingLevel.INFO;
+
+        @AttributeDefinition(description = "The logger name")
+        String loggerName() default "metrics";
+
+        @AttributeDefinition(description = "If specified, only metrics whose 
name starts with this value are logged. If both prefix and pattern are set, 
prefix is used.")
+        String prefix() default "";
+
+        @AttributeDefinition(description = "If specified, only metrics whose 
name matches this regular expression will be logged. If both prefix and pattern 
are set, prefix is used.")
+        String pattern() default "";
+
+        @AttributeDefinition(description = "Restrict the metrics logged to a 
specifically named registry.")
+        String registryName() default "";
+    }
+
+}

Added: 
sling/trunk/bundles/commons/metrics/src/test/java/org/apache/sling/commons/metrics/internal/LogReporterTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics/src/test/java/org/apache/sling/commons/metrics/internal/LogReporterTest.java?rev=1804334&view=auto
==============================================================================
--- 
sling/trunk/bundles/commons/metrics/src/test/java/org/apache/sling/commons/metrics/internal/LogReporterTest.java
 (added)
+++ 
sling/trunk/bundles/commons/metrics/src/test/java/org/apache/sling/commons/metrics/internal/LogReporterTest.java
 Mon Aug  7 13:30:08 2017
@@ -0,0 +1,449 @@
+/*
+ * 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.commons.metrics.internal;
+
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.Slf4jReporter;
+import junitx.util.PrivateAccessor;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+
+
+import static org.mockito.Mockito.*;
+import static org.junit.Assert.*;
+
+import java.lang.annotation.Annotation;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(MockitoJUnitRunner.class)
+public class LogReporterTest {
+
+    @Mock
+    private BundleContext bundleContext;
+
+    LogReporter reporterService = new LogReporter();
+
+    @Test
+    public void testSpecificRegistryNameInclude() {
+        MetricRegistry registry = new MetricRegistry();
+        ServiceReference<MetricRegistry> registryServiceReference = 
mock(ServiceReference.class);
+        
when(bundleContext.getService(registryServiceReference)).thenReturn(registry);
+        
when(registryServiceReference.getProperty(MetricWebConsolePlugin.METRIC_REGISTRY_NAME)).thenReturn("oak");
+
+        LogReporter.Config config = createConfigWithRegistryName("oak");
+        reporterService.activate(config, bundleContext);
+
+        Slf4jReporter reporter = null;
+        try {
+            reporter = reporterService.addingService(registryServiceReference);
+            assertNotNull(reporter);
+        } finally {
+            if (reporter != null) {
+                reporter.close();
+            }
+            reporterService.deactivate(bundleContext);
+        }
+    }
+
+    @Test
+    public void testSpecificRegistryNameExclude() {
+        MetricRegistry registry = new MetricRegistry();
+        ServiceReference<MetricRegistry> registryServiceReference = 
mock(ServiceReference.class);
+        
when(bundleContext.getService(registryServiceReference)).thenReturn(registry);
+        
when(registryServiceReference.getProperty(MetricWebConsolePlugin.METRIC_REGISTRY_NAME)).thenReturn("other");
+
+        LogReporter.Config config = createConfigWithRegistryName("oak");
+        reporterService.activate(config, bundleContext);
+
+        Slf4jReporter reporter = null;
+        try {
+            reporter = reporterService.addingService(registryServiceReference);
+            assertNull(reporter);
+        } finally {
+            if (reporter != null) {
+                reporter.close();
+            }
+            reporterService.deactivate(bundleContext);
+        }
+    }
+
+    @Test
+    public void testSpecificRegistryNameExcludeNullName() {
+        MetricRegistry registry = new MetricRegistry();
+        ServiceReference<MetricRegistry> registryServiceReference = 
mock(ServiceReference.class);
+        
when(bundleContext.getService(registryServiceReference)).thenReturn(registry);
+
+        LogReporter.Config config = createConfigWithRegistryName("oak");
+        reporterService.activate(config, bundleContext);
+
+        Slf4jReporter reporter = null;
+        try {
+            reporter = reporterService.addingService(registryServiceReference);
+            assertNull(reporter);
+        } finally {
+            if (reporter != null) {
+                reporter.close();
+            }
+            reporterService.deactivate(bundleContext);
+        }
+    }
+
+    @Test
+    public void testLoggerName() throws Exception {
+        MetricRegistry registry = new MetricRegistry();
+        ServiceReference<MetricRegistry> registryServiceReference = 
mock(ServiceReference.class);
+        
when(bundleContext.getService(registryServiceReference)).thenReturn(registry);
+
+        LogReporter.Config config = createConfigWithLoggerNameAndLevel("test", 
Slf4jReporter.LoggingLevel.WARN);
+        reporterService.activate(config, bundleContext);
+
+        Slf4jReporter reporter = null;
+        try {
+            reporter = reporterService.addingService(registryServiceReference);
+            assertNotNull(reporter);
+
+            Object loggerProxy = PrivateAccessor.getField(reporter, 
"loggerProxy");
+            assertEquals("WarnLoggerProxy", 
loggerProxy.getClass().getSimpleName());
+
+            Logger logger = (Logger) PrivateAccessor.getField(loggerProxy, 
"logger");
+            assertEquals("test", logger.getName());
+        } finally {
+            if (reporter != null) {
+                reporter.close();
+            }
+            reporterService.deactivate(bundleContext);
+        }
+    }
+
+    @Test
+    public void testPrefix() throws Exception {
+        MetricRegistry registry = new MetricRegistry();
+        ServiceReference<MetricRegistry> registryServiceReference = 
mock(ServiceReference.class);
+        
when(bundleContext.getService(registryServiceReference)).thenReturn(registry);
+
+        LogReporter.Config config = createConfigWithPrefix("testPrefix");
+        reporterService.activate(config, bundleContext);
+
+        Slf4jReporter reporter = null;
+        try {
+            reporter = reporterService.addingService(registryServiceReference);
+            assertNotNull(reporter);
+
+            MetricFilter filter = (MetricFilter) 
PrivateAccessor.getField(reporter, "filter");
+            assertEquals("PrefixFilter", filter.getClass().getSimpleName());
+            assertTrue(filter.matches("testPrefixedName", null));
+            assertFalse(filter.matches("testNonPrefixedName", null));
+        } finally {
+            if (reporter != null) {
+                reporter.close();
+            }
+            reporterService.deactivate(bundleContext);
+        }
+    }
+
+    @Test
+    public void testPattern() throws Exception {
+        MetricRegistry registry = new MetricRegistry();
+        ServiceReference<MetricRegistry> registryServiceReference = 
mock(ServiceReference.class);
+        
when(bundleContext.getService(registryServiceReference)).thenReturn(registry);
+
+        LogReporter.Config config = createConfigWithPattern("[0-9]test.*");
+        reporterService.activate(config, bundleContext);
+
+        Slf4jReporter reporter = null;
+        try {
+            reporter = reporterService.addingService(registryServiceReference);
+            assertNotNull(reporter);
+
+            MetricFilter filter = (MetricFilter) 
PrivateAccessor.getField(reporter, "filter");
+            assertEquals("PatternFilter", filter.getClass().getSimpleName());
+            assertTrue(filter.matches("5testTest", null));
+            assertFalse(filter.matches("ZtestTest", null));
+        } finally {
+            if (reporter != null) {
+                reporter.close();
+            }
+            reporterService.deactivate(bundleContext);
+        }
+    }
+
+    @Test
+    public void testPrefixAndPattern() throws Exception {
+        MetricRegistry registry = new MetricRegistry();
+        ServiceReference<MetricRegistry> registryServiceReference = 
mock(ServiceReference.class);
+        
when(bundleContext.getService(registryServiceReference)).thenReturn(registry);
+
+        LogReporter.Config config = 
createConfigWithPrefixAndPattern("testPrefix", "[0-9]test.*");
+        reporterService.activate(config, bundleContext);
+
+        Slf4jReporter reporter = null;
+        try {
+            reporter = reporterService.addingService(registryServiceReference);
+            assertNotNull(reporter);
+
+            MetricFilter filter = (MetricFilter) 
PrivateAccessor.getField(reporter, "filter");
+            assertEquals("PrefixFilter", filter.getClass().getSimpleName());
+            assertTrue(filter.matches("testPrefixedName", null));
+            assertFalse(filter.matches("testNonPrefixedName", null));
+        } finally {
+            if (reporter != null) {
+                reporter.close();
+            }
+            reporterService.deactivate(bundleContext);
+        }
+    }
+
+    @Test
+    public void testRemove() {
+        Slf4jReporter reporter = mock(Slf4jReporter.class);
+        reporterService.removedService(null, reporter);
+        verify(reporter, times(1)).close();
+    }
+
+    @Test
+    public void testNoOpCalls() {
+        // extra no-op calls for coverage
+        reporterService.removedService(null, null);
+        reporterService.modifiedService(null, null);
+    }
+
+    private LogReporter.Config createConfigWithRegistryName(final String 
registryName) {
+        return new LogReporter.Config() {
+            @Override
+            public long period() {
+                return 5;
+            }
+
+            @Override
+            public TimeUnit timeUnit() {
+                return TimeUnit.MINUTES;
+            }
+
+            @Override
+            public Slf4jReporter.LoggingLevel level() {
+                return Slf4jReporter.LoggingLevel.INFO;
+            }
+
+            @Override
+            public String loggerName() {
+                return "metrics";
+            }
+
+            @Override
+            public String prefix() {
+                return null;
+            }
+
+            @Override
+            public String pattern() {
+                return null;
+            }
+
+            @Override
+            public String registryName() {
+                return registryName;
+            }
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return LogReporter.Config.class;
+            }
+        };
+    }
+
+    private LogReporter.Config createConfigWithLoggerNameAndLevel(final String 
loggerName, final Slf4jReporter.LoggingLevel level) {
+        return new LogReporter.Config() {
+            @Override
+            public long period() {
+                return 5;
+            }
+
+            @Override
+            public TimeUnit timeUnit() {
+                return TimeUnit.MINUTES;
+            }
+
+            @Override
+            public Slf4jReporter.LoggingLevel level() {
+                return level;
+            }
+
+            @Override
+            public String loggerName() {
+                return loggerName;
+            }
+
+            @Override
+            public String prefix() {
+                return null;
+            }
+
+            @Override
+            public String pattern() {
+                return null;
+            }
+
+            @Override
+            public String registryName() {
+                return "";
+            }
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return LogReporter.Config.class;
+            }
+        };
+    }
+
+    private LogReporter.Config createConfigWithPrefix(final String prefix) {
+        return new LogReporter.Config() {
+            @Override
+            public long period() {
+                return 5;
+            }
+
+            @Override
+            public TimeUnit timeUnit() {
+                return TimeUnit.MINUTES;
+            }
+
+            @Override
+            public Slf4jReporter.LoggingLevel level() {
+                return Slf4jReporter.LoggingLevel.INFO;
+            }
+
+            @Override
+            public String loggerName() {
+                return "metrics";
+            }
+
+            @Override
+            public String prefix() {
+                return prefix;
+            }
+
+            @Override
+            public String pattern() {
+                return null;
+            }
+
+            @Override
+            public String registryName() {
+                return null;
+            }
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return LogReporter.Config.class;
+            }
+        };
+    }
+
+    private LogReporter.Config createConfigWithPattern(final String pattern) {
+        return new LogReporter.Config() {
+            @Override
+            public long period() {
+                return 5;
+            }
+
+            @Override
+            public TimeUnit timeUnit() {
+                return TimeUnit.MINUTES;
+            }
+
+            @Override
+            public Slf4jReporter.LoggingLevel level() {
+                return Slf4jReporter.LoggingLevel.INFO;
+            }
+
+            @Override
+            public String loggerName() {
+                return "metrics";
+            }
+
+            @Override
+            public String prefix() {
+                return null;
+            }
+
+            @Override
+            public String pattern() {
+                return pattern;
+            }
+
+            @Override
+            public String registryName() {
+                return null;
+            }
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return LogReporter.Config.class;
+            }
+        };
+    }
+
+    private LogReporter.Config createConfigWithPrefixAndPattern(final String 
prefix, final String pattern) {
+        return new LogReporter.Config() {
+            @Override
+            public long period() {
+                return 5;
+            }
+
+            @Override
+            public TimeUnit timeUnit() {
+                return TimeUnit.MINUTES;
+            }
+
+            @Override
+            public Slf4jReporter.LoggingLevel level() {
+                return Slf4jReporter.LoggingLevel.INFO;
+            }
+
+            @Override
+            public String loggerName() {
+                return "metrics";
+            }
+
+            @Override
+            public String prefix() {
+                return prefix;
+            }
+
+            @Override
+            public String pattern() {
+                return pattern;
+            }
+
+            @Override
+            public String registryName() {
+                return null;
+            }
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return LogReporter.Config.class;
+            }
+        };
+    }
+}


Reply via email to