All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.couchbase.client.core.cnc.LoggingEventConsumer Maven / Gradle / Ivy

There is a newer version: 3.7.2
Show newest version
/*
 * Copyright (c) 2018 Couchbase, 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
 *
 *    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 com.couchbase.client.core.cnc;

import com.couchbase.client.core.env.LoggerConfig;
import com.couchbase.client.core.msg.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static com.couchbase.client.core.logging.RedactableArgument.redactUser;
import static com.couchbase.client.core.util.CbCollections.isNullOrEmpty;

/**
 * Consumes {@link Event Events} and logs them per configuration.
 * 

* This consumer is intended to be attached by default and performs convenient logging * throughout the system. */ public class LoggingEventConsumer implements Consumer { /** * Contains the selected loggers that should be used for logging. */ private final Map loggers = new HashMap<>(Event.Category.values().length); private final LoggerConfig loggerConfig; /** * Creates a new {@link LoggingEventConsumer} with all defaults. * * @return a {@link LoggingEventConsumer}. */ public static LoggingEventConsumer create() { return new LoggingEventConsumer(LoggerConfig.create()); } public static LoggingEventConsumer create(final LoggerConfig loggerConfig) { return new LoggingEventConsumer(loggerConfig); } private LoggingEventConsumer(final LoggerConfig loggerConfig) { this.loggerConfig = loggerConfig; } @Override public void accept(final Event event) { Event.Severity severity = event.severity(); if (severity == Event.Severity.TRACING) { return; } Logger logger = loggers.computeIfAbsent(event.category(), LoggerFactory::getLogger); if (!mustLogEvent(severity, logger)) { return; } StringBuilder logLineBuilder = new StringBuilder(); logLineBuilder.append("[").append(event.category()).append("]"); logLineBuilder.append("[").append(event.getClass().getSimpleName()).append("]"); if (!event.duration().isZero()) { logLineBuilder .append("[") .append(convertEventDuration(event.duration())) .append("]"); } String description = event.description(); if (description != null && !description.isEmpty()) { logLineBuilder.append(" ").append(description); } if (event.context() != null) { logLineBuilder.append(" ").append(event.context().exportAsString(Context.ExportFormat.JSON)); } String logLine = logLineBuilder.toString(); boolean diagnosticContext = loggerConfig.diagnosticContextEnabled() && event.context() instanceof RequestContext; if (diagnosticContext) { attachContext(((RequestContext) event.context()).clientContext()); } switch (severity) { case VERBOSE: if (event.cause() != null) { logger.trace("{}", logLine, event.cause()); } else { logger.trace("{}", logLine); } break; case DEBUG: if (event.cause() != null) { logger.debug("{}", logLine, event.cause()); } else { logger.debug("{}", logLine); } break; case INFO: if (event.cause() != null) { logger.info("{}", logLine, event.cause()); } else { logger.info("{}", logLine); } break; case WARN: if (event.cause() != null) { logger.warn("{}", logLine, event.cause()); } else { logger.warn("{}", logLine); } break; case ERROR: if (event.cause() != null) { logger.error("{}", logLine, event.cause()); } else { logger.error("{}", logLine); } default: } if (diagnosticContext) { MDC.clear(); } } private static void attachContext(final Map context) { if (isNullOrEmpty(context)) { return; } MDC.setContextMap( context.entrySet().stream().collect(Collectors.toMap( e -> redactUser(e.getKey()).toString(), e -> { Object v = e.getValue(); return v == null ? "" : redactUser(v.toString()).toString(); })) ); } /** * Helper method to check if an event must be logged based on the severity. * * @param severity the severity of the event. * @param logger the logger to check if specific log levels are enabled. * @return true if the event must be logged, false otherwise. */ private static boolean mustLogEvent(final Event.Severity severity, final Logger logger) { switch(severity) { case VERBOSE: return logger.isTraceEnabled(); case DEBUG: return logger.isDebugEnabled(); case INFO: return logger.isInfoEnabled(); case WARN: return logger.isWarnEnabled(); case ERROR: return logger.isErrorEnabled(); default: return true; } } /** * Converts the event duration into a reasonable string format. *

* Note that the units are not cut directly at the "kilo" boundary but rather at kilo * 10 so that the precision * at the lower ends is not lost. So 1ms will be shown as 10000 micros and 1s as 10000 millis. * * @param duration the duration to convert. * @return the converted duration. */ static String convertEventDuration(final Duration duration) { long nanos = duration.toNanos(); if (nanos < 1000L) { // everything below a microsecond is ns return nanos + "ns"; } else if (nanos < 10_000_000L) { return TimeUnit.NANOSECONDS.toMicros(nanos) + "us"; // everything below 10ms is micros } else if (nanos < 10_000_000_000L) { // everything below 10s is millis return TimeUnit.NANOSECONDS.toMillis(nanos) + "ms"; } else { // everything higher than 10s is seconds return TimeUnit.NANOSECONDS.toSeconds(nanos) + "s"; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy