ARTEMIS-4655 report logging metrics

It may be useful to configure alerts for ERROR or WARN events in the log
which may go unnoticed otherwise.
This commit is contained in:
Justin Bertram 2024-02-22 15:25:05 -06:00 committed by Robbie Gemmell
parent b5791344b4
commit 661a4e6fdc
13 changed files with 152 additions and 1 deletions

View File

@ -676,6 +676,9 @@ public final class ActiveMQDefaultConfiguration {
// Whether or not to report uptime metrics
private static final boolean DEFAULT_UPTIME_METRICS = false;
// Whether or not to report logging metrics
private static final boolean DEFAULT_LOGGING_METRICS = false;
// How often (in ms) to scan for expired MQTT sessions
private static long DEFAULT_MQTT_SESSION_SCAN_INTERVAL = 500;
@ -1865,6 +1868,13 @@ public final class ActiveMQDefaultConfiguration {
return DEFAULT_UPTIME_METRICS;
}
/**
* Whether or not to report logging metrics
*/
public static Boolean getDefaultLoggingMetrics() {
return DEFAULT_LOGGING_METRICS;
}
/**
* How often (in ms) to scan for expired MQTT sessions
*/

View File

@ -30,6 +30,7 @@ public class MetricsConfiguration implements Serializable {
private boolean fileDescriptors = ActiveMQDefaultConfiguration.getDefaultFileDescriptorsMetrics();
private boolean processor = ActiveMQDefaultConfiguration.getDefaultProcessorMetrics();
private boolean uptime = ActiveMQDefaultConfiguration.getDefaultUptimeMetrics();
private boolean logging = ActiveMQDefaultConfiguration.getDefaultLoggingMetrics();
private ActiveMQMetricsPlugin plugin;
public boolean isJvmMemory() {
@ -95,6 +96,15 @@ public class MetricsConfiguration implements Serializable {
return this;
}
public boolean isLogging() {
return logging;
}
public MetricsConfiguration setLogging(boolean logging) {
this.logging = logging;
return this;
}
public ActiveMQMetricsPlugin getPlugin() {
return plugin;
}

View File

@ -1012,6 +1012,8 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
metricsConfiguration.setProcessor(XMLUtil.parseBoolean(child));
} else if (child.getNodeName().equals("uptime")) {
metricsConfiguration.setUptime(XMLUtil.parseBoolean(child));
} else if (child.getNodeName().equals("logging")) {
metricsConfiguration.setLogging(XMLUtil.parseBoolean(child));
} else if (child.getNodeName().equals("plugin")) {
metricsConfiguration.setPlugin(parseMetricsPlugin(child, config));
}

View File

@ -32,6 +32,7 @@ import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.logging.Log4j2Metrics;
import io.micrometer.core.instrument.binder.system.FileDescriptorMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
@ -86,6 +87,9 @@ public class MetricsManager {
if (metricsConfiguration.isUptime()) {
new UptimeMetrics().bindTo(meterRegistry);
}
if (metricsConfiguration.isLogging()) {
new Log4j2Metrics().bindTo(meterRegistry);
}
}
}

View File

@ -4836,6 +4836,14 @@
</xsd:annotation>
</xsd:element>
<xsd:element name="logging" type="xsd:boolean" default="false" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
whether or not to report logging metrics
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="plugin" maxOccurs="1" minOccurs="0">
<xsd:complexType>
<xsd:annotation>

View File

@ -147,6 +147,8 @@ public class DefaultsFileConfigurationTest extends ConfigurationImplTest {
Assert.assertEquals(ActiveMQDefaultConfiguration.getDefaultProcessorMetrics(), conf.getMetricsConfiguration().isProcessor());
Assert.assertEquals(ActiveMQDefaultConfiguration.getDefaultUptimeMetrics(), conf.getMetricsConfiguration().isUptime());
Assert.assertEquals(ActiveMQDefaultConfiguration.getDefaultLoggingMetrics(), conf.getMetricsConfiguration().isLogging());
}
// Protected ---------------------------------------------------------------------------------------------

View File

@ -584,6 +584,7 @@ public class FileConfigurationTest extends ConfigurationImplTest {
assertTrue(metricsConfiguration.isFileDescriptors());
assertTrue(metricsConfiguration.isProcessor());
assertTrue(metricsConfiguration.isUptime());
assertTrue(metricsConfiguration.isLogging());
}
private void verifyAddresses() {

View File

@ -384,6 +384,7 @@
<file-descriptors>true</file-descriptors>
<processor>true</processor>
<uptime>true</uptime>
<logging>true</logging>
<plugin class-name="org.apache.activemq.artemis.core.server.metrics.plugins.SimpleMetricsPlugin">
<property key="foo" value="x"/>
<property key="bar" value="y"/>

View File

@ -265,6 +265,7 @@
<file-descriptors>true</file-descriptors>
<processor>true</processor>
<uptime>true</uptime>
<logging>true</logging>
<plugin class-name="org.apache.activemq.artemis.core.server.metrics.plugins.SimpleMetricsPlugin">
<property key="foo" value="x"/>
<property key="bar" value="y"/>

View File

@ -22,6 +22,7 @@
<file-descriptors>true</file-descriptors>
<processor>true</processor>
<uptime>true</uptime>
<logging>true</logging>
<plugin class-name="org.apache.activemq.artemis.core.server.metrics.plugins.SimpleMetricsPlugin">
<property key="foo" value="x"/>
<property key="bar" value="y"/>

View File

@ -27,6 +27,7 @@
<file-descriptors>true</file-descriptors>
<processor>true</processor>
<uptime>true</uptime>
<logging>true</logging>
<plugin class-name="org.apache.activemq.artemis.core.config.impl.FileConfigurationTest$FakeMetricPlugin">
<property key="key1" value="value1"/>
<property key="key2" value="value2"/>

View File

@ -118,13 +118,23 @@ Uptime::
Gauges process start time and uptime.
+
Disabled by default.
Logging::
Counts the number of logging events per logging category (e.g. `WARN`, `ERROR`, etc.).
+
Disabled by default.
+
[WARNING]
====
This works _exclusively_ with Log4j2 (i.e the default logging implementation shipped with the broker).
If you're embedding the broker and using a different logging implementation (e.g. Log4j 1.x, JUL, Logback, etc.) and you enable these metrics then the broker will fail to start with a `java.lang.NoClassDefFoundError` as it attempts to locate Log4j2 classes that don't exist on the classpath.
====
== Configuration
Metrics for all addresses and queues are enabled by default.
If you want to disable metrics for a particular address or set of addresses you can do so by setting the `enable-metrics` `address-setting` to `false`.
In `broker.xml` use the `metrics` element to configure which JVM metrics are reported and to configure the plugin itself.
In `broker.xml` use the `metrics` element to configure which general broker and JVM metrics are reported and to configure the plugin itself.
Here's a configuration with all optional metrics:
[,xml]
@ -137,6 +147,7 @@ Here's a configuration with all optional metrics:
<file-descriptors>true</file-descriptors> <!-- defaults to false -->
<processor>true</processor> <!-- defaults to false -->
<uptime>true</uptime> <!-- defaults to false -->
<logging>true</logging> <!-- defaults to false -->
<plugin class-name="org.apache.activemq.artemis.core.server.metrics.plugins.LoggingMetricsPlugin"/>
</metrics>
----

View File

@ -0,0 +1,99 @@
/*
* 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.activemq.artemis.tests.integration.plugin;
import java.lang.invoke.MethodHandles;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import org.apache.activemq.artemis.core.config.MetricsConfiguration;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.metrics.plugins.SimpleMetricsPlugin;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggingMetricsTest extends ActiveMQTestBase {
@Override
@Before
public void setUp() throws Exception {
super.setUp();
}
@Test
public void testLoggingMetrics() throws Exception {
final String idName = "log4j2.events";
final Tag brokerTag = Tag.of("broker", "localhost");
final String levelName = "level";
int start = 0;
String message = "";
ActiveMQServer server = createServer(false, createDefaultInVMConfig()
.setMetricsConfiguration(new MetricsConfiguration()
.setPlugin(new SimpleMetricsPlugin().init(null))
.setLogging(true)));
server.start();
Configurator.setLevel(MethodHandles.lookup().lookupClass(), Level.TRACE);
final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Meter.Id trace = new Meter.Id(idName, Tags.of(brokerTag, Tag.of(levelName, "trace")), null, null, null);
assertTrue(MetricsPluginTest.getMetrics(server).containsKey(trace));
start = MetricsPluginTest.getMetrics(server).get(trace).intValue();
logger.trace(message);
logger.trace(message);
logger.trace(message);
assertEquals(3, MetricsPluginTest.getMetrics(server).get(trace).intValue() - start);
Meter.Id debug = new Meter.Id(idName, Tags.of(brokerTag, Tag.of(levelName, "debug")), null, null, null);
assertTrue(MetricsPluginTest.getMetrics(server).containsKey(debug));
start = MetricsPluginTest.getMetrics(server).get(debug).intValue();
logger.debug(message);
logger.debug(message);
logger.debug(message);
assertEquals(3, MetricsPluginTest.getMetrics(server).get(debug).intValue() - start);
Meter.Id info = new Meter.Id(idName, Tags.of(brokerTag, Tag.of(levelName, "info")), null, null, null);
assertTrue(MetricsPluginTest.getMetrics(server).containsKey(info));
start = MetricsPluginTest.getMetrics(server).get(info).intValue();
logger.info(message);
logger.info(message);
logger.info(message);
assertEquals(3, MetricsPluginTest.getMetrics(server).get(info).intValue() - start);
Meter.Id warn = new Meter.Id(idName, Tags.of(brokerTag, Tag.of(levelName, "warn")), null, null, null);
assertTrue(MetricsPluginTest.getMetrics(server).containsKey(warn));
start = MetricsPluginTest.getMetrics(server).get(warn).intValue();
logger.warn(message);
logger.warn(message);
logger.warn(message);
assertEquals(3, MetricsPluginTest.getMetrics(server).get(warn).intValue() - start);
Meter.Id error = new Meter.Id(idName, Tags.of(brokerTag, Tag.of(levelName, "error")), null, null, null);
assertTrue(MetricsPluginTest.getMetrics(server).containsKey(error));
start = MetricsPluginTest.getMetrics(server).get(error).intValue();
logger.error(message);
logger.error(message);
logger.error(message);
assertEquals(3, MetricsPluginTest.getMetrics(server).get(error).intValue() - start);
}
}