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 2020/03/17 10:28:51 UTC

[sling-whiteboard] branch master updated: metrics-osgi: add a JSON writer for metrics

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-whiteboard.git


The following commit(s) were added to refs/heads/master by this push:
     new d4c51d6  metrics-osgi: add a JSON writer for metrics
d4c51d6 is described below

commit d4c51d69a93f00a67493a1108762b5f435baa79b
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Tue Mar 17 11:28:27 2020 +0100

    metrics-osgi: add a JSON writer for metrics
---
 osgi-metrics/consumers/bnd.bnd                     |  1 +
 osgi-metrics/consumers/pom.xml                     | 13 +++
 .../impl/json/JsonWritingMetricsListener.java      | 95 ++++++++++++++++++++++
 .../impl/json/JsonWritingMetricsListenerTest.java  | 77 ++++++++++++++++++
 4 files changed, 186 insertions(+)

diff --git a/osgi-metrics/consumers/bnd.bnd b/osgi-metrics/consumers/bnd.bnd
new file mode 100644
index 0000000..0be6687
--- /dev/null
+++ b/osgi-metrics/consumers/bnd.bnd
@@ -0,0 +1 @@
+Conditional-Package: org.apache.felix.utils.json
\ No newline at end of file
diff --git a/osgi-metrics/consumers/pom.xml b/osgi-metrics/consumers/pom.xml
index 21e90a5..8f47241 100644
--- a/osgi-metrics/consumers/pom.xml
+++ b/osgi-metrics/consumers/pom.xml
@@ -85,6 +85,19 @@
             <version>0.1.0-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+             <artifactId>org.apache.felix.utils</artifactId>
+             <version>1.11.4</version>
+             <scope>provided</scope>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>3.3.3</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/osgi-metrics/consumers/src/main/java/org/apache/sling/metrics/osgi/consumers/impl/json/JsonWritingMetricsListener.java b/osgi-metrics/consumers/src/main/java/org/apache/sling/metrics/osgi/consumers/impl/json/JsonWritingMetricsListener.java
new file mode 100644
index 0000000..75f8e19
--- /dev/null
+++ b/osgi-metrics/consumers/src/main/java/org/apache/sling/metrics/osgi/consumers/impl/json/JsonWritingMetricsListener.java
@@ -0,0 +1,95 @@
+/*
+ * 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.metrics.osgi.consumers.impl.json;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.felix.utils.json.JSONWriter;
+import org.apache.sling.metrics.osgi.BundleStartDuration;
+import org.apache.sling.metrics.osgi.ServiceRestartCounter;
+import org.apache.sling.metrics.osgi.StartupMetrics;
+import org.apache.sling.metrics.osgi.StartupMetricsListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+public class JsonWritingMetricsListener implements StartupMetricsListener {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+    
+    private BundleContext ctx;
+
+    @Activate
+    protected void activate(BundleContext ctx) {
+        this.ctx = ctx;
+    }
+
+    @Override
+    public void onStartupComplete(StartupMetrics metrics) {
+
+        File metricsFile = ctx.getDataFile("startup-metrics-" + System.currentTimeMillis() + ".json");
+        if ( metricsFile == null ) {
+            logger.warn("Unable to get data file in the bundle area, startup metrics will not be written");
+            return;
+        }
+        
+        try {
+            try ( FileWriter fw = new FileWriter(metricsFile)) {
+                JSONWriter w = new JSONWriter(fw);
+                w.object();
+                // application metrics
+                w.key("application");
+                w.object();
+                w.key("startTimeMillis").value(metrics.getJvmStartup().toEpochMilli());
+                w.key("startDurationMillis").value(metrics.getStartupTime().toMillis());
+                w.endObject();
+                
+                // bundle metrics
+                w.key("bundles");
+                w.array();
+                for ( BundleStartDuration bsd : metrics.getBundleStartDurations() ) {
+                    w.object();
+                    w.key("symbolicName").value(bsd.getSymbolicName());
+                    w.key("startTimeMillis").value(bsd.getStartingAt().toEpochMilli());
+                    w.key("startDurationMillis").value(bsd.getStartedAfter().toMillis());
+                    w.endObject();
+                }
+                w.endArray();
+                
+                // service metrics
+                w.key("services");
+                w.array();
+                for ( ServiceRestartCounter src : metrics.getServiceRestarts() ) {
+                    w.object();
+                    w.key("identifier").value(src.getServiceIdentifier());
+                    w.key("restarts").value(src.getServiceRestarts());
+                    w.endObject();
+                }
+                w.endArray();
+                
+                w.endObject();
+            }
+        } catch (IOException e) {
+            logger.warn("Failed wrting startup metrics", e);
+        }
+    }
+}
diff --git a/osgi-metrics/consumers/src/test/java/org/apache/sling/metrics/osgi/consumers/impl/json/JsonWritingMetricsListenerTest.java b/osgi-metrics/consumers/src/test/java/org/apache/sling/metrics/osgi/consumers/impl/json/JsonWritingMetricsListenerTest.java
new file mode 100644
index 0000000..c117585
--- /dev/null
+++ b/osgi-metrics/consumers/src/test/java/org/apache/sling/metrics/osgi/consumers/impl/json/JsonWritingMetricsListenerTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.metrics.osgi.consumers.impl.json;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Arrays;
+
+import org.apache.felix.utils.json.JSONParser;
+import org.apache.sling.metrics.osgi.BundleStartDuration;
+import org.apache.sling.metrics.osgi.ServiceRestartCounter;
+import org.apache.sling.metrics.osgi.StartupMetrics;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.Mockito;
+import org.osgi.framework.BundleContext;
+
+public class JsonWritingMetricsListenerTest {
+
+    @Rule
+    public TemporaryFolder tmp = new TemporaryFolder();
+    
+    @Test
+    public void metricsArePersisted() throws IOException {
+        // bridge the mock bundle context with the temporary folder
+        BundleContext mockBundleContext = mock(BundleContext.class);
+        when(mockBundleContext.getDataFile(Mockito.anyString())).thenAnswer( i -> tmp.newFile(i.getArgument(0, String.class)));
+        
+        JsonWritingMetricsListener listener = new JsonWritingMetricsListener();
+        listener.activate(mockBundleContext);
+        
+        StartupMetrics metrics = StartupMetrics.Builder
+                .withJvmStartup(Instant.now())
+                .withStartupTime(Duration.ofMillis(50))
+                .withBundleStartDurations(Arrays.asList(new BundleStartDuration("foo", Instant.now(), Duration.ofMillis(5))))
+                .withServiceRestarts(Arrays.asList(new ServiceRestartCounter("some.service", 1)))
+                .build();
+        
+        listener.onStartupComplete(metrics);
+        
+        File[] files = tmp.getRoot().listFiles();
+        
+        assertThat("Bundle data area should hold one file", files.length, equalTo(1));
+        
+        File metricsFile = files[0];
+        try ( FileInputStream fis = new FileInputStream(metricsFile)) {
+            JSONParser p = new JSONParser(fis);
+            assertThat(p.getParsed().keySet(), hasItem("application"));
+            assertThat(p.getParsed().keySet(), hasItem("bundles"));
+            assertThat(p.getParsed().keySet(), hasItem("services"));
+        }
+    }
+}