Skip to content

Commit

Permalink
perf: improve line handler
Browse files Browse the repository at this point in the history
  • Loading branch information
nekrassov01 committed Mar 19, 2024
1 parent 00359b6 commit d3524e1
Showing 1 changed file with 78 additions and 44 deletions.
122 changes: 78 additions & 44 deletions handler.go
Original file line number Diff line number Diff line change
@@ -1,110 +1,144 @@
package parser

import (
"bytes"
"os"
"strconv"
"strings"

"github.com/mattn/go-isatty"
)

const size = 2024

// JSONLineHandler serializes log lines into JSON (NDJSON) format. It keywords the line number if specified.
// Labels and values are combined into key-value pairs, and the result is a single JSON object.
func JSONLineHandler(labels []string, values []string, _ bool) (string, error) {
b := &strings.Builder{}
b.WriteString("{")
func JSONLineHandler(labels, values []string, _ bool) (string, error) {
buf := &bytes.Buffer{}
buf.Grow(size)
buf.WriteByte('{')
for i, value := range values {
if i < len(labels) {
if i > 0 {
b.WriteString(",")
buf.WriteByte(',')
}
b.WriteString("\"")
b.WriteString(labels[i])
b.WriteString("\":")
b.WriteString(strconv.Quote(value))
buf.WriteByte('"')
buf.WriteString(labels[i])
buf.WriteString("\":")
buf.WriteByte('"')
writeEscapedString(buf, value)
buf.WriteByte('"')
}
}
b.WriteString("}")
return b.String(), nil
buf.WriteByte('}')
return buf.String(), nil
}

// PrettyJSONLineHandler enhances JSONLineHandler by formatting the output for readability. It uses indentation and new lines.
func PrettyJSONLineHandler(labels []string, values []string, _ bool) (string, error) {
b := &strings.Builder{}
b.WriteString("{\n")
func PrettyJSONLineHandler(labels, values []string, _ bool) (string, error) {
buf := &bytes.Buffer{}
buf.Grow(size)
buf.WriteString("{\n")
for i, value := range values {
if i < len(labels) {
if i > 0 {
b.WriteString(",\n")
buf.WriteString(",\n")
}
b.WriteString(" \"")
b.WriteString(labels[i])
b.WriteString("\": ")
b.WriteString(strconv.Quote(value))
buf.WriteString(" \"")
buf.WriteString(labels[i])
buf.WriteString("\": ")
buf.WriteByte('"')
writeEscapedString(buf, value)
buf.WriteByte('"')
}
}
b.WriteString("\n}")
return b.String(), nil
buf.WriteString("\n}")
return buf.String(), nil
}

// KeyValuePairLineHandler converts log lines into a space-separated string of key-value pairs.
func KeyValuePairLineHandler(labels []string, values []string, _ bool) (string, error) {
b := &strings.Builder{}
func KeyValuePairLineHandler(labels, values []string, _ bool) (string, error) {
buf := &bytes.Buffer{}
buf.Grow(size)
for i, value := range values {
if i < len(labels) {
if i > 0 {
b.WriteString(" ")
buf.WriteByte(' ')
}
b.WriteString(labels[i])
b.WriteString("=")
b.WriteString(strconv.Quote(value))
buf.WriteString(labels[i])
buf.WriteByte('=')
buf.WriteByte('"')
writeEscapedString(buf, value)
buf.WriteByte('"')
}
}
return b.String(), nil
return buf.String(), nil
}

// LTSVLineHandler formats log lines as LTSV (Labeled Tab-separated Values).
func LTSVLineHandler(labels []string, values []string, _ bool) (string, error) {
b := &strings.Builder{}
func LTSVLineHandler(labels, values []string, _ bool) (string, error) {
buf := &bytes.Buffer{}
buf.Grow(size)
for i, value := range values {
if i < len(labels) {
if i > 0 {
b.WriteString("\t")
buf.WriteByte('\t')
}
b.WriteString(labels[i])
b.WriteString(":")
buf.WriteString(labels[i])
buf.WriteByte(':')
if value == "" {
b.WriteString("-")
buf.WriteByte('-')
} else {
b.WriteString(value)
buf.WriteString(value)
}
}
}
return b.String(), nil
return buf.String(), nil
}

// TSVLineHandler formats log lines as TSV (Tab-separated Values).
func TSVLineHandler(labels []string, values []string, isFirst bool) (string, error) {
b := &strings.Builder{}
func TSVLineHandler(labels, values []string, isFirst bool) (string, error) {
buf := &bytes.Buffer{}
buf.Grow(size)
if isFirst {
header := strings.Join(labels, "\t")
if isatty.IsTerminal(os.Stdout.Fd()) {
header = "\033[1;37m" + header + "\033[0m"
}
b.WriteString(header)
b.WriteString("\n")
buf.WriteString(header)
buf.WriteByte('\n')
}
for i, value := range values {
if i < len(labels) {
if i > 0 {
b.WriteString("\t")
buf.WriteByte('\t')
}
if value == "" {
b.WriteString("-")
buf.WriteByte('-')
} else {
b.WriteString(value)
buf.WriteString(value)
}
}
}
return b.String(), nil
return buf.String(), nil
}

// EscapedString writes the string s to the given bytes.Buffer while properly escaping
// special characters (backslash, double quote, newline, carriage return, tab).
func writeEscapedString(buf *bytes.Buffer, s string) {
for _, r := range s {
switch r {
case '\\':
buf.WriteString("\\\\")
case '"':
buf.WriteString("\\\"")
case '\n':
buf.WriteString("\\n")
case '\r':
buf.WriteString("\\r")
case '\t':
buf.WriteString("\\t")
default:
buf.WriteRune(r)
}
}
}

0 comments on commit d3524e1

Please sign in to comment.