summaryrefslogtreecommitdiffstats
path: root/client/go/cmd/logfmt/handleline.go
blob: 33a1a1b386b0cbf3557e5c5271dd72f74d73c126 (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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// vespa logfmt command
// Author: arnej

package logfmt

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

// handle a line in "vespa.log" format; do filtering and formatting as specified in opts

func handleLine(opts *Options, line string) (output string, err error) {
	fields := strings.SplitN(line, "\t", 7)
	if len(fields) < 7 {
		return "", fmt.Errorf("not enough fields: '%s'", line)
	}
	timestampfield := fields[0] // seconds, optional fractional seconds
	hostfield := fields[1]
	pidfield := fields[2] // pid, optional tid
	servicefield := fields[3]
	componentfield := fields[4]
	levelfield := fields[5]
	messagefields := fields[6:]

	if !opts.showLevel(levelfield) {
		return "", nil
	}
	if opts.OnlyHostname != "" && opts.OnlyHostname != hostfield {
		return "", nil
	}
	if opts.OnlyPid != "" && opts.OnlyPid != pidfield {
		return "", nil
	}
	if opts.OnlyService != "" && opts.OnlyService != servicefield {
		return "", nil
	}
	if opts.OnlyInternal && !isInternal(componentfield) {
		return "", nil
	}
	if opts.ComponentFilter.unmatched(componentfield) {
		return "", nil
	}
	if opts.MessageFilter.unmatched(strings.Join(messagefields, "\t")) {
		return "", nil
	}

	var buf strings.Builder

	if opts.showField("fmttime") {
		secs, err := strconv.ParseFloat(timestampfield, 64)
		if err != nil {
			return "", err
		}
		nsecs := int64(secs * 1e9)
		timestamp := time.Unix(0, nsecs)
		if opts.showField("usecs") {
			buf.WriteString(timestamp.Format("[2006-01-02 15:04:05.000000] "))
		} else if opts.showField("msecs") {
			buf.WriteString(timestamp.Format("[2006-01-02 15:04:05.000] "))
		} else {
			buf.WriteString(timestamp.Format("[2006-01-02 15:04:05] "))
		}
	} else if opts.showField("time") {
		buf.WriteString(timestampfield)
		buf.WriteString(" ")
	}
	if opts.showField("host") {
		buf.WriteString(fmt.Sprintf("%-8s ", hostfield))
	}
	if opts.showField("level") {
		buf.WriteString(fmt.Sprintf("%-7s ", strings.ToUpper(levelfield)))
	}
	if opts.showField("pid") {
		// OnlyPid, _, _ := strings.Cut(pidfield, "/")
		buf.WriteString(fmt.Sprintf("%6s ", pidfield))
	}
	if opts.showField("service") {
		if opts.TruncateService {
			buf.WriteString(fmt.Sprintf("%-9.9s ", servicefield))
		} else {
			buf.WriteString(fmt.Sprintf("%-16s ", servicefield))
		}
	}
	if opts.showField("component") {
		if opts.TruncateComponent {
			buf.WriteString(fmt.Sprintf("%-15.15s ", componentfield))
		} else {
			buf.WriteString(fmt.Sprintf("%s\t", componentfield))
		}
	}
	if opts.showField("message") {
		var msgBuf strings.Builder
		for idx, message := range messagefields {
			if idx > 0 {
				msgBuf.WriteString("\n\t")
			}
			if opts.DequoteNewlines {
				message = strings.ReplaceAll(message, "\\n\\t", "\n\t")
				message = strings.ReplaceAll(message, "\\n", "\n\t")
			}
			msgBuf.WriteString(message)
		}
		message := msgBuf.String()
		if strings.Contains(message, "\n") {
			buf.WriteString("\n\t")
		}
		buf.WriteString(message)
	}
	buf.WriteString("\n")
	output = buf.String()
	return
}