aboutsummaryrefslogtreecommitdiffstats
path: root/jdisc_http_service/src/main/java/com/yahoo/container/logging/JsonConnectionLogWriter.java
blob: 158d2ec4ea6be58e4d222837291efcf78d252888 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright Verizon Media. 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.Objects;
import java.util.Optional;

/**
 * @author bjorncs
 */
class JsonConnectionLogWriter implements LogWriter<ConnectionLogEntry> {

    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()));
            writeOptionalString(generator, "remoteAddress", unwrap(record.remoteAddress()));
            writeOptionalInteger(generator, "remotePort", unwrap(record.remotePort()));
            writeOptionalLong(generator, "httpBytesReceived", unwrap(record.httpBytesReceived()));
            writeOptionalLong(generator, "httpBytesSent", unwrap(record.httpBytesSent()));
            writeOptionalLong(generator, "requests", unwrap(record.requests()));
            writeOptionalLong(generator, "responses", unwrap(record.responses()));

            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());

            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();
                }

                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> T unwrap(Optional<T> 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);
    }
}