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/08/18 08:47:34 UTC
svn commit: r1805386 - in /sling/trunk/bundles/commons/metrics-rrd4j: ./
src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/
src/main/java/org/apache/sling/ src/main/java/org/apache/sling/commons/
src/main/java/org/apache/sling/c...
Author: rombert
Date: Fri Aug 18 08:47:34 2017
New Revision: 1805386
URL: http://svn.apache.org/viewvc?rev=1805386&view=rev
Log:
SLING-7055 - RRD4J metrics reporter
Initial implementation
Submitted-By: Marcel Reutegger
Added:
sling/trunk/bundles/commons/metrics-rrd4j/
sling/trunk/bundles/commons/metrics-rrd4j/README.md
sling/trunk/bundles/commons/metrics-rrd4j/pom.xml
sling/trunk/bundles/commons/metrics-rrd4j/src/
sling/trunk/bundles/commons/metrics-rrd4j/src/main/
sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/
sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/
sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/
sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/
sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/
sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/
sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/
sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/
sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CopyMetricRegistryListener.java
sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java
sling/trunk/bundles/commons/metrics-rrd4j/src/test/
sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/
sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/
sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/apache/
sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/apache/sling/
sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/apache/sling/commons/
sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/apache/sling/commons/metrics/
sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/apache/sling/commons/metrics/rrd4j/
sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/
sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java
sling/trunk/bundles/commons/metrics-rrd4j/src/test/resources/
sling/trunk/bundles/commons/metrics-rrd4j/src/test/resources/logback-test.xml
Added: sling/trunk/bundles/commons/metrics-rrd4j/README.md
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics-rrd4j/README.md?rev=1805386&view=auto
==============================================================================
--- sling/trunk/bundles/commons/metrics-rrd4j/README.md (added)
+++ sling/trunk/bundles/commons/metrics-rrd4j/README.md Fri Aug 18 08:47:34 2017
@@ -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
Added: sling/trunk/bundles/commons/metrics-rrd4j/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics-rrd4j/pom.xml?rev=1805386&view=auto
==============================================================================
--- sling/trunk/bundles/commons/metrics-rrd4j/pom.xml (added)
+++ sling/trunk/bundles/commons/metrics-rrd4j/pom.xml Fri Aug 18 08:47:34 2017
@@ -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>
Added: sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java?rev=1805386&view=auto
==============================================================================
--- sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java (added)
+++ sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CodahaleMetricsReporter.java Fri Aug 18 08:47:34 2017
@@ -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);
+ }
+}
Added: sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CopyMetricRegistryListener.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CopyMetricRegistryListener.java?rev=1805386&view=auto
==============================================================================
--- sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CopyMetricRegistryListener.java (added)
+++ sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/CopyMetricRegistryListener.java Fri Aug 18 08:47:34 2017
@@ -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);
+ }
+
+}
Added: sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java?rev=1805386&view=auto
==============================================================================
--- sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java (added)
+++ sling/trunk/bundles/commons/metrics-rrd4j/src/main/java/org/apache/sling/commons/metrics/rrd4j/impl/RRD4JReporter.java Fri Aug 18 08:47:34 2017
@@ -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;
+ }
+}
Added: sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java?rev=1805386&view=auto
==============================================================================
--- sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java (added)
+++ sling/trunk/bundles/commons/metrics-rrd4j/src/test/java/org/apache/sling/commons/metrics/rrd4j/impl/ReporterTest.java Fri Aug 18 08:47:34 2017
@@ -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;
+ }
+ }
+}
Added: sling/trunk/bundles/commons/metrics-rrd4j/src/test/resources/logback-test.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/commons/metrics-rrd4j/src/test/resources/logback-test.xml?rev=1805386&view=auto
==============================================================================
--- sling/trunk/bundles/commons/metrics-rrd4j/src/test/resources/logback-test.xml (added)
+++ sling/trunk/bundles/commons/metrics-rrd4j/src/test/resources/logback-test.xml Fri Aug 18 08:47:34 2017
@@ -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>