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

io.quarkiverse.googlecloudservices.logging.runtime.ecs.EscJsonFormat Maven / Gradle / Ivy

package io.quarkiverse.googlecloudservices.logging.runtime.ecs;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.ErrorManager;
import java.util.logging.Level;

import org.jboss.logmanager.ExtLogRecord;

import com.google.common.base.Strings;

import io.quarkiverse.googlecloudservices.logging.runtime.LoggingConfiguration;
import io.quarkiverse.googlecloudservices.logging.runtime.TraceInfo;
import io.quarkiverse.googlecloudservices.logging.runtime.util.SimpleFormatter;

/**
 * This is the ESC json formatter.
 */
public class EscJsonFormat {

    private static final SimpleFormatter MSG_FORMAT = new SimpleFormatter();

    protected LoggingConfiguration config;
    protected ErrorManager errorManager;

    public Map format(ExtLogRecord record, TraceInfo tracing) {
        return toEsc(record, tracing);
    }

    public void init(LoggingConfiguration config, ErrorManager errorManager) {
        this.setLoggingConfiguration(config);
        this.setErrorManager(errorManager);
    }

    public void setLoggingConfiguration(LoggingConfiguration config) {
        this.config = config;
    }

    public void setErrorManager(ErrorManager errorManager) {
        this.errorManager = errorManager;
    }

    public Map toEsc(ExtLogRecord record, TraceInfo tracing) {
        if (this.config == null) {
            return null; // sanity check
        } else {
            Map m = new HashMap<>();
            putEcsVersion(m);
            putTimestamp(m, record.getInstant());
            putLoggerName(m, record.getLoggerName(), record.getLoggerClassName());
            putThreadName(m, record.getThreadName());
            putThreadId(m, record.getThreadID());
            putFormattedMessage(m, record);
            putLogLevel(m, record.getLevel());
            putSource(m, record.getSourceClassName(), record.getSourceLineNumber(), record.getSourceMethodName());
            putThrown(m, record.getThrown());
            putHost(m, record.getHostName());
            putMdcIfEnabled(m, record.getMdcCopy());
            putParametersIfEnabled(m, record.getParameters());
            if (tracing != null) {
                putTracing(m, tracing);
            }
            return m;
        }
    }

    protected void putTracing(Map m, TraceInfo tracing) {
        if (!Strings.isNullOrEmpty(tracing.getTraceId())) {
            getOrCreateObject(m, "trace").put("id", tracing.getTraceId());
        }
        if (!Strings.isNullOrEmpty(tracing.getSpanId())) {
            getOrCreateObject(m, "span").put("id", tracing.getSpanId());
        }
    }

    @SuppressWarnings("unchecked")
    protected void putParametersIfEnabled(Map m, Object[] parameters) {
        if (parameters != null && parameters.length > 0 && this.config.structured().parameters().included()) {
            List list = (List) m.computeIfAbsent(this.config.structured().parameters().fieldName(),
                    (k) -> new ArrayList(parameters.length));
            for (Object o : parameters) {
                if (shouldIncludeParameter(o)) {
                    list.add(String.valueOf(o));
                }
            }
        }
    }

    protected boolean shouldIncludeParameter(Object p) {
        return true;
    }

    protected void putHost(Map m, String hostName) {
        if (!Strings.isNullOrEmpty(hostName)) {
            getOrCreateObject(m, "host").put("name", hostName);
        }
    }

    protected void putThrown(Map m, Throwable thrown) {
        if (thrown != null) {
            Map error = getOrCreateObject(m, "error");
            error.put("type", thrown.getClass().getName());
            String msg = thrown.getMessage();
            if (!Strings.isNullOrEmpty(msg)) {
                error.put("message", msg);
            }
            if (this.config.structured().stackTrace().included()) {
                // render as a standard out string
                StringWriter sw = new StringWriter(1024);
                PrintWriter pw = new PrintWriter(sw);
                thrown.printStackTrace(pw);
                pw.flush();
                error.put("stack_trace", sw.toString());
            }
        }
    }

    protected void putMdcIfEnabled(Map m, Map mdcCopy) {
        if (mdcCopy != null && !mdcCopy.isEmpty() && this.config.structured().mdc().included()) {
            Map mdc = getOrCreateObject(m, this.config.structured().mdc().fieldName());
            mdcCopy.forEach((k, v) -> mdc.put(k, v));
        }
    }

    protected void putSource(Map m, String sourceClassName, int sourceLineNumber, String sourceMethodName) {
        if (!Strings.isNullOrEmpty(sourceClassName)) {
            Map log = getOrCreateObject(m, "log");
            Map origin = getOrCreateObject(log, "origin");
            Map clazz = getOrCreateObject(origin, "class");
            clazz.put("name", sourceClassName);
            clazz.put("line", Integer.valueOf(sourceLineNumber));
            if (!Strings.isNullOrEmpty(sourceMethodName)) {
                origin.put("function", sourceMethodName);
            }
        }
    }

    protected void putLogLevel(Map m, Level level) {
        getOrCreateObject(m, "log").put("level", level.getName());
    }

    protected void putFormattedMessage(Map m, ExtLogRecord record) {
        m.put("message", MSG_FORMAT.format(record));
    }

    protected void putThreadId(Map m, long longThreadID) {
        // default value is zero, so check for that
        if (longThreadID != 0) {
            getOrCreateObject(getOrCreateObject(m, "process"), "thread").put("id", Long.valueOf(longThreadID));
        }
    }

    protected void putThreadName(Map m, String threadName) {
        if (!Strings.isNullOrEmpty(threadName)) {
            getOrCreateObject(getOrCreateObject(m, "process"), "thread").put("name", threadName);
        }
    }

    protected void putLoggerName(Map m, String loggerName, String loggerClassName) {
        // logger class name takes precedence
        if (!Strings.isNullOrEmpty(loggerClassName)) {
            getOrCreateObject(m, "log").put("logger", loggerClassName);
        } else if (!Strings.isNullOrEmpty(loggerName)) {
            getOrCreateObject(m, "log").put("logger", loggerName);
        }
    }

    @SuppressWarnings("unchecked")
    protected Map getOrCreateObject(Map m, String name) {
        return (Map) m.computeIfAbsent(name, (k) -> new HashMap(3));
    }

    protected void putEcsVersion(Map m) {
        getOrCreateObject(m, "ecs").put("version", "1.2.0");
    }

    protected void putTimestamp(Map m, Instant instant) {
        m.put("@timestamp", instant.atOffset(ZoneOffset.UTC).toString());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy