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/10/20 14:35:39 UTC

[sling-org-apache-sling-commons-metrics-rrd4j] branch master created (now 64fc956)

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

rombert pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics-rrd4j.git.


      at 64fc956  SLING-7167 Adjust READMEs

This branch includes the following new commits:

     new 9e8e6c9  SLING-7055 - RRD4J metrics reporter
     new a699af7  SLING-7063 - Inventory printer for metrics RRD4J reporter
     new a00a09f  SLING-7071 - Rename metrics file on configuration change
     new cb80901  SLING-7077 - Reduce size of RRD4J metrics reporter
     new 8ad1d5c  SLING-7074 - RRD4J improve configuration handling
     new 955a2a6  SLING-7090 - RRD4J metrics reporter does not close file on deactivate
     new 553c276  [maven-release-plugin] prepare release org.apache.sling.commons.metrics-rrd4j-1.0.0
     new d4ad5b4  [maven-release-plugin] prepare for next development iteration
     new 64fc956  SLING-7167 Adjust READMEs

The 9 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


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

[sling-org-apache-sling-commons-metrics-rrd4j] 01/09: SLING-7055 - RRD4J metrics reporter

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics-rrd4j.git

commit 9e8e6c9f538189d96a0bf5055d40e2c227f4f097
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Fri Aug 18 08:47:34 2017 +0000

    SLING-7055 - RRD4J metrics reporter
    
    Initial implementation
    
    Submitted-By: Marcel Reutegger
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1805386 13f79535-47bb-0310-9956-ffa450edef68
---
 README.md                                          |  16 ++
 pom.xml                                            | 105 ++++++++
 .../rrd4j/impl/CodahaleMetricsReporter.java        | 170 ++++++++++++
 .../rrd4j/impl/CopyMetricRegistryListener.java     | 113 ++++++++
 .../commons/metrics/rrd4j/impl/RRD4JReporter.java  | 289 +++++++++++++++++++++
 .../commons/metrics/rrd4j/impl/ReporterTest.java   |  97 +++++++
 src/test/resources/logback-test.xml                |  30 +++
 7 files changed, 820 insertions(+)

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..542068f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,16 @@
+# Apache Sling RRD4J metrics reporter
+
+This is a bundle that stores metrics on the local filesystem using
+[RRD4J](https://github.com/rrd4j/rrd4j).
+
+Build this bundle with Maven:
+
+    mvn clean install
+
+The reporter will not store metrics by default. You need to configure it and
+tell the reporter what metrics to store.
+
+Go to the Apache Felix Web Console and configure 'Apache Sling Metrics reporter
+writing to RRD4J'. The reporter will start storing metrics once data sources
+have been added and the configuration is saved. Please note, the metrics file
+is recreated/cleared whenever the configuration is changed.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..4ef5901
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>31</version>
+    </parent>
+
+    <artifactId>org.apache.sling.commons.metrics-rrd4j</artifactId>
+    <packaging>bundle</packaging>
+    <version>0.0.1-SNAPSHOT</version>
+
+    <name>Apache Sling RRD4J metrics reporter</name>
+    <description>
+       Stores Metrics to the local filesystem using RRD4J.
+    </description>
+
+    <properties>
+        <sling.java.version>8</sling.java.version>
+    </properties>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/metrics-rrd4j</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/metrics-rrd4j</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics-rrd4j</url>
+    </scm>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            com.mongodb;resolution:=optional,
+                            com.sleepycat.je;resolution:=optional,
+                            sun.misc;resolution:=optional,
+                            sun.nio.ch;resolution:=optional,
+                            *
+                        </Import-Package>
+                        <Embed-Dependency>rrd4j</Embed-Dependency>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.dropwizard.metrics</groupId>
+            <artifactId>metrics-core</artifactId>
+            <version>3.1.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.rrd4j</groupId>
+            <artifactId>rrd4j</artifactId>
+            <version>3.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.1.7</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.osgi-mock</artifactId>
+            <version>2.3.2</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
new file mode 100644
index 0000000..97cf78a
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
@@ -0,0 +1,170 @@
+/*
+ * 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.rrd4j.impl;
+
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.ScheduledReporter;
+
+import org.osgi.framework.BundleContext;
+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.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.sling.commons.metrics.rrd4j.impl.RRD4JReporter.DEFAULT_STEP;
+
+@Component(
+        immediate = true,
+        configurationPolicy = ConfigurationPolicy.REQUIRE
+)
+@Designate(ocd = CodahaleMetricsReporter.Configuration.class)
+public class CodahaleMetricsReporter {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CodahaleMetricsReporter.class);
+
+    private ScheduledReporter reporter;
+
+    private Map<String, CopyMetricRegistryListener> listeners = new ConcurrentHashMap<>();
+
+    @ObjectClassDefinition(name = "Apache Sling Metrics reporter writing to RRD4J",
+            description = "For syntax details on RRD data-source and round " +
+                    "robin archive definitions see " +
+                    "https://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html and " +
+                    "https://github.com/rrd4j/rrd4j/wiki/Tutorial. Changing " +
+                    "any attribute in this configuration will replace an " +
+                    "existing RRD file with a empty one!")
+    public @interface Configuration {
+
+        @AttributeDefinition(
+                name = "Data sources",
+                description = "RRDTool data source definitions " +
+                        "(e.g. 'DS:oak_SESSION_LOGIN_COUNTER:COUNTER:300:0:U'). " +
+                        "Replace colon characters in the metric name with an " +
+                        "underscore!"
+        )
+        String[] datasources() default {};
+
+        @AttributeDefinition(
+                name = "Step",
+                description = "The base interval in seconds with which data " +
+                        "will be fed into the RRD"
+        )
+        int step() default DEFAULT_STEP;
+
+        @AttributeDefinition(
+                name = "Archives",
+                description = "RRDTool round robin archive definitions. The " +
+                        "default configuration defines four archives based " +
+                        "on a default step of five seconds: " +
+                        "1) per minute averages for six hours, " +
+                        "2) per five minute averages 48 hours, " +
+                        "3) per hour averages for four weeks, " +
+                        "4) per day averages for one year."
+        )
+        String[] archives() default {
+            "RRA:AVERAGE:0.5:12:360", "RRA:AVERAGE:0.5:60:576", "RRA:AVERAGE:0.5:720:336", "RRA:AVERAGE:0.5:17280:365"
+        };
+
+        @AttributeDefinition(
+                name = "Path",
+                description = "Path of the RRD file where metrics are stored. " +
+                        "If the path is relative, it is resolved relative to " +
+                        "the value of the framework property 'sling.home' when " +
+                        "available, otherwise relative to the current working " +
+                        "directory."
+        )
+        String path() default "metrics/metrics.rrd";
+    }
+
+    private MetricRegistry metricRegistry = new MetricRegistry();
+
+    @Activate
+    void activate(BundleContext context, Configuration config) throws Exception {
+        LOG.info("Starting RRD4J Metrics reporter");
+        File path = new File(config.path());
+        if (!path.isAbsolute()) {
+            String home = context.getProperty("sling.home");
+            if (home != null) {
+                path = new File(home, path.getPath());
+            }
+        }
+
+        reporter = RRD4JReporter.forRegistry(metricRegistry)
+                .convertRatesTo(TimeUnit.SECONDS)
+                .convertDurationsTo(TimeUnit.MICROSECONDS)
+                .withPath(path)
+                .withDatasources(config.datasources())
+                .withArchives(config.archives())
+                .withStep(config.step())
+                .build();
+        reporter.start(config.step(), TimeUnit.SECONDS);
+        LOG.info("Started RRD4J Metrics reporter. Writing to " + path);
+    }
+
+    @Deactivate
+    void deactivate() {
+        LOG.info("Stopping RRD4J Metrics reporter");
+        reporter.stop();
+        reporter = null;
+        LOG.info("Stopped RRD4J Metrics reporter");
+    }
+
+    @Reference(
+            service = MetricRegistry.class,
+            cardinality = ReferenceCardinality.MULTIPLE,
+            policy = ReferencePolicy.DYNAMIC)
+    synchronized void addMetricRegistry(MetricRegistry metricRegistry,
+                                        Map<String, Object> properties) {
+        String name = (String) properties.get("name");
+        if (name == null) {
+            name = metricRegistry.toString();
+        }
+        CopyMetricRegistryListener listener = new CopyMetricRegistryListener(this.metricRegistry, name);
+        listener.start(metricRegistry);
+        this.listeners.put(name, listener);
+        LOG.info("Bound Metrics Registry {} ",name);
+    }
+
+    synchronized void removeMetricRegistry(MetricRegistry metricRegistry,
+                                           Map<String, Object> properties) {
+        String name = (String) properties.get("name");
+        if (name == null) {
+            name = metricRegistry.toString();
+        }
+        CopyMetricRegistryListener metricRegistryListener = listeners.get(name);
+        if ( metricRegistryListener != null) {
+            metricRegistryListener.stop(metricRegistry);
+            this.listeners.remove(name);
+        }
+        LOG.info("Unbound Metrics Registry {} ",name);
+    }
+}
diff --git a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CopyMetricRegistryListener.java b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CopyMetricRegistryListener.java
new file mode 100644
index 0000000..fbb20aa
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CopyMetricRegistryListener.java
@@ -0,0 +1,113 @@
+/*
+ * 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.rrd4j.impl;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.MetricRegistryListener;
+import com.codahale.metrics.Timer;
+
+class CopyMetricRegistryListener implements MetricRegistryListener {
+
+    private final MetricRegistry parent;
+    private final String name;
+
+    CopyMetricRegistryListener(MetricRegistry parent, String name) {
+        this.parent = parent;
+        this.name = name;
+    }
+
+    void start(MetricRegistry metricRegistry) {
+        metricRegistry.addListener(this);
+    }
+
+    void stop(MetricRegistry metricRegistry) {
+        metricRegistry.removeListener(this);
+        for(String name : metricRegistry.getMetrics().keySet()) {
+            removeMetric(name);
+        }
+    }
+
+    private void addMetric(String metricName, Metric m) {
+        parent.register(getMetricName(metricName), m);
+    }
+
+    private void removeMetric(String metricName) {
+        parent.remove(getMetricName(metricName));
+    }
+
+    private String getMetricName(String metricName) {
+        return name + "_" + metricName;
+    }
+
+    @Override
+    public void onGaugeAdded(String s, Gauge<?> gauge) {
+        addMetric(s, gauge);
+    }
+
+    @Override
+    public void onGaugeRemoved(String s) {
+        removeMetric(s);
+    }
+
+    @Override
+    public void onCounterAdded(String s, Counter counter) {
+        addMetric(s, counter);
+    }
+
+    @Override
+    public void onCounterRemoved(String s) {
+        removeMetric(s);
+    }
+
+    @Override
+    public void onHistogramAdded(String s, Histogram histogram) {
+        addMetric(s, histogram);
+    }
+
+    @Override
+    public void onHistogramRemoved(String s) {
+        removeMetric(s);
+    }
+
+    @Override
+    public void onMeterAdded(String s, Meter meter) {
+        addMetric(s, meter);
+    }
+
+    @Override
+    public void onMeterRemoved(String s) {
+        removeMetric(s);
+    }
+
+    @Override
+    public void onTimerAdded(String s, Timer timer) {
+        addMetric(s, timer);
+    }
+
+    @Override
+    public void onTimerRemoved(String s) {
+        removeMetric(s);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java
new file mode 100644
index 0000000..8eaacfa
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java
@@ -0,0 +1,289 @@
+/*
+ * 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.rrd4j.impl;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.ScheduledReporter;
+import com.codahale.metrics.Timer;
+
+import org.rrd4j.core.RrdDb;
+import org.rrd4j.core.RrdDef;
+import org.rrd4j.core.Sample;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.SortedMap;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.String.join;
+
+class RRD4JReporter extends ScheduledReporter {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(RRD4JReporter.class);
+    static final int DEFAULT_STEP = 5;
+
+    private final Map<String, Integer> dictionary = new HashMap<>();
+    private final RrdDb rrdDB;
+
+    static Builder forRegistry(MetricRegistry metricRegistry) {
+        return new Builder(metricRegistry);
+    }
+
+    static class Builder {
+        private MetricRegistry metricRegistry;
+        private TimeUnit ratesUnit;
+        private TimeUnit durationUnit;
+        private File path = new File(".");
+        private final List<String> indexedDS = new ArrayList<>();
+        private final Map<String, Integer> dictionary = new HashMap<>();
+        private final List<String> archives = new ArrayList<>();
+        private int step = DEFAULT_STEP;
+
+        Builder(MetricRegistry metricRegistry ) {
+            this.metricRegistry = metricRegistry;
+        }
+
+        Builder withPath(File path) {
+            this.path = path;
+            return this;
+        }
+
+        Builder withDatasources(String[] datasources) {
+            this.indexedDS.clear();
+            this.dictionary.clear();
+
+            int i = 0;
+            for (String ds : datasources) {
+                String[] tokens = ds.split(":");
+                if (tokens.length != 6) {
+                    throw new IllegalArgumentException("Invalid data source definition: " + ds);
+                }
+                dictionary.put(normalize(tokens[1]), i);
+                tokens[1] = String.valueOf(i);
+                this.indexedDS.add(checkDataSource(join(":", tokens)));
+                i++;
+            }
+            return this;
+        }
+
+        Builder withArchives(String[] archives) {
+            this.archives.clear();
+            this.archives.addAll(Arrays.asList(archives));
+            return this;
+        }
+
+        Builder withStep(int step) {
+            this.step = step;
+            return this;
+        }
+
+        Builder convertRatesTo(TimeUnit ratesUnit) {
+            this.ratesUnit = ratesUnit;
+            return this;
+        }
+
+        Builder convertDurationsTo(TimeUnit durationUnit) {
+            this.durationUnit = durationUnit;
+            return this;
+        }
+
+        ScheduledReporter build() throws IOException {
+            return new RRD4JReporter(metricRegistry, "RRD4JReporter",
+                    MetricFilter.ALL, ratesUnit, durationUnit, dictionary, createDef());
+        }
+
+        private String checkDataSource(String ds)
+                throws IllegalArgumentException {
+            new RrdDef("path").addDatasource(ds);
+            return ds;
+        }
+
+        private RrdDef createDef() {
+            RrdDef def = new RrdDef(path.getPath());
+            def.setStep(step);
+            for (String ds : indexedDS) {
+                def.addDatasource(ds);
+            }
+            for (String rra : archives) {
+                def.addArchive(rra);
+            }
+            return def;
+        }
+    }
+
+    RRD4JReporter(MetricRegistry registry,
+                  String name,
+                  MetricFilter filter,
+                  TimeUnit rateUnit,
+                  TimeUnit durationUnit,
+                  Map<String, Integer> dictionary,
+                  RrdDef rrdDef) throws IOException {
+        super(registry, name, filter, rateUnit, durationUnit);
+        this.dictionary.putAll(dictionary);
+        this.rrdDB = createDB(rrdDef);
+        storeDictionary(rrdDef.getPath() + ".properties");
+    }
+
+    @Override
+    public void close() {
+        try {
+            rrdDB.close();
+        } catch (IOException e) {
+            LOGGER.warn("Closing RRD failed", e);
+        }
+        super.close();
+    }
+
+    @Override
+    public void report(SortedMap<String, Gauge> gauges,
+                       SortedMap<String, Counter> counters,
+                       SortedMap<String, Histogram> histograms,
+                       SortedMap<String, Meter> meters,
+                       SortedMap<String, Timer> timers) {
+
+        try {
+            Sample sample = rrdDB.createSample(System.currentTimeMillis() / 1000);
+            for (Map.Entry<String, Gauge> entry : gauges.entrySet()) {
+                update(sample, indexForName(entry.getKey()), entry.getValue());
+            }
+
+            for (Map.Entry<String, Counter> entry : counters.entrySet()) {
+                update(sample, indexForName(entry.getKey()), entry.getValue());
+            }
+
+            for (Map.Entry<String, Histogram> entry : histograms.entrySet()) {
+                update(sample, indexForName(entry.getKey()), entry.getValue());
+            }
+
+            for (Map.Entry<String, Meter> entry : meters.entrySet()) {
+                update(sample, indexForName(entry.getKey()), entry.getValue());
+            }
+
+            for (Map.Entry<String, Timer> entry : timers.entrySet()) {
+                update(sample, indexForName(entry.getKey()), entry.getValue());
+            }
+            sample.update();
+        } catch (IOException e) {
+            LOGGER.warn("Unable to write sample to RRD", e);
+        }
+    }
+
+    private int indexForName(String name) {
+        Integer idx = dictionary.get(normalize(name));
+        return idx != null ? idx : -1;
+    }
+
+    private static String normalize(String name) {
+        return name.replaceAll(":", "_");
+    }
+
+    private void update(Sample sample, int nameIdx, Gauge g) {
+        if (nameIdx < 0) {
+            return;
+        }
+        Object value = g.getValue();
+        if (value instanceof Number) {
+            sample.setValue(nameIdx, ((Number) value).doubleValue());
+        }
+    }
+
+    private void update(Sample sample, int nameIdx, Counter c) {
+        if (nameIdx < 0) {
+            return;
+        }
+        sample.setValue(nameIdx, c.getCount());
+    }
+
+    private void update(Sample sample, int nameIdx, Histogram h) {
+        if (nameIdx < 0) {
+            return;
+        }
+        sample.setValue(nameIdx, h.getCount());
+    }
+
+    private void update(Sample sample, int nameIdx, Timer t) {
+        if (nameIdx < 0) {
+            return;
+        }
+        sample.setValue(nameIdx, t.getCount());
+    }
+
+
+    private void update(Sample sample, int nameIdx, Meter m) {
+        if (nameIdx < 0) {
+            return;
+        }
+        LOGGER.debug("Sample: {} = {}", nameIdx, m.getCount());
+        sample.setValue(nameIdx, m.getCount());
+    }
+
+    private void storeDictionary(String path) throws IOException {
+        File dictFile = new File(path);
+        if (dictFile.exists() && ! dictFile.delete()) {
+            throw new IOException("Unable to delete dictionary file: " + dictFile.getPath());
+        }
+        Properties dict = new Properties();
+        for (Map.Entry<String, Integer> entry : dictionary.entrySet()) {
+            dict.put(String.valueOf(entry.getValue()), entry.getKey());
+        }
+        try (FileOutputStream out = new FileOutputStream(dictFile)) {
+            dict.store(out, "RRD4JReporter dictionary");
+        }
+    }
+
+    private RrdDb createDB(RrdDef definition) throws IOException {
+        File dbFile = new File(definition.getPath());
+        if (!dbFile.getParentFile().exists()) {
+            if (!dbFile.getParentFile().mkdirs()) {
+                throw new IOException("Unable to create directory for RRD file: " + dbFile.getParent());
+            }
+        }
+        RrdDb db = null;
+        if (dbFile.exists()) {
+            db = new RrdDb(definition.getPath());
+            if (!db.getRrdDef().equals(definition)) {
+                // definition changed -> re-create DB
+                db.close();
+                if (!dbFile.delete()) {
+                    throw new IOException("Unable to delete RRD file: " + dbFile.getPath());
+                }
+                LOGGER.warn("Configuration changed, recreating RRD file for metrics: " + dbFile.getPath());
+                db = null;
+            }
+        }
+        if (db == null) {
+            db = new RrdDb(definition);
+        }
+        return db;
+    }
+}
diff --git a/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java b/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java
new file mode 100644
index 0000000..2dffcf9
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.rrd4j.impl;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+
+import org.apache.sling.commons.metrics.rrd4j.impl.CodahaleMetricsReporter;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.rrd4j.core.RrdDb;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class ReporterTest {
+
+    private static final File RRD = new File(new File("target", "metrics"), "metrics.rrd");
+
+    private static final long TEST_VALUE = 42;
+
+    @Rule
+    public final OsgiContext context = new OsgiContext();
+
+    private MetricRegistry registry = new MetricRegistry();
+
+    private CodahaleMetricsReporter reporter = new CodahaleMetricsReporter();
+
+    @Before
+    public void before() throws Exception {
+        RRD.delete();
+        context.registerService(MetricRegistry.class, registry, "name", "sling");
+
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("step", 1L);
+        properties.put("datasources", new String[]{"DS:sling_myMetric:GAUGE:300:0:U"});
+        properties.put("archives", new String[]{"RRA:AVERAGE:0.5:1:60"});
+        properties.put("path", RRD.getPath());
+        context.registerInjectActivateService(reporter, properties);
+
+        registry.register("myMetric", new TestGauge(TEST_VALUE));
+    }
+
+    @Test
+    public void writeRRD() throws Exception {
+        assertTrue(RRD.exists());
+        for (int i = 0; i < 10; i++) {
+            RrdDb db = new RrdDb(RRD.getPath(), true);
+            try {
+                double lastValue = db.getDatasource("0").getLastValue();
+                if (lastValue == (double) TEST_VALUE) {
+                    return;
+                }
+            } finally {
+                db.close();
+            }
+            Thread.sleep(1000);
+        }
+        fail("RRD4J reporter did not update database in time");
+    }
+
+    private static final class TestGauge implements Gauge<Long> {
+
+        private final long value;
+
+        TestGauge(long value) {
+            this.value = value;
+        }
+
+        @Override
+        public Long getValue() {
+            return value;
+        }
+    }
+}
diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..bcb98cc
--- /dev/null
+++ b/src/test/resources/logback-test.xml
@@ -0,0 +1,30 @@
+<!--
+   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.
+  -->
+<configuration>
+
+    <appender name="file" class="ch.qos.logback.core.FileAppender">
+        <file>target/unit-tests.log</file>
+        <encoder>
+            <pattern>%date{HH:mm:ss.SSS} %-5level %-40([%thread] %F:%L) %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="INFO">
+        <appender-ref ref="file"/>
+    </root>
+
+</configuration>

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

[sling-org-apache-sling-commons-metrics-rrd4j] 04/09: SLING-7077 - Reduce size of RRD4J metrics reporter

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics-rrd4j.git

commit cb8090144712ff82aba7e6a671bb9b271c622e5f
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Thu Aug 24 08:50:25 2017 +0000

    SLING-7077 - Reduce size of RRD4J metrics reporter
    
    Submitted-By: Marcel Reutegger
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1806010 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 52d544c..e424a12 100644
--- a/pom.xml
+++ b/pom.xml
@@ -60,7 +60,7 @@
                             sun.nio.ch;resolution:=optional,
                             *
                         </Import-Package>
-                        <Embed-Dependency>rrd4j</Embed-Dependency>
+                        <Embed-Dependency>rrd4j;inline=com/**|org/rrd4j/ConsolFun*|org/rrd4j/DsType*|org/rrd4j/core/**|org/rrd4j/data/**</Embed-Dependency>
                     </instructions>
                 </configuration>
             </plugin>

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

[sling-org-apache-sling-commons-metrics-rrd4j] 09/09: SLING-7167 Adjust READMEs

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics-rrd4j.git

commit 64fc956b0b388144d4f2756e9c9af62ada970e1d
Author: Oliver Lietz <ol...@apache.org>
AuthorDate: Tue Oct 3 09:49:50 2017 +0000

    SLING-7167 Adjust READMEs
    
    add uniform header linking to Sling project
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1810820 13f79535-47bb-0310-9956-ffa450edef68
---
 README.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 542068f..e2df165 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
 # Apache Sling RRD4J metrics reporter
 
+This module is part of the [Apache Sling](https://sling.apache.org) project.
+
 This is a bundle that stores metrics on the local filesystem using
 [RRD4J](https://github.com/rrd4j/rrd4j).
 
@@ -13,4 +15,4 @@ tell the reporter what metrics to store.
 Go to the Apache Felix Web Console and configure 'Apache Sling Metrics reporter
 writing to RRD4J'. The reporter will start storing metrics once data sources
 have been added and the configuration is saved. Please note, the metrics file
-is recreated/cleared whenever the configuration is changed.
\ No newline at end of file
+is recreated/cleared whenever the configuration is changed.

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

[sling-org-apache-sling-commons-metrics-rrd4j] 06/09: SLING-7090 - RRD4J metrics reporter does not close file on deactivate

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics-rrd4j.git

commit 955a2a6bea1cb453a9c20dd2c62106b0b218da2f
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Tue Aug 29 12:47:36 2017 +0000

    SLING-7090 - RRD4J metrics reporter does not close file on deactivate
    
    Submitted-By: Marcel Reutegger
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1806584 13f79535-47bb-0310-9956-ffa450edef68
---
 .../sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java       | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
index fd874b0..1c24c8a 100644
--- a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
+++ b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
@@ -164,7 +164,7 @@ public class CodahaleMetricsReporter implements InventoryPrinter, ZipAttachmentP
     void deactivate() {
         LOG.info("Stopping RRD4J Metrics reporter");
         if (reporter != null) {
-            reporter.stop();
+            reporter.close();
             reporter = null;
         }
         configuration = null;

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

[sling-org-apache-sling-commons-metrics-rrd4j] 02/09: SLING-7063 - Inventory printer for metrics RRD4J reporter

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics-rrd4j.git

commit a699af7324fc1254e7f80dc7e0d67430f463c606
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Tue Aug 22 09:06:55 2017 +0000

    SLING-7063 - Inventory printer for metrics RRD4J reporter
    
    Submitted-By: Marcel Reutegger
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1805733 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            |  6 ++
 .../rrd4j/impl/CodahaleMetricsReporter.java        | 74 ++++++++++++++++++++--
 2 files changed, 73 insertions(+), 7 deletions(-)

diff --git a/pom.xml b/pom.xml
index 4ef5901..52d544c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -84,6 +84,12 @@
             <version>3.1</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.inventory</artifactId>
+            <version>1.0.0</version>
+            <scope>provided</scope>
+        </dependency>
         <!-- test dependencies -->
         <dependency>
             <groupId>ch.qos.logback</groupId>
diff --git a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
index 97cf78a..c0d96cb 100644
--- a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
+++ b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
@@ -21,6 +21,9 @@ package org.apache.sling.commons.metrics.rrd4j.impl;
 import com.codahale.metrics.MetricRegistry;
 import com.codahale.metrics.ScheduledReporter;
 
+import org.apache.felix.inventory.Format;
+import org.apache.felix.inventory.InventoryPrinter;
+import org.apache.felix.inventory.ZipAttachmentProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -36,21 +39,35 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.util.Arrays;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 import static org.apache.sling.commons.metrics.rrd4j.impl.RRD4JReporter.DEFAULT_STEP;
 
 @Component(
         immediate = true,
-        configurationPolicy = ConfigurationPolicy.REQUIRE
+        configurationPolicy = ConfigurationPolicy.REQUIRE,
+        service = {InventoryPrinter.class, ZipAttachmentProvider.class},
+        property = {
+                InventoryPrinter.NAME + "=rrd4j-reporter",
+                InventoryPrinter.TITLE + "=Sling Metrics RRD4J reporter",
+                InventoryPrinter.FORMAT + "=TEXT"
+        }
 )
 @Designate(ocd = CodahaleMetricsReporter.Configuration.class)
-public class CodahaleMetricsReporter {
+public class CodahaleMetricsReporter implements InventoryPrinter, ZipAttachmentProvider {
 
     private static final Logger LOG = LoggerFactory.getLogger(CodahaleMetricsReporter.class);
 
+    private Configuration configuration;
+    private File rrd;
     private ScheduledReporter reporter;
 
     private Map<String, CopyMetricRegistryListener> listeners = new ConcurrentHashMap<>();
@@ -110,24 +127,25 @@ public class CodahaleMetricsReporter {
     @Activate
     void activate(BundleContext context, Configuration config) throws Exception {
         LOG.info("Starting RRD4J Metrics reporter");
-        File path = new File(config.path());
-        if (!path.isAbsolute()) {
+        configuration = config;
+        rrd = new File(config.path());
+        if (!rrd.isAbsolute()) {
             String home = context.getProperty("sling.home");
             if (home != null) {
-                path = new File(home, path.getPath());
+                rrd = new File(home, rrd.getPath());
             }
         }
 
         reporter = RRD4JReporter.forRegistry(metricRegistry)
                 .convertRatesTo(TimeUnit.SECONDS)
                 .convertDurationsTo(TimeUnit.MICROSECONDS)
-                .withPath(path)
+                .withPath(rrd)
                 .withDatasources(config.datasources())
                 .withArchives(config.archives())
                 .withStep(config.step())
                 .build();
         reporter.start(config.step(), TimeUnit.SECONDS);
-        LOG.info("Started RRD4J Metrics reporter. Writing to " + path);
+        LOG.info("Started RRD4J Metrics reporter. Writing to " + rrd);
     }
 
     @Deactivate
@@ -135,6 +153,8 @@ public class CodahaleMetricsReporter {
         LOG.info("Stopping RRD4J Metrics reporter");
         reporter.stop();
         reporter = null;
+        configuration = null;
+        rrd = null;
         LOG.info("Stopped RRD4J Metrics reporter");
     }
 
@@ -167,4 +187,44 @@ public class CodahaleMetricsReporter {
         }
         LOG.info("Unbound Metrics Registry {} ",name);
     }
+
+    //------------------------< InventoryPrinter >------------------------------
+
+    @Override
+    public void print(PrintWriter pw, Format format, boolean isZip) {
+        if (format == Format.TEXT) {
+            pw.println("Sling Metrics RRD4J reporter");
+            pw.println("Path: " + rrd.getAbsolutePath());
+            pw.println("Step: " + configuration.step());
+            pw.println("Datasources: " + Arrays.asList(configuration.datasources()));
+            pw.println("Archives: " + Arrays.asList(configuration.archives()));
+        }
+    }
+
+    //----------------------< ZipAttachmentProvider >---------------------------
+
+    @Override
+    public void addAttachments(ZipOutputStream zos, String namePrefix)
+            throws IOException {
+        if (rrd.exists()) {
+            appendFile(zos, rrd, namePrefix + configuration.path());
+        }
+        File props = new File(rrd.getParentFile(), rrd.getName() + ".properties");
+        if (props.exists()) {
+            appendFile(zos, props, namePrefix + configuration.path() + ".properties");
+        }
+    }
+
+    private void appendFile(ZipOutputStream zos, File file, String name)
+            throws IOException {
+        ZipEntry entry = new ZipEntry(name);
+        entry.setSize(file.length());
+        zos.putNextEntry(entry);
+        try {
+            Files.copy(file.toPath(), zos);
+            zos.flush();
+        } finally {
+            zos.closeEntry();
+        }
+    }
 }

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

[sling-org-apache-sling-commons-metrics-rrd4j] 08/09: [maven-release-plugin] prepare for next development iteration

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics-rrd4j.git

commit d4ad5b40a18704af5277ce982c51dc08652bef4b
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Tue Sep 5 13:56:50 2017 +0000

    [maven-release-plugin] prepare for next development iteration
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1807351 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index b0c2c1c..024e00a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
 
     <artifactId>org.apache.sling.commons.metrics-rrd4j</artifactId>
     <packaging>bundle</packaging>
-    <version>1.0.0</version>
+    <version>1.0.1-SNAPSHOT</version>
 
     <name>Apache Sling RRD4J metrics reporter</name>
     <description>
@@ -40,9 +40,9 @@
     </properties>
 
     <scm>
-        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/tags/org.apache.sling.commons.metrics-rrd4j-1.0.0</connection>
-        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/tags/org.apache.sling.commons.metrics-rrd4j-1.0.0</developerConnection>
-        <url>http://svn.apache.org/viewvc/sling/tags/org.apache.sling.commons.metrics-rrd4j-1.0.0</url>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/metrics-rrd4j</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/metrics-rrd4j</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics-rrd4j</url>
     </scm>
 
     <build>

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

[sling-org-apache-sling-commons-metrics-rrd4j] 07/09: [maven-release-plugin] prepare release org.apache.sling.commons.metrics-rrd4j-1.0.0

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics-rrd4j.git

commit 553c27669de444a2e2964fcba0f083b0cd2d949b
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Tue Sep 5 13:56:35 2017 +0000

    [maven-release-plugin] prepare release org.apache.sling.commons.metrics-rrd4j-1.0.0
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1807349 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index e424a12..b0c2c1c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
 
     <artifactId>org.apache.sling.commons.metrics-rrd4j</artifactId>
     <packaging>bundle</packaging>
-    <version>0.0.1-SNAPSHOT</version>
+    <version>1.0.0</version>
 
     <name>Apache Sling RRD4J metrics reporter</name>
     <description>
@@ -40,9 +40,9 @@
     </properties>
 
     <scm>
-        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/metrics-rrd4j</connection>
-        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/metrics-rrd4j</developerConnection>
-        <url>http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics-rrd4j</url>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/tags/org.apache.sling.commons.metrics-rrd4j-1.0.0</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/tags/org.apache.sling.commons.metrics-rrd4j-1.0.0</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/tags/org.apache.sling.commons.metrics-rrd4j-1.0.0</url>
     </scm>
 
     <build>

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

[sling-org-apache-sling-commons-metrics-rrd4j] 05/09: SLING-7074 - RRD4J improve configuration handling

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics-rrd4j.git

commit 8ad1d5c04bd638c05d4dd354ebc9fce86d7b1a87
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Thu Aug 24 14:51:33 2017 +0000

    SLING-7074 - RRD4J improve configuration handling
    
    Submitted-By: Alex Deparvu
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1806051 13f79535-47bb-0310-9956-ffa450edef68
---
 .../rrd4j/impl/CodahaleMetricsReporter.java        |  44 ++++--
 .../commons/metrics/rrd4j/impl/RRD4JReporter.java  | 164 ++++++++++++++++-----
 .../metrics/rrd4j/impl/InvalidParamsTest.java      | 152 +++++++++++++++++++
 3 files changed, 310 insertions(+), 50 deletions(-)

diff --git a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
index c0d96cb..fd874b0 100644
--- a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
+++ b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
@@ -49,6 +49,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
+import static org.apache.sling.commons.metrics.rrd4j.impl.RRD4JReporter.DEFAULT_PATH;
 import static org.apache.sling.commons.metrics.rrd4j.impl.RRD4JReporter.DEFAULT_STEP;
 
 @Component(
@@ -119,7 +120,7 @@ public class CodahaleMetricsReporter implements InventoryPrinter, ZipAttachmentP
                         "available, otherwise relative to the current working " +
                         "directory."
         )
-        String path() default "metrics/metrics.rrd";
+        String path() default DEFAULT_PATH;
     }
 
     private MetricRegistry metricRegistry = new MetricRegistry();
@@ -128,31 +129,44 @@ public class CodahaleMetricsReporter implements InventoryPrinter, ZipAttachmentP
     void activate(BundleContext context, Configuration config) throws Exception {
         LOG.info("Starting RRD4J Metrics reporter");
         configuration = config;
-        rrd = new File(config.path());
-        if (!rrd.isAbsolute()) {
-            String home = context.getProperty("sling.home");
-            if (home != null) {
-                rrd = new File(home, rrd.getPath());
-            }
-        }
-
+        rrd = getSafePath(context, config);
         reporter = RRD4JReporter.forRegistry(metricRegistry)
-                .convertRatesTo(TimeUnit.SECONDS)
-                .convertDurationsTo(TimeUnit.MICROSECONDS)
                 .withPath(rrd)
                 .withDatasources(config.datasources())
                 .withArchives(config.archives())
                 .withStep(config.step())
                 .build();
-        reporter.start(config.step(), TimeUnit.SECONDS);
-        LOG.info("Started RRD4J Metrics reporter. Writing to " + rrd);
+        if (reporter != null) {
+            reporter.start(config.step(), TimeUnit.SECONDS);
+            LOG.info("Started RRD4J Metrics reporter: {}.", reporter);
+        } else {
+            LOG.warn("Illegal config will not start the RRD reporter. [path={}, datasources={}, archives={}, step={}].",
+                    rrd.getPath(), config.datasources(), config.archives(), config.step());
+        }
+    }
+
+    private static File getSafePath(BundleContext context, Configuration config) {
+        String path = config.path();
+        if (path == null || path.isEmpty()) {
+            path = DEFAULT_PATH;
+        }
+        File rrd = new File(path);
+        if (!rrd.isAbsolute()) {
+            String home = context.getProperty("sling.home");
+            if (home != null) {
+                rrd = new File(home, rrd.getPath());
+            }
+        }
+        return rrd;
     }
 
     @Deactivate
     void deactivate() {
         LOG.info("Stopping RRD4J Metrics reporter");
-        reporter.stop();
-        reporter = null;
+        if (reporter != null) {
+            reporter.stop();
+            reporter = null;
+        }
         configuration = null;
         rrd = null;
         LOG.info("Stopped RRD4J Metrics reporter");
diff --git a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java
index bc27a40..a0caef4 100644
--- a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java
+++ b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java
@@ -27,6 +27,7 @@ import com.codahale.metrics.MetricRegistry;
 import com.codahale.metrics.ScheduledReporter;
 import com.codahale.metrics.Timer;
 
+import org.rrd4j.core.Archive;
 import org.rrd4j.core.RrdDb;
 import org.rrd4j.core.RrdDef;
 import org.rrd4j.core.Sample;
@@ -39,11 +40,12 @@ import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
 import java.util.SortedMap;
 import java.util.concurrent.TimeUnit;
 
@@ -56,6 +58,7 @@ class RRD4JReporter extends ScheduledReporter {
 
     private static final String PROPERTIES_SUFFIX = ".properties";
     static final int DEFAULT_STEP = 5;
+    static final String DEFAULT_PATH = "metrics/metrics.rrd";
 
     private final Map<String, Integer> dictionary = new HashMap<>();
     private final RrdDb rrdDB;
@@ -66,8 +69,8 @@ class RRD4JReporter extends ScheduledReporter {
 
     static class Builder {
         private MetricRegistry metricRegistry;
-        private TimeUnit ratesUnit;
-        private TimeUnit durationUnit;
+        private TimeUnit ratesUnit = TimeUnit.SECONDS;
+        private TimeUnit durationUnit = TimeUnit.MICROSECONDS;
         private File path = new File(".");
         private final List<String> indexedDS = new ArrayList<>();
         private final Map<String, Integer> dictionary = new HashMap<>();
@@ -79,35 +82,63 @@ class RRD4JReporter extends ScheduledReporter {
         }
 
         Builder withPath(File path) {
+            if (path == null) {
+                LOGGER.warn("Illegal path value, will use default({}).", DEFAULT_PATH);
+                path = new File(DEFAULT_PATH);
+            }
             this.path = path;
             return this;
         }
 
         Builder withDatasources(String[] datasources) {
+            if (datasources == null) {
+                datasources = new String[0];
+            }
+
             this.indexedDS.clear();
             this.dictionary.clear();
 
             int i = 0;
             for (String ds : datasources) {
                 String[] tokens = ds.split(":");
-                if (tokens.length != 6) {
-                    throw new IllegalArgumentException("Invalid data source definition: " + ds);
+                if (tokens.length == 6) {
+                    String key = normalize(tokens[1]);
+                    tokens[1] = String.valueOf(i);
+                    try {
+                        indexedDS.add(checkDataSource(join(":", tokens)));
+                        dictionary.put(key, i);
+                    } catch (IllegalArgumentException ex) {
+                        LOGGER.warn("Ignoring malformed datasource {}.", ds);
+                    }
+                } else {
+                    LOGGER.warn("Ignoring malformed datasource {}.", ds);
                 }
-                dictionary.put(normalize(tokens[1]), i);
-                tokens[1] = String.valueOf(i);
-                this.indexedDS.add(checkDataSource(join(":", tokens)));
                 i++;
             }
             return this;
         }
 
         Builder withArchives(String[] archives) {
+            if (archives == null) {
+                archives = new String[0];
+            }
             this.archives.clear();
-            this.archives.addAll(Arrays.asList(archives));
+
+            for (String archive : archives) {
+                try {
+                    this.archives.add(checkArchive(archive));
+                } catch (IllegalArgumentException ex) {
+                    LOGGER.warn("Ignoring malformed archive {}.", archive);
+                }
+            }
             return this;
         }
 
         Builder withStep(int step) {
+            if (step <= 0) {
+                LOGGER.warn("Illegal step value, will use default({}).", DEFAULT_STEP);
+                step = DEFAULT_STEP;
+            }
             this.step = step;
             return this;
         }
@@ -122,20 +153,26 @@ class RRD4JReporter extends ScheduledReporter {
             return this;
         }
 
-        ScheduledReporter build() throws IOException {
-            return new RRD4JReporter(metricRegistry, "RRD4JReporter",
-                    MetricFilter.ALL, ratesUnit, durationUnit, dictionary, createDef());
+        RRD4JReporter build() throws IOException {
+            if (indexedDS.isEmpty() || archives.isEmpty()) {
+                return null;
+            }
+            return new RRD4JReporter(metricRegistry, "RRD4JReporter", MetricFilter.ALL, ratesUnit, durationUnit,
+                    dictionary, createDef());
         }
 
-        private String checkDataSource(String ds)
-                throws IllegalArgumentException {
+        private String checkDataSource(String ds) throws IllegalArgumentException {
             new RrdDef("path").addDatasource(ds);
             return ds;
         }
 
+        private String checkArchive(String arch) throws IllegalArgumentException {
+            new RrdDef("path").addArchive(arch);
+            return arch;
+        }
+
         private RrdDef createDef() {
-            RrdDef def = new RrdDef(path.getPath());
-            def.setStep(step);
+            RrdDef def = new RrdDef(path.getPath(), step);
             for (String ds : indexedDS) {
                 def.addDatasource(ds);
             }
@@ -181,23 +218,23 @@ class RRD4JReporter extends ScheduledReporter {
         try {
             Sample sample = rrdDB.createSample(System.currentTimeMillis() / 1000);
             for (Map.Entry<String, Gauge> entry : gauges.entrySet()) {
-                reported += update(sample, indexForName(entry.getKey()), entry.getValue());
+                reported += update(sample, entry.getKey(), entry.getValue());
             }
 
             for (Map.Entry<String, Counter> entry : counters.entrySet()) {
-                reported += update(sample, indexForName(entry.getKey()), entry.getValue());
+                reported += update(sample, entry.getKey(), entry.getValue());
             }
 
             for (Map.Entry<String, Histogram> entry : histograms.entrySet()) {
-                reported += update(sample, indexForName(entry.getKey()), entry.getValue());
+                reported += update(sample, entry.getKey(), entry.getValue());
             }
 
             for (Map.Entry<String, Meter> entry : meters.entrySet()) {
-                reported += update(sample, indexForName(entry.getKey()), entry.getValue());
+                reported += update(sample, entry.getKey(), entry.getValue());
             }
 
             for (Map.Entry<String, Timer> entry : timers.entrySet()) {
-                reported += update(sample, indexForName(entry.getKey()), entry.getValue());
+                reported += update(sample, entry.getKey(), entry.getValue());
             }
             sample.update();
         } catch (IOException e) {
@@ -218,48 +255,68 @@ class RRD4JReporter extends ScheduledReporter {
         return name.replaceAll(":", "_");
     }
 
-    private int update(Sample sample, int nameIdx, Gauge g) {
+    private static void log(String key, String type, Number value) {
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Sample: {} ({}) = {}", key, type, value);
+        }
+    }
+
+    private int update(Sample sample, String key, Gauge g) {
+        int nameIdx = indexForName(key);
         if (nameIdx < 0) {
             return 0;
         }
         Object value = g.getValue();
         if (value instanceof Number) {
-            sample.setValue(nameIdx, ((Number) value).doubleValue());
+            double val = ((Number) value).doubleValue();
+            sample.setValue(nameIdx, val);
+            log(key, "gauge", val);
             return 1;
         }
         return 0;
     }
 
-    private int update(Sample sample, int nameIdx, Counter c) {
+    private int update(Sample sample, String key, Counter c) {
+        int nameIdx = indexForName(key);
         if (nameIdx < 0) {
             return 0;
         }
-        sample.setValue(nameIdx, c.getCount());
+        long val = c.getCount();
+        sample.setValue(nameIdx, val);
+        log(key, "counter", val);
         return 1;
     }
 
-    private int update(Sample sample, int nameIdx, Histogram h) {
+    private int update(Sample sample, String key, Histogram h) {
+        int nameIdx = indexForName(key);
         if (nameIdx < 0) {
             return 0;
         }
-        sample.setValue(nameIdx, h.getCount());
+        long val = h.getCount();
+        sample.setValue(nameIdx, val);
+        log(key, "histogram", val);
         return 1;
     }
 
-    private int update(Sample sample, int nameIdx, Timer t) {
+    private int update(Sample sample, String key, Timer t) {
+        int nameIdx = indexForName(key);
         if (nameIdx < 0) {
             return 0;
         }
-        sample.setValue(nameIdx, t.getCount());
+        long val = t.getCount();
+        sample.setValue(nameIdx, val);
+        log(key, "timer", val);
         return 1;
     }
 
-
-    private int update(Sample sample, int nameIdx, Meter m) {
+    private int update(Sample sample, String key, Meter m) {
+        int nameIdx = indexForName(key);
         if (nameIdx < 0) {
             return 0;
         }
-        sample.setValue(nameIdx, m.getCount());
+        long val = m.getCount();
+        sample.setValue(nameIdx, val);
+        log(key, "meter", val);
         return 1;
     }
 
@@ -277,7 +334,7 @@ class RRD4JReporter extends ScheduledReporter {
         }
     }
 
-    private RrdDb createDB(RrdDef definition) throws IOException {
+    private static RrdDb createDB(RrdDef definition) throws IOException {
         File dbFile = new File(definition.getPath());
         if (!dbFile.getParentFile().exists()) {
             if (!dbFile.getParentFile().mkdirs()) {
@@ -302,7 +359,7 @@ class RRD4JReporter extends ScheduledReporter {
         return db;
     }
 
-    private File renameDB(File dbFile) throws IOException {
+    private static File renameDB(File dbFile) throws IOException {
         // find a suitable suffix
         int idx = 0;
         while (new File(dbFile.getPath() + suffix(idx)).exists()) {
@@ -321,7 +378,7 @@ class RRD4JReporter extends ScheduledReporter {
         return "." + idx;
     }
 
-    private void rename(Path path, String newName) throws IOException {
+    private static void rename(Path path, String newName) throws IOException {
         if (!Files.exists(path)) {
             // nothing to rename
             return;
@@ -329,4 +386,41 @@ class RRD4JReporter extends ScheduledReporter {
         Path target = path.resolveSibling(newName);
         Files.move(path, target, REPLACE_EXISTING);
     }
+
+    long getStep() {
+        try {
+            return rrdDB.getHeader().getStep();
+        } catch (IOException e) {
+            LOGGER.error(e.getMessage(), e);
+        }
+        return -1;
+    }
+
+    String getPath() {
+        try {
+            return rrdDB.getCanonicalPath();
+        } catch (IOException e) {
+            LOGGER.error(e.getMessage(), e);
+        }
+        return "";
+    }
+
+    Set<String> getDatasources() {
+        return dictionary.keySet();
+    }
+
+    Set<String> getArchives() {
+        Set<String> archives = new HashSet<>();
+        for (int i = 0; i < rrdDB.getArcCount(); i++) {
+            Archive ar = rrdDB.getArchive(i);
+            archives.add(ar.toString());
+        }
+        return archives;
+    }
+
+    @Override
+    public String toString() {
+        return "RRD4JReporter [path=" + getPath() + ", datasources=" + getDatasources() + ", archives=" + getArchives()
+                + ", step=" + getStep() + ", dictionary=" + dictionary + "]";
+    }
 }
diff --git a/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/InvalidParamsTest.java b/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/InvalidParamsTest.java
new file mode 100644
index 0000000..3a69b43
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/InvalidParamsTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.rrd4j.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.util.Set;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import com.codahale.metrics.MetricRegistry;
+
+public class InvalidParamsTest {
+
+    @Rule
+    public TemporaryFolder folder = new TemporaryFolder(new File("target"));
+
+    private MetricRegistry registry = new MetricRegistry();
+
+    @Test
+    public void badStep() throws Exception {
+        File rrd = new File(folder.newFolder("InvalidParamsTest"), "metrics.rrd");
+        assertFalse(rrd.exists());
+
+        int step = -1;
+        String[] datasources = { "DS:oak_SESSION_LOGIN_COUNTER:COUNTER:300:0:U" };
+        String[] archives = { "RRA:AVERAGE:0.5:12:360" };
+
+        RRD4JReporter reporter = RRD4JReporter.forRegistry(registry).withPath(rrd).withDatasources(datasources)
+                .withArchives(archives).withStep(step).build();
+        assertNotNull(reporter);
+        assertTrue(rrd.exists());
+        assertTrue(reporter.getStep() == 5);
+
+        Set<String> ds = reporter.getDatasources();
+        assertEquals(1, ds.size());
+        assertEquals("oak_SESSION_LOGIN_COUNTER", ds.iterator().next());
+    }
+
+    @Test
+    public void noDatasource() throws Exception {
+        File rrd = new File(folder.newFolder("InvalidParamsTest"), "metrics.rrd");
+        assertFalse(rrd.exists());
+
+        String[] datasources = null;
+        String[] archives = { "RRA:AVERAGE:0.5:12:360" };
+
+        RRD4JReporter reporter = RRD4JReporter.forRegistry(registry).withPath(rrd).withDatasources(datasources)
+                .withArchives(archives).build();
+        assertNull(reporter);
+        assertFalse(rrd.exists());
+    }
+
+    @Test
+    public void badDatasource() throws Exception {
+        File rrd = new File(folder.newFolder("InvalidParamsTest"), "metrics.rrd");
+        assertFalse(rrd.exists());
+
+        String[] datasources = { "malformed:input" };
+        String[] archives = { "RRA:AVERAGE:0.5:12:360" };
+
+        RRD4JReporter reporter = RRD4JReporter.forRegistry(registry).withPath(rrd).withDatasources(datasources)
+                .withArchives(archives).build();
+        assertNull(reporter);
+        assertFalse(rrd.exists());
+    }
+
+    @Test
+    public void badDatasourceFilter() throws Exception {
+        File rrd = new File(folder.newFolder("InvalidParamsTest"), "metrics.rrd");
+        assertFalse(rrd.exists());
+
+        String[] datasources = { "DS:oak_SESSION_LOGIN_COUNTER:COUNTER:300:0:U", "malformed:input" };
+        String[] archives = { "RRA:AVERAGE:0.5:12:360" };
+
+        RRD4JReporter reporter = RRD4JReporter.forRegistry(registry).withPath(rrd).withDatasources(datasources)
+                .withArchives(archives).build();
+        assertNotNull(reporter);
+        assertTrue(rrd.exists());
+
+        Set<String> ds = reporter.getDatasources();
+        assertEquals(1, ds.size());
+        assertEquals("oak_SESSION_LOGIN_COUNTER", ds.iterator().next());
+    }
+
+    @Test
+    public void noArchive() throws Exception {
+        File rrd = new File(folder.newFolder("InvalidParamsTest"), "metrics.rrd");
+        assertFalse(rrd.exists());
+
+        String[] datasources = { "DS:oak_SESSION_LOGIN_COUNTER:COUNTER:300:0:U" };
+        String[] archives = null;
+
+        RRD4JReporter reporter = RRD4JReporter.forRegistry(registry).withPath(rrd).withDatasources(datasources)
+                .withArchives(archives).build();
+        assertNull(reporter);
+        assertFalse(rrd.exists());
+    }
+
+    @Test
+    public void badArchive() throws Exception {
+        File rrd = new File(folder.newFolder("InvalidParamsTest"), "metrics.rrd");
+        assertFalse(rrd.exists());
+
+        String[] datasources = { "DS:oak_SESSION_LOGIN_COUNTER:COUNTER:300:0:U" };
+        String[] archives = { "malformed:input" };
+
+        RRD4JReporter reporter = RRD4JReporter.forRegistry(registry).withPath(rrd).withDatasources(datasources)
+                .withArchives(archives).build();
+        assertNull(reporter);
+        assertFalse(rrd.exists());
+    }
+
+    @Test
+    public void badArchiveFilter() throws Exception {
+        File rrd = new File(folder.newFolder("InvalidParamsTest"), "metrics.rrd");
+        assertFalse(rrd.exists());
+
+        String[] datasources = { "DS:oak_SESSION_LOGIN_COUNTER:COUNTER:300:0:U" };
+        String[] archives = { "RRA:AVERAGE:0.5:12:360", "malformed:input" };
+
+        RRD4JReporter reporter = RRD4JReporter.forRegistry(registry).withPath(rrd).withDatasources(datasources)
+                .withArchives(archives).build();
+        assertNotNull(reporter);
+        assertTrue(rrd.exists());
+        Set<String> archs = reporter.getArchives();
+        assertEquals(1, archs.size());
+    }
+}

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

[sling-org-apache-sling-commons-metrics-rrd4j] 03/09: SLING-7071 - Rename metrics file on configuration change

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-metrics-rrd4j.git

commit a00a09f49f635e983ac63d89dda040129edc1a05
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Tue Aug 22 09:07:03 2017 +0000

    SLING-7071 - Rename metrics file on configuration change
    
    Submitted-By: Marcel Reutegger
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1805734 13f79535-47bb-0310-9956-ffa450edef68
---
 .../commons/metrics/rrd4j/impl/RRD4JReporter.java  | 87 ++++++++++++++++------
 .../commons/metrics/rrd4j/impl/ReporterTest.java   | 33 ++++++--
 2 files changed, 92 insertions(+), 28 deletions(-)

diff --git a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java
index 8eaacfa..bc27a40 100644
--- a/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java
+++ b/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java
@@ -36,6 +36,8 @@ import org.slf4j.LoggerFactory;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -46,10 +48,13 @@ import java.util.SortedMap;
 import java.util.concurrent.TimeUnit;
 
 import static java.lang.String.join;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 
 class RRD4JReporter extends ScheduledReporter {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(RRD4JReporter.class);
+
+    private static final String PROPERTIES_SUFFIX = ".properties";
     static final int DEFAULT_STEP = 5;
 
     private final Map<String, Integer> dictionary = new HashMap<>();
@@ -151,7 +156,7 @@ class RRD4JReporter extends ScheduledReporter {
         super(registry, name, filter, rateUnit, durationUnit);
         this.dictionary.putAll(dictionary);
         this.rrdDB = createDB(rrdDef);
-        storeDictionary(rrdDef.getPath() + ".properties");
+        storeDictionary(rrdDef.getPath() + PROPERTIES_SUFFIX);
     }
 
     @Override
@@ -170,31 +175,37 @@ class RRD4JReporter extends ScheduledReporter {
                        SortedMap<String, Histogram> histograms,
                        SortedMap<String, Meter> meters,
                        SortedMap<String, Timer> timers) {
-
+        long time = System.nanoTime();
+        int total = gauges.size() + counters.size() + histograms.size() + meters.size() + timers.size();
+        int reported = 0;
         try {
             Sample sample = rrdDB.createSample(System.currentTimeMillis() / 1000);
             for (Map.Entry<String, Gauge> entry : gauges.entrySet()) {
-                update(sample, indexForName(entry.getKey()), entry.getValue());
+                reported += update(sample, indexForName(entry.getKey()), entry.getValue());
             }
 
             for (Map.Entry<String, Counter> entry : counters.entrySet()) {
-                update(sample, indexForName(entry.getKey()), entry.getValue());
+                reported += update(sample, indexForName(entry.getKey()), entry.getValue());
             }
 
             for (Map.Entry<String, Histogram> entry : histograms.entrySet()) {
-                update(sample, indexForName(entry.getKey()), entry.getValue());
+                reported += update(sample, indexForName(entry.getKey()), entry.getValue());
             }
 
             for (Map.Entry<String, Meter> entry : meters.entrySet()) {
-                update(sample, indexForName(entry.getKey()), entry.getValue());
+                reported += update(sample, indexForName(entry.getKey()), entry.getValue());
             }
 
             for (Map.Entry<String, Timer> entry : timers.entrySet()) {
-                update(sample, indexForName(entry.getKey()), entry.getValue());
+                reported += update(sample, indexForName(entry.getKey()), entry.getValue());
             }
             sample.update();
         } catch (IOException e) {
             LOGGER.warn("Unable to write sample to RRD", e);
+        } finally {
+            time = System.nanoTime() - time;
+            LOGGER.debug("{} out of {} metrics reported in {} \u03bcs",
+                    reported, total, TimeUnit.NANOSECONDS.toMicros(time));
         }
     }
 
@@ -207,44 +218,49 @@ class RRD4JReporter extends ScheduledReporter {
         return name.replaceAll(":", "_");
     }
 
-    private void update(Sample sample, int nameIdx, Gauge g) {
+    private int update(Sample sample, int nameIdx, Gauge g) {
         if (nameIdx < 0) {
-            return;
+            return 0;
         }
         Object value = g.getValue();
         if (value instanceof Number) {
             sample.setValue(nameIdx, ((Number) value).doubleValue());
+            return 1;
         }
+        return 0;
     }
 
-    private void update(Sample sample, int nameIdx, Counter c) {
+    private int update(Sample sample, int nameIdx, Counter c) {
         if (nameIdx < 0) {
-            return;
+            return 0;
         }
         sample.setValue(nameIdx, c.getCount());
+        return 1;
     }
 
-    private void update(Sample sample, int nameIdx, Histogram h) {
+    private int update(Sample sample, int nameIdx, Histogram h) {
         if (nameIdx < 0) {
-            return;
+            return 0;
         }
         sample.setValue(nameIdx, h.getCount());
+        return 1;
     }
 
-    private void update(Sample sample, int nameIdx, Timer t) {
+    private int update(Sample sample, int nameIdx, Timer t) {
         if (nameIdx < 0) {
-            return;
+            return 0;
         }
         sample.setValue(nameIdx, t.getCount());
+        return 1;
     }
 
 
-    private void update(Sample sample, int nameIdx, Meter m) {
+    private int update(Sample sample, int nameIdx, Meter m) {
         if (nameIdx < 0) {
-            return;
+            return 0;
         }
-        LOGGER.debug("Sample: {} = {}", nameIdx, m.getCount());
         sample.setValue(nameIdx, m.getCount());
+        return 1;
     }
 
     private void storeDictionary(String path) throws IOException {
@@ -274,10 +290,9 @@ class RRD4JReporter extends ScheduledReporter {
             if (!db.getRrdDef().equals(definition)) {
                 // definition changed -> re-create DB
                 db.close();
-                if (!dbFile.delete()) {
-                    throw new IOException("Unable to delete RRD file: " + dbFile.getPath());
-                }
-                LOGGER.warn("Configuration changed, recreating RRD file for metrics: " + dbFile.getPath());
+                File renamed = renameDB(dbFile);
+                LOGGER.info("Configuration changed, renamed existing RRD file to: {}",
+                        renamed.getPath());
                 db = null;
             }
         }
@@ -286,4 +301,32 @@ class RRD4JReporter extends ScheduledReporter {
         }
         return db;
     }
+
+    private File renameDB(File dbFile) throws IOException {
+        // find a suitable suffix
+        int idx = 0;
+        while (new File(dbFile.getPath() + suffix(idx)).exists()) {
+            idx++;
+        }
+        // rename rrd file
+        rename(dbFile.toPath(), dbFile.getName() + suffix(idx));
+        // rename properties file
+        rename(dbFile.toPath().resolveSibling(dbFile.getName() + PROPERTIES_SUFFIX),
+                dbFile.getName() + suffix(idx) + PROPERTIES_SUFFIX);
+
+        return new File(dbFile.getParentFile(), dbFile.getName() + suffix(idx));
+    }
+
+    private static String suffix(int idx) {
+        return "." + idx;
+    }
+
+    private void rename(Path path, String newName) throws IOException {
+        if (!Files.exists(path)) {
+            // nothing to rename
+            return;
+        }
+        Path target = path.resolveSibling(newName);
+        Files.move(path, target, REPLACE_EXISTING);
+    }
 }
diff --git a/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java b/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java
index 2dffcf9..00f5a8e 100644
--- a/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java
+++ b/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java
@@ -25,19 +25,21 @@ import java.util.Map;
 import com.codahale.metrics.Gauge;
 import com.codahale.metrics.MetricRegistry;
 
-import org.apache.sling.commons.metrics.rrd4j.impl.CodahaleMetricsReporter;
+import org.apache.sling.testing.mock.osgi.MockOsgi;
 import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.rrd4j.core.RrdDb;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 public class ReporterTest {
 
     private static final File RRD = new File(new File("target", "metrics"), "metrics.rrd");
+    private static final File RRD_0 = new File(new File("target", "metrics"), "metrics.rrd.0");
 
     private static final long TEST_VALUE = 42;
 
@@ -51,13 +53,10 @@ public class ReporterTest {
     @Before
     public void before() throws Exception {
         RRD.delete();
+        RRD_0.delete();
         context.registerService(MetricRegistry.class, registry, "name", "sling");
 
-        Map<String, Object> properties = new HashMap<>();
-        properties.put("step", 1L);
-        properties.put("datasources", new String[]{"DS:sling_myMetric:GAUGE:300:0:U"});
-        properties.put("archives", new String[]{"RRA:AVERAGE:0.5:1:60"});
-        properties.put("path", RRD.getPath());
+        Map<String, Object> properties = newConfig();
         context.registerInjectActivateService(reporter, properties);
 
         registry.register("myMetric", new TestGauge(TEST_VALUE));
@@ -81,6 +80,19 @@ public class ReporterTest {
         fail("RRD4J reporter did not update database in time");
     }
 
+    @Test
+    public void reconfigure() throws Exception {
+        assertFalse(RRD_0.exists());
+        MockOsgi.deactivate(reporter, context.bundleContext());
+
+        // re-activate with changed configuration
+        Map<String, Object> properties = newConfig();
+        properties.put("step", 5L);
+        MockOsgi.activate(reporter, context.bundleContext(), properties);
+
+        assertTrue(RRD_0.exists());
+    }
+
     private static final class TestGauge implements Gauge<Long> {
 
         private final long value;
@@ -94,4 +106,13 @@ public class ReporterTest {
             return value;
         }
     }
+
+    private static Map<String, Object> newConfig() {
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("step", 1L);
+        properties.put("datasources", new String[]{"DS:sling_myMetric:GAUGE:300:0:U"});
+        properties.put("archives", new String[]{"RRA:AVERAGE:0.5:1:60"});
+        properties.put("path", RRD.getPath());
+        return properties;
+    }
 }

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