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

com.yahoo.container.logging.JsonConnectionLogWriter Maven / Gradle / Ivy

// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.logging;

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yahoo.container.logging.ConnectionLogEntry.SslHandshakeFailure.ExceptionEntry;

import java.io.IOException;
import java.io.OutputStream;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @author bjorncs
 */
class JsonConnectionLogWriter implements LogWriter {

    private final JsonFactory jsonFactory = new JsonFactory(new ObjectMapper());

    @Override
    public void write(ConnectionLogEntry record, OutputStream outputStream) throws IOException {
        try (JsonGenerator generator = createJsonGenerator(outputStream)) {
            generator.writeStartObject();
            generator.writeStringField("id", record.id());
            generator.writeStringField("timestamp", record.timestamp().toString());

            writeOptionalSeconds(generator, "duration", unwrap(record.durationSeconds()));
            writeOptionalString(generator, "peerAddress", unwrap(record.peerAddress()));
            writeOptionalInteger(generator, "peerPort", unwrap(record.peerPort()));
            writeOptionalString(generator, "localAddress", unwrap(record.localAddress()));
            writeOptionalInteger(generator, "localPort", unwrap(record.localPort()));

            String proxyProtocolVersion = unwrap(record.proxyProtocolVersion());
            String proxyProtocolRemoteAddress = unwrap(record.remoteAddress());
            Integer proxyProtocolRemotePort = unwrap(record.remotePort());
            if (isAnyValuePresent(proxyProtocolVersion, proxyProtocolRemoteAddress, proxyProtocolRemotePort)) {
                generator.writeObjectFieldStart("proxyProtocol");
                writeOptionalString(generator, "version", proxyProtocolVersion);
                writeOptionalString(generator, "remoteAddress", proxyProtocolRemoteAddress);
                writeOptionalInteger(generator, "remotePort", proxyProtocolRemotePort);
                generator.writeEndObject();
            }

            String httpVersion = unwrap(record.httpProtocol());
            Long httpBytesReceived = unwrap(record.httpBytesReceived());
            Long httpBytesSent = unwrap(record.httpBytesSent());
            Long httpRequests = unwrap(record.requests());
            Long httpResponses = unwrap(record.responses());
            if (isAnyValuePresent(httpVersion, httpBytesReceived, httpBytesSent, httpRequests, httpResponses)) {
                generator.writeObjectFieldStart("http");
                writeOptionalString(generator, "version", httpVersion);
                writeOptionalLong(generator, "bytesReceived", httpBytesReceived);
                writeOptionalLong(generator, "responses", httpResponses);
                writeOptionalLong(generator, "bytesSent", httpBytesSent);
                writeOptionalLong(generator, "requests", httpRequests);
                generator.writeEndObject();
            }

            String sslProtocol = unwrap(record.sslProtocol());
            String sslSessionId = unwrap(record.sslSessionId());
            String sslCipherSuite = unwrap(record.sslCipherSuite());
            String sslPeerSubject = unwrap(record.sslPeerSubject());
            Instant sslPeerNotBefore = unwrap(record.sslPeerNotBefore());
            Instant sslPeerNotAfter = unwrap(record.sslPeerNotAfter());
            String sslSniServerName = unwrap(record.sslSniServerName());
            ConnectionLogEntry.SslHandshakeFailure sslHandshakeFailure = unwrap(record.sslHandshakeFailure());
            List sslSubjectAlternativeNames = record.sslSubjectAlternativeNames();

            if (isAnyValuePresent(
                    sslProtocol, sslSessionId, sslCipherSuite, sslPeerSubject, sslPeerNotBefore, sslPeerNotAfter,
                    sslSniServerName, sslHandshakeFailure)) {
                generator.writeObjectFieldStart("ssl");

                writeOptionalString(generator, "protocol", sslProtocol);
                writeOptionalString(generator, "sessionId", sslSessionId);
                writeOptionalString(generator, "cipherSuite", sslCipherSuite);
                writeOptionalString(generator, "peerSubject", sslPeerSubject);
                writeOptionalTimestamp(generator, "peerNotBefore", sslPeerNotBefore);
                writeOptionalTimestamp(generator, "peerNotAfter", sslPeerNotAfter);
                writeOptionalString(generator, "sniServerName", sslSniServerName);

                if (sslHandshakeFailure != null) {
                    generator.writeObjectFieldStart("handshake-failure");
                    generator.writeArrayFieldStart("exception");
                    for (ExceptionEntry entry : sslHandshakeFailure.exceptionChain()) {
                        generator.writeStartObject();
                        generator.writeStringField("cause", entry.name());
                        generator.writeStringField("message", entry.message());
                        generator.writeEndObject();
                    }
                    generator.writeEndArray();
                    generator.writeStringField("type", sslHandshakeFailure.type());
                    generator.writeEndObject();
                }
                if (!sslSubjectAlternativeNames.isEmpty()) {
                    generator.writeArrayFieldStart("san");
                    for (String sanEntry : sslSubjectAlternativeNames) {
                        generator.writeString(sanEntry);
                    }
                    generator.writeEndArray();
                }
                generator.writeEndObject();
            }
        }
    }

    private void writeOptionalString(JsonGenerator generator, String name, String value) throws IOException {
        if (value != null) {
            generator.writeStringField(name, value);
        }
    }

    private void writeOptionalInteger(JsonGenerator generator, String name, Integer value) throws IOException {
        if (value != null) {
            generator.writeNumberField(name, value);
        }
    }

    private void writeOptionalLong(JsonGenerator generator, String name, Long value) throws IOException {
        if (value != null) {
            generator.writeNumberField(name, value);
        }
    }

    private void writeOptionalTimestamp(JsonGenerator generator, String name, Instant value) throws IOException {
        if (value != null) {
            generator.writeStringField(name, value.toString());
        }
    }

    private void writeOptionalSeconds(JsonGenerator generator, String name, Double value) throws IOException {
        if (value != null) {
            FormatUtil.writeSecondsField(generator, name, value);
        }
    }

    private static boolean isAnyValuePresent(Object... values) { return Arrays.stream(values).anyMatch(Objects::nonNull); }
    private static  T unwrap(Optional maybeValue) { return maybeValue.orElse(null); }

    private JsonGenerator createJsonGenerator(OutputStream outputStream) throws IOException {
        return jsonFactory.createGenerator(outputStream, JsonEncoding.UTF8)
                .configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false)
                .configure(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM, false);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy