Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
import org.apache.logging.log4j.core.filter.CompositeFilter;

import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
Expand All @@ -50,14 +52,11 @@
* "https://github.com/micrometer-metrics/micrometer/issues/2176">micrometer#2176</a>
* @author Steven Sheehy
* @author Johnny Lim
* @author Harsh Verma
* @since 1.1.0
*/
public class Log4j2Metrics implements MeterBinder, AutoCloseable {

private static final String METER_NAME = "log4j2.events";

private static final String METER_DESCRIPTION = "Number of log events";

private static final InternalLogger logger = InternalLoggerFactory.getInstance(Log4j2Metrics.class);

private final Iterable<Tag> tags;
Expand All @@ -68,6 +67,8 @@ public class Log4j2Metrics implements MeterBinder, AutoCloseable {

private final List<PropertyChangeListener> changeListeners = new CopyOnWriteArrayList<>();

private final List<LogInterceptor> logInterceptors;

public Log4j2Metrics() {
this(emptyList());
}
Expand All @@ -77,8 +78,13 @@ public Log4j2Metrics(Iterable<Tag> tags) {
}

public Log4j2Metrics(Iterable<Tag> tags, LoggerContext loggerContext) {
this(tags, loggerContext, emptyList());
}

public Log4j2Metrics(Iterable<Tag> tags, LoggerContext loggerContext, List<LogInterceptor> logInterceptors) {
this.tags = tags;
this.loggerContext = loggerContext;
this.logInterceptors = logInterceptors;
}

@Override
Expand Down Expand Up @@ -135,7 +141,7 @@ private boolean addFilterIfAbsent(LoggerConfig loggerConfig, MetricsFilter metri

private MetricsFilter getOrCreateMetricsFilterAndStart(MeterRegistry registry) {
return metricsFilters.computeIfAbsent(registry, r -> {
MetricsFilter metricsFilter = new MetricsFilter(r, tags);
MetricsFilter metricsFilter = new MetricsFilter(r, tags, logInterceptors);
metricsFilter.start();
return metricsFilter;
});
Expand Down Expand Up @@ -173,60 +179,11 @@ private void removeMetricsFilter(Configuration configuration) {

static class MetricsFilter extends AbstractFilter {

private final Counter fatalCounter;

private final Counter errorCounter;

private final Counter warnCounter;

private final Counter infoCounter;

private final Counter debugCounter;

private final Counter traceCounter;

MetricsFilter(MeterRegistry registry, Iterable<Tag> tags) {
fatalCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "fatal")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);

errorCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "error")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);

warnCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "warn")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);

infoCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "info")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);

debugCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "debug")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);

traceCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "trace")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);
private final IncrementLogCounterStrategy incrementLogCounterStrategy;

MetricsFilter(MeterRegistry registry, Iterable<Tag> tags, List<LogInterceptor> logInterceptors) {
this.incrementLogCounterStrategy = IncrementLogCounterFactory.getIncrementLogCounterStrategy(registry, tags,
logInterceptors);
}

@Override
Expand All @@ -236,30 +193,153 @@ public Result filter(LogEvent event) {
}

private void incrementCounter(LogEvent event) {
switch (event.getLevel().getStandardLevel()) {
case FATAL:
fatalCounter.increment();
break;
case ERROR:
errorCounter.increment();
break;
case WARN:
warnCounter.increment();
break;
case INFO:
infoCounter.increment();
break;
case DEBUG:
debugCounter.increment();
break;
case TRACE:
traceCounter.increment();
break;
default:
break;
}
incrementLogCounterStrategy.incrementCounter(event);
}

}

}

class IncrementLogCounterFactory {

public static IncrementLogCounterStrategy getIncrementLogCounterStrategy(MeterRegistry registry, Iterable<Tag> tags,
List<LogInterceptor> logInterceptors) {
if (logInterceptors.isEmpty()) {
return new StaticLogCounter(registry, tags);
}
else {
return new DynamicLogCounter(registry, tags, logInterceptors);
}
}

}

interface IncrementLogCounterStrategy {

String METER_DESCRIPTION = "Number of log events";

String METER_NAME = "log4j2.events";

void incrementCounter(LogEvent event);

}

class StaticLogCounter implements IncrementLogCounterStrategy {

private final Counter fatalCounter;

private final Counter errorCounter;

private final Counter warnCounter;

private final Counter infoCounter;

private final Counter debugCounter;

private final Counter traceCounter;

StaticLogCounter(MeterRegistry registry, Iterable<Tag> tags) {
fatalCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "fatal")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);

errorCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "error")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);

warnCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "warn")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);

infoCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "info")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);

debugCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "debug")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);

traceCounter = Counter.builder(METER_NAME)
.tags(tags)
.tags("level", "trace")
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);
}

@Override
public void incrementCounter(LogEvent event) {
switch (event.getLevel().getStandardLevel()) {
case FATAL:
fatalCounter.increment();
break;
case ERROR:
errorCounter.increment();
break;
case WARN:
warnCounter.increment();
break;
case INFO:
infoCounter.increment();
break;
case DEBUG:
debugCounter.increment();
break;
case TRACE:
traceCounter.increment();
break;
default:
break;
}
}

}

class DynamicLogCounter implements IncrementLogCounterStrategy {

private final MeterRegistry registry;

private final Iterable<Tag> tags;

private final Iterable<LogInterceptor> logInterceptors;

DynamicLogCounter(MeterRegistry registry, Iterable<Tag> tags, Iterable<LogInterceptor> logInterceptors) {
this.registry = registry;
this.tags = tags;
this.logInterceptors = logInterceptors;
}

@Override
public void incrementCounter(LogEvent event) {
List<Tag> dynamicTags = new ArrayList<>();
for (LogInterceptor logInterceptor : logInterceptors) {
dynamicTags.addAll(logInterceptor.getTagFromLogs(event));
}

Counter counter = Counter.builder(METER_NAME)
.tags(tags)
.tags(dynamicTags)
.tags("level", event.getLevel().getStandardLevel().name().toLowerCase(Locale.ROOT))
.description(METER_DESCRIPTION)
.baseUnit(BaseUnits.EVENTS)
.register(registry);

counter.increment();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2026 VMware, Inc.
*
* Licensed 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
*
* https://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 io.micrometer.core.instrument.binder.logging;

import io.micrometer.core.instrument.Tag;
import org.apache.logging.log4j.core.LogEvent;
import java.util.List;

/**
* {@link Log4j2Metrics} uses this interface to get tags from the log events. This is used
* to get tags like exception name from the log events and add them to the metrics.
* author: Harsh Verma
*
* @since 1.16.4
*/
public interface LogInterceptor {

List<Tag> getTagFromLogs(LogEvent event);

}
Loading