Skip to content

Commit 6c86d67

Browse files
authored
fix: executor logs (#781)
Signed-off-by: Timur Tuktamyshev <timur.tuktamyshev@flant.com>
1 parent b133f22 commit 6c86d67

File tree

2 files changed

+84
-18
lines changed

2 files changed

+84
-18
lines changed

pkg/executor/executor.go

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -216,52 +216,68 @@ func (pl *proxyLogger) Write(p []byte) (int, error) {
216216
return len(p), nil
217217
}
218218

219-
// logEntry.Log(log.FatalLevel, string(logLine))
220219
pl.mergeAndLogInputLog(pl.ctx, logMap, "hook")
221220

222221
return len(p), nil
223222
}
224223

225224
func (pl *proxyLogger) writerScanner(p []byte) {
226225
scanner := bufio.NewScanner(bytes.NewReader(p))
227-
228-
// Set the buffer size to the maximum token size to avoid buffer overflows
229226
scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize)
230227

231-
// Define a split function to split the input into chunks of up to 64KB
228+
var jsonBuf []string
229+
// Split large entries into chunks
232230
chunkSize := bufio.MaxScanTokenSize // 64KB
233231
splitFunc := func(data []byte, atEOF bool) (int, []byte, error) {
234232
if len(data) >= chunkSize {
235233
return chunkSize, data[:chunkSize], nil
236234
}
237-
238235
return bufio.ScanLines(data, atEOF)
239236
}
240-
241-
// Use the custom split function to split the input
242237
scanner.Split(splitFunc)
243-
244238
// Scan the input and write it to the logger using the specified print function
245239
for scanner.Scan() {
246-
// prevent empty logging
247-
str := strings.TrimSpace(scanner.Text())
248-
if str == "" {
240+
// Prevent empty logging
241+
line := strings.TrimSpace(scanner.Text())
242+
if line == "" {
249243
continue
250244
}
251245

252-
if len(str) > 10000 {
253-
str = fmt.Sprintf("%s:truncated", str[:10000])
246+
if len(jsonBuf) > 0 || strings.HasPrefix(line, "{") {
247+
jsonBuf = append(jsonBuf, line)
248+
joined := strings.Join(jsonBuf, "\n")
249+
if err := pl.tryLogJSON(joined); err == nil {
250+
jsonBuf = nil
251+
}
252+
continue
254253
}
255254

256-
pl.logger.Info(str)
257-
}
255+
if len(line) > 10000 {
256+
line = fmt.Sprintf("%s:truncated", line[:10000])
257+
}
258258

259+
if err := pl.tryLogJSON(line); err == nil {
260+
continue
261+
}
262+
pl.logger.Info(line)
263+
}
259264
// If there was an error while scanning the input, log an error
260265
if err := scanner.Err(); err != nil {
261266
pl.logger.Error("reading from scanner", log.Err(err))
262267
}
263268
}
264269

270+
// tryLogJSON tries to parse the string as JSON and log it if it succeeds
271+
func (pl *proxyLogger) tryLogJSON(s string) error {
272+
var m map[string]interface{}
273+
err := json.Unmarshal([]byte(s), &m)
274+
if err == nil {
275+
pl.mergeAndLogInputLog(pl.ctx, m, "hook")
276+
return nil
277+
}
278+
return fmt.Errorf("failed to parse json: %w", err)
279+
}
280+
265281
// level = level
266282
// msg = msg
267283
// prefix for all fields hook_

pkg/executor/executor_test.go

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ package executor
33
import (
44
"bytes"
55
"context"
6+
"encoding/json"
7+
"fmt"
68
"io"
79
"math/rand/v2"
810
"os"
911
"regexp"
12+
"strings"
1013
"testing"
1114
"time"
1215

@@ -69,7 +72,6 @@ func TestRunAndLogLines(t *testing.T) {
6972

7073
_, err = ex.RunAndLogLines(context.Background(), map[string]string{"a": "b"})
7174
assert.NoError(t, err)
72-
7375
reg := regexp.MustCompile(`{"level":"info","msg":"hook result","a":"b","hook":{"truncated":".*:truncated"},"output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"`)
7476
assert.Regexp(t, reg, buf.String())
7577

@@ -105,9 +107,26 @@ func TestRunAndLogLines(t *testing.T) {
105107

106108
_, err := ex.RunAndLogLines(context.Background(), map[string]string{"a": "b"})
107109
assert.NoError(t, err)
110+
lines := strings.Split(strings.TrimSpace(buf.String()), "\n")
108111

109-
assert.Equal(t, buf.String(), `{"level":"debug","msg":"json log line not map[string]interface{}","source":"executor/executor.go:211","a":"b","line":["a","b","c"],"output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n"+
110-
`{"level":"info","msg":"[\"a\",\"b\",\"c\"]\n","source":"executor/executor.go:214","a":"b","output":"stdout","time":"2006-01-02T15:04:05Z"}`+"\n")
112+
var debugLine map[string]interface{}
113+
err = json.Unmarshal([]byte(lines[0]), &debugLine)
114+
assert.NoError(t, err)
115+
assert.Equal(t, "debug", debugLine["level"])
116+
assert.Equal(t, "json log line not map[string]interface{}", debugLine["msg"])
117+
assert.Equal(t, []interface{}{"a", "b", "c"}, debugLine["line"])
118+
assert.Equal(t, "b", debugLine["a"])
119+
assert.Equal(t, "stdout", debugLine["output"])
120+
assert.Equal(t, "2006-01-02T15:04:05Z", debugLine["time"])
121+
122+
var infoLine map[string]interface{}
123+
err = json.Unmarshal([]byte(lines[1]), &infoLine)
124+
assert.NoError(t, err)
125+
assert.Equal(t, "info", infoLine["level"])
126+
assert.Equal(t, "[\"a\",\"b\",\"c\"]\n", infoLine["msg"])
127+
assert.Equal(t, "b", infoLine["a"])
128+
assert.Equal(t, "stdout", infoLine["output"])
129+
assert.Equal(t, "2006-01-02T15:04:05Z", infoLine["time"])
111130
buf.Reset()
112131
})
113132

@@ -189,6 +208,37 @@ c d
189208

190209
buf.Reset()
191210
})
211+
212+
t.Run("multiline json several logs", func(t *testing.T) {
213+
logger.SetLevel(log.LevelInfo)
214+
arg := `
215+
{"a":"b","c":"d"}
216+
{"b":"c","d":"a"}
217+
{"c":"d","a":"b"}
218+
{"d":"a","b":"c"}
219+
plain text
220+
{
221+
"a":"b",
222+
"c":"d"
223+
}`
224+
ex := NewExecutor("", "echo", []string{arg}, []string{}).
225+
WithLogProxyHookJSON(true).
226+
WithLogger(logger)
227+
228+
_, err := ex.RunAndLogLines(context.Background(), map[string]string{"foor": "baar"})
229+
assert.NoError(t, err)
230+
fmt.Println("actual", buf.String())
231+
expected := ""
232+
expected += `{"level":"info","msg":"hook result","foor":"baar","hook_a":"b","hook_c":"d","output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}` + "\n"
233+
expected += `{"level":"info","msg":"hook result","foor":"baar","hook_b":"c","hook_d":"a","output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}` + "\n"
234+
expected += `{"level":"info","msg":"hook result","foor":"baar","hook_a":"b","hook_c":"d","output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}` + "\n"
235+
expected += `{"level":"info","msg":"hook result","foor":"baar","hook_b":"c","hook_d":"a","output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}` + "\n"
236+
expected += `{"level":"info","msg":"plain text","foor":"baar","output":"stdout","time":"2006-01-02T15:04:05Z"}` + "\n"
237+
expected += `{"level":"info","msg":"hook result","foor":"baar","hook_a":"b","hook_c":"d","output":"stdout","proxyJsonLog":true,"time":"2006-01-02T15:04:05Z"}` + "\n"
238+
assert.Equal(t, expected, buf.String())
239+
240+
buf.Reset()
241+
})
192242
}
193243

194244
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

0 commit comments

Comments
 (0)