aboutsummaryrefslogtreecommitdiffstats
path: root/jdisc_core/src/main/java/com/yahoo/jdisc/core/ConsoleLogFormatter.java
blob: f7f53d304dfd2c8991221fac866390734eec641a (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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.jdisc.core;

import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogEntry;
import org.osgi.service.log.LogLevel;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.time.Instant;

/**
 * @author Simon Thoresen Hult
 */
class ConsoleLogFormatter {

    // The string used as a replacement for absent/null values.
    static final String ABSENCE_REPLACEMENT = "-";

    private final String hostName;
    private final String processId;
    private final String serviceName;

    public ConsoleLogFormatter(String hostName, String processId, String serviceName) {
        this.hostName = formatOptional(hostName);
        this.processId = formatOptional(processId);
        this.serviceName = formatOptional(serviceName);
    }

    public String formatEntry(LogEntry entry) {
        StringBuilder ret = new StringBuilder();
        formatTime(entry, ret).append('\t');
        formatHostName(ret).append('\t');
        formatProcessId(entry, ret).append('\t');
        formatServiceName(ret).append('\t');
        formatComponent(entry, ret).append('\t');
        formatLevel(entry, ret).append('\t');
        formatMessage(entry, ret);
        formatException(entry, ret);
        return ret.toString();
    }

    // TODO: The non-functional, side effect-laden coding style here is ugly and makes testing hard. See ticket 7128315.

    private StringBuilder formatTime(LogEntry entry, StringBuilder out) {
        Instant instant = Instant.ofEpochMilli(entry.getTime());
        out.append(String.format("%d.%06d", instant.getEpochSecond(), instant.getNano() / 1000));
        return out;
    }

    private StringBuilder formatHostName(StringBuilder out) {
        out.append(hostName);
        return out;
    }

    private StringBuilder formatProcessId(LogEntry entry, StringBuilder out) {
        out.append(processId);
        String threadId = getProperty(entry, "THREAD_ID");
        if (threadId != null) {
            out.append('/').append(threadId);
        }
        return out;
    }

    private StringBuilder formatServiceName(StringBuilder out) {
        out.append(serviceName);
        return out;
    }

    private StringBuilder formatComponent(LogEntry entry, StringBuilder out) {
        Bundle bundle = entry.getBundle();
        String loggerName = getProperty(entry, "LOGGER_NAME");
        if (bundle == null && loggerName == null) {
            out.append("-");
        } else {
            if (bundle != null) {
                out.append(bundle.getSymbolicName());
            }
            if (loggerName != null) {
                out.append('/').append(loggerName);
            }
        }
        return out;
    }

    private StringBuilder formatLevel(LogEntry entry, StringBuilder out) {
        switch (entry.getLogLevel()) {
        case ERROR:
            out.append("error");
            break;
        case WARN:
            out.append("warning");
            break;
        case INFO:
            out.append("info");
            break;
        case DEBUG:
            out.append("debug");
            break;
        default:
            out.append("unknown");
            break;
        }
        return out;
    }

    private StringBuilder formatMessage(LogEntry entry, StringBuilder out) {
        String msg = entry.getMessage();
        if (msg != null) {
            formatString(msg, out);
        }
        return out;
    }

    private StringBuilder formatException(LogEntry entry, StringBuilder out) {
        Throwable t = entry.getException();
        if (t != null) {
            if (entry.getLogLevel() == LogLevel.INFO) {
                out.append(": ");
                String msg = t.getMessage();
                if (msg != null) {
                    formatString(msg, out);
                } else {
                    out.append(t.getClass().getName());
                }
            } else {
                Writer buf = new StringWriter();
                t.printStackTrace(new PrintWriter(buf));
                formatString("\n" + buf, out);
            }
        }
        return out;
    }

    private static StringBuilder formatString(String str, StringBuilder out) {
        for (int i = 0, len = str.length(); i < len; ++i) {
            char c = str.charAt(i);
            switch (c) {
            case '\n':
                out.append("\\n");
                break;
            case '\r':
                out.append("\\r");
                break;
            case '\t':
                out.append("\\t");
                break;
            case '\\':
                out.append("\\\\");
                break;
            default:
                out.append(c);
                break;
            }
        }
        return out;
    }

    private static String getProperty(LogEntry entry, String name) {
        ServiceReference<?> ref = entry.getServiceReference();
        if (ref == null) {
            return null;
        }
        Object val = ref.getProperty(name);
        if (val == null) {
            return null;
        }
        return val.toString();
    }

    static String formatOptional(String str) {
        return formatOptional(str, ABSENCE_REPLACEMENT);
    }

    private static String formatOptional(final String str, final String replacementIfAbsent) {
        if (str == null) {
            return replacementIfAbsent;
        }
        final String result = str.trim();
        if (result.isEmpty()) {
            return replacementIfAbsent;
        }
        return result;
    }
}