Изменить формат журнала SpringBoot - микрометр на JSON

У меня есть приложение SpringBoot, которое использует микрометр для распечатки показателей приложения.

Мой pom.xml имеет:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-core</artifactId>
    <version>1.1.3</version>
</dependency>

Мой класс конфигурации:

@Configuration
public class CoreConfiguration {

    public static final String USER_REQUEST_CHANNEL = "userRequestChannel";
    public static final String USER_RESPONSE_CHANNEL = "userResponseChannel";
    public static final String MDC_ADD = "add";
    public static final String DONE_CHANNEL = "nullChannel";
    public static final String ADMIN_REQUEST_CHANNEL = "adminRequestChannel";
    public static final String ADMIN_RESPONSE_CHANNEL = "adminResponseChannel";
    public static final String SUPPORT_COMPLETED_CHANNEL = "supportCompletedChannel";
    public static final String SUPPORT_RUNNING_CHANNEL = "nullChannel";
    public static final String SUPPORT_ERROR_CHANNEL = "nullChannel";

    @Bean(name = USER_REQUEST_CHANNEL)
    public MessageChannel oAuthRequestChannel() {
        return MessageChannels.direct().get();
    }

    @Bean(name = USER_RESPONSE_CHANNEL)
    public MessageChannel oAuthResponseChannel() {
        return MessageChannels.direct().get();
    }

    @Bean(name = FIRST_TRADE_CHANNEL)
    public MessageChannel firstTradeChannel() {
        return MessageChannels.direct().get();
    }

    @Bean(name = ADMIN_REQUEST_CHANNEL)
    public MessageChannel instructionExecutionRequestChannel() {
        return MessageChannels.direct().get();
    }

    @Bean(name = ADMIN_RESPONSE_CHANNEL)
    public MessageChannel instructionExecutionResponseChannel() {
        return MessageChannels.direct().get();
    }

    @Bean(name = SUPPORT_COMPLETED_CHANNEL)
    public MessageChannel groupExecutionCompletedChannel() {
        return MessageChannels.direct().get();
    }

    /**
     * Turn on the Micrometer log file metrics.
     *
     * @return
     */
    @Bean
    public LoggingMeterRegistry loggingMeterRegistry(@Value("${micrometer.log.minutes}") long minutes) {
        LoggingRegistryConfig config = new LoggingRegistryConfig() {
            @Override
            public String get(String s) {
                return null;
            }

            @Override
            public Duration step() {
                return Duration.ofMinutes(minutes);
            }
        };
        return LoggingMeterRegistry.builder(config).build();
    }
}

ИСПОЛЬЗОВАНИЕ В КЛАССЕ:

public IntegrationFlow processRequest(HttpRequest request) {
    return IntegrationFlows.from(INPUT_CHANNEL)
            .enrichHeader(m -> m.headerExpression(REQUEST_ID,"payload.message.headers." + REQUEST_ID))
            .log(LoggingHandler.Level.DEBUG, CoreConfiguration.class.getName(), m -> {
                Throwable t = (Throwable) m.getPayload();
                return throwableToString(t);})
            .get();
}

Я вижу вывод метрик, записанных в мой файл журнала, как:

2019-02-25 14:40:23,337 | ИНФО | [издатель метрик регистрации] | [meter.core.instrument.logging.LoggingMeterRegistry] | MY_SAMPLE_APP | идентификатор пользователя = [] | jvm.memory.max{area=heap,id=PS Survivor Space} значение=12,5 МБ

Как выйти из системы в формате JSON?

ЧТО МНЕ НУЖНО:

{
"ts": "2019-02-25 14:40:23,337" ,
"level" : "INFO",
"className" : "meter.core.instrument.logging.LoggingMeterRegistry",
"appName" : "MY_SAMPLE_APP",
"userId" : "",
"metric" : 
        {"metricType": "jvm.memory.max",
         "area":"heap",
         "id":"PS Survivor Space",
         "value":"12.5 MiB"
         }
}

Обновление вопроса с кодом согласно ответу Джона. @Джон, как ты думаешь, приведенный ниже код правильный? Я реализовал пользовательский реестр Meter Registry, который расширяет LoggingMeterRegistry.

Единственная разница между LoggingMeterRegistry и CustomMeterRegistry заключается в том, что мой пользовательский класс выводит идентификатор =

В LoggingMeterRegistry: this.loggingSink.accept(print.id() + " throughput=" + print.rate(count));

В CustomMeterRegistry: this.loggingSink.accept("ID=" + print.id() + " throughput=" + print.rate(count));

ПОЛНЫЙ КОД:

public abstract class SplunkMeterRegistry extends LoggingMeterRegistry {
@Override
protected void publish() {
{
    if (this.config.enabled()) {
        this.getMeters().stream().sorted((m1, m2) -> {
            int typeComp = m1.getId().getType().compareTo(m2.getId().getType());
            return typeComp == 0 ? m1.getId().getName().compareTo(m2.getId().getName()) : typeComp;
        }).forEach((m) -> {
            LoggingMeterRegistry.Printer print = new LoggingMeterRegistry.Printer(m);
            m.use((gauge) -> {
                this.loggingSink.accept("ID=" + print.id() + " value=" + print.value(gauge.value()));
            }, (counter) -> {
                double count = counter.count();
                if (this.config.logInactive() || count != 0.0D) {
                    this.loggingSink.accept("ID=" + print.id() + " throughput=" + print.rate(count));
                }
            }, (timer) -> {
                HistogramSnapshot snapshot = timer.takeSnapshot();
                long count = snapshot.count();
                if (this.config.logInactive() || count != 0L) {
                    this.loggingSink.accept("ID=" + print.id() + " throughput=" + print.unitlessRate((double)count) + " mean=" + print.time(snapshot.mean(this.getBaseTimeUnit())) + " max=" + print.time(snapshot.max(this.getBaseTimeUnit())));
                }
            }, (summary) -> {
                HistogramSnapshot snapshot = summary.takeSnapshot();
                long count = snapshot.count();
                if (this.config.logInactive() || count != 0L) {
                    this.loggingSink.accept("ID=" + print.id() + " throughput=" + print.unitlessRate((double)count) + " mean=" + print.value(snapshot.mean()) + " max=" + print.value(snapshot.max()));
                }
            }, (longTaskTimer) -> {
                int activeTasks = longTaskTimer.activeTasks();
                if (this.config.logInactive() || activeTasks != 0) {
                    this.loggingSink.accept("ID=" + print.id() + " active=" + print.value((double)activeTasks) + " duration=" + print.time(longTaskTimer.duration(this.getBaseTimeUnit())));
                }
            }, (timeGauge) -> {
                double value = timeGauge.value(this.getBaseTimeUnit());
                if (this.config.logInactive() || value != 0.0D) {
                    this.loggingSink.accept("ID=" + print.id() + " value=" + print.time(value));
                }
            }, (counter) -> {
                double count = counter.count();
                if (this.config.logInactive() || count != 0.0D) {
                    this.loggingSink.accept("ID=" + print.id() + " throughput=" + print.rate(count));
                }
            }, (timer) -> {
                double count = timer.count();
                if (this.config.logInactive() || count != 0.0D) {
                    this.loggingSink.accept("ID=" + print.id() + " throughput=" + print.rate(count) + " mean=" + print.time(timer.mean(this.getBaseTimeUnit())));
                }
            }, (meter) -> {
                this.loggingSink.accept("ID=" + print.id() + StreamSupport.stream(meter.measure().spliterator(), false).map((ms) -> {
                    return ms.getStatistic().getTagValueRepresentation() + "=" + DoubleFormat.decimalOrNan(ms.getValue());
                }));
            });
        });
    }

}
}

}


person Deepboy    schedule 27.02.2019    source источник
comment
Мне непонятно, как это приводит к полезной нагрузке JSON, о которой вы изначально просили. Тем не менее, теперь я вижу, что вам действительно нужна реализация Splunk. Возможно, нам следует подумать о первоклассной поддержке для этого?   -  person jkschneider    schedule 01.03.2019
comment
@jkschneider, спасибо за ответ. Мне нужно закончить код за 2-3 дня. Не могли бы вы помочь мне написать собственный MeterRegistry, который выводит в формате JSON, предоставив мне пример кода?   -  person Deepboy    schedule 01.03.2019
comment
Большинство реестров, основанных на push-уведомлениях, пишут JSON. См. DatadogMeterRegistry, InfluxMeterRegistry и т. д.   -  person jkschneider    schedule 02.03.2019


Ответы (1)


Вы должны реализовать пользовательский MeterRegistry, возможно, используя LoggingMeterRegistry в качестве ссылки, который сериализует данные в желаемом формате. По сути, реализации MeterRegistry на основе push - это просто разные форматы сериализации для разных потребителей.

person jkschneider    schedule 28.02.2019