Skip to content

Commit

Permalink
Create one MetricsFilter per registry in Log4j2Metrics
Browse files Browse the repository at this point in the history
Before, we were needlessly making multiple instances of `MetricsFilter` that were functionally equivalent. This reduces the instances to one per registry to which Log4j2Metrics is bound. This will reduce allocations and memory usage slightly.
  • Loading branch information
shakuzen committed Jan 23, 2025
1 parent 9111b44 commit f26dba7
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.core.filter.CompositeFilter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static java.util.Collections.emptyList;

Expand Down Expand Up @@ -61,7 +61,7 @@ public class Log4j2Metrics implements MeterBinder, AutoCloseable {

private final LoggerContext loggerContext;

private final List<MetricsFilter> metricsFilters = new ArrayList<>();
private final ConcurrentMap<MeterRegistry, MetricsFilter> metricsFilters = new ConcurrentHashMap<>();

public Log4j2Metrics() {
this(emptyList());
Expand All @@ -80,7 +80,7 @@ public Log4j2Metrics(Iterable<Tag> tags, LoggerContext loggerContext) {
public void bindTo(MeterRegistry registry) {
Configuration configuration = loggerContext.getConfiguration();
LoggerConfig rootLoggerConfig = configuration.getRootLogger();
rootLoggerConfig.addFilter(createMetricsFilterAndStart(registry));
rootLoggerConfig.addFilter(getOrCreateMetricsFilterAndStart(registry));

loggerContext.getConfiguration()
.getLoggers()
Expand All @@ -102,25 +102,26 @@ public void bindTo(MeterRegistry registry) {
if (logFilter instanceof MetricsFilter) {
return;
}
loggerConfig.addFilter(createMetricsFilterAndStart(registry));
loggerConfig.addFilter(getOrCreateMetricsFilterAndStart(registry));
});

loggerContext.updateLoggers(configuration);
}

private MetricsFilter createMetricsFilterAndStart(MeterRegistry registry) {
MetricsFilter metricsFilter = new MetricsFilter(registry, tags);
metricsFilter.start();
metricsFilters.add(metricsFilter);
return metricsFilter;
private MetricsFilter getOrCreateMetricsFilterAndStart(MeterRegistry registry) {
return metricsFilters.computeIfAbsent(registry, r -> {
MetricsFilter metricsFilter = new MetricsFilter(r, tags);
metricsFilter.start();
return metricsFilter;
});
}

@Override
public void close() {
if (!metricsFilters.isEmpty()) {
Configuration configuration = loggerContext.getConfiguration();
LoggerConfig rootLoggerConfig = configuration.getRootLogger();
metricsFilters.forEach(rootLoggerConfig::removeFilter);
metricsFilters.values().forEach(rootLoggerConfig::removeFilter);

loggerContext.getConfiguration()
.getLoggers()
Expand All @@ -129,12 +130,13 @@ public void close() {
.filter(loggerConfig -> !loggerConfig.isAdditive())
.forEach(loggerConfig -> {
if (loggerConfig != rootLoggerConfig) {
metricsFilters.forEach(loggerConfig::removeFilter);
metricsFilters.values().forEach(loggerConfig::removeFilter);
}
});

loggerContext.updateLoggers(configuration);
metricsFilters.forEach(MetricsFilter::stop);
metricsFilters.values().forEach(MetricsFilter::stop);
metricsFilters.clear();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationSource;
Expand All @@ -34,6 +35,7 @@

import java.io.IOException;
import java.time.Duration;
import java.util.Iterator;

import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -184,4 +186,47 @@ void asyncLogShouldNotBeDuplicated() throws IOException {
.until(() -> registry.get("log4j2.events").tags("level", "info").counter().count() == 3);
}

@Test
void metricsFilterIsReused() {
LoggerContext loggerContext = new LoggerContext("test");

LoggerConfig loggerConfig = new LoggerConfig("com.test", Level.INFO, false);
Configuration configuration = loggerContext.getConfiguration();
configuration.addLogger("com.test", loggerConfig);
loggerContext.setConfiguration(configuration);
loggerContext.updateLoggers();

Logger logger1 = loggerContext.getLogger("com.test.log1");
loggerContext.getLogger("com.test.log2");

new Log4j2Metrics(emptyList(), loggerContext).bindTo(registry);
Iterator<Filter> rootFilters = loggerContext.getRootLogger().getFilters();
Log4j2Metrics.MetricsFilter rootFilter = (Log4j2Metrics.MetricsFilter) rootFilters.next();
assertThat(rootFilters.hasNext()).isFalse();

Log4j2Metrics.MetricsFilter logger1Filter = (Log4j2Metrics.MetricsFilter) loggerContext.getConfiguration()
.getLoggerConfig(logger1.getName())
.getFilter();
assertThat(logger1Filter).isEqualTo(rootFilter);
}

@Test
void multipleRegistriesCanBeBound() {
MeterRegistry registry2 = new SimpleMeterRegistry();

Log4j2Metrics log4j2Metrics = new Log4j2Metrics(emptyList());
log4j2Metrics.bindTo(registry);

Logger logger = LogManager.getLogger(Log4j2MetricsTest.class);
Configurator.setLevel(logger, Level.INFO);
logger.info("Hello, world!");
assertThat(registry.get("log4j2.events").tags("level", "info").counter().count()).isEqualTo(1);

log4j2Metrics.bindTo(registry2);
logger.info("Hello, world!");
assertThat(registry.get("log4j2.events").tags("level", "info").counter().count()).isEqualTo(2);
assertThat(registry2.get("log4j2.events").tags("level", "info").counter().count()).isEqualTo(1);

}

}

0 comments on commit f26dba7

Please sign in to comment.