mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 19:09:58 +00:00
TUN-9371: Add logging format as JSON
Closes TUN-9371
This commit is contained in:
@@ -17,6 +17,7 @@ type Config struct {
|
||||
|
||||
type ConsoleConfig struct {
|
||||
noColor bool
|
||||
asJSON bool
|
||||
}
|
||||
|
||||
type FileConfig struct {
|
||||
@@ -48,6 +49,7 @@ func createDefaultConfig() Config {
|
||||
return Config{
|
||||
ConsoleConfig: &ConsoleConfig{
|
||||
noColor: false,
|
||||
asJSON: false,
|
||||
},
|
||||
FileConfig: &FileConfig{
|
||||
Dirname: "",
|
||||
@@ -67,11 +69,12 @@ func createDefaultConfig() Config {
|
||||
func CreateConfig(
|
||||
minLevel string,
|
||||
disableTerminal bool,
|
||||
formatJSON bool,
|
||||
rollingLogPath, nonRollingLogFilePath string,
|
||||
) *Config {
|
||||
var console *ConsoleConfig
|
||||
if !disableTerminal {
|
||||
console = createConsoleConfig()
|
||||
console = createConsoleConfig(formatJSON)
|
||||
}
|
||||
|
||||
var file *FileConfig
|
||||
@@ -95,9 +98,10 @@ func CreateConfig(
|
||||
}
|
||||
}
|
||||
|
||||
func createConsoleConfig() *ConsoleConfig {
|
||||
func createConsoleConfig(formatJSON bool) *ConsoleConfig {
|
||||
return &ConsoleConfig{
|
||||
noColor: false,
|
||||
asJSON: formatJSON,
|
||||
}
|
||||
}
|
||||
|
||||
|
37
logger/console.go
Normal file
37
logger/console.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
|
||||
// consoleWriter allows us the simplicity to prevent duplicate json keys in the logger events reported.
|
||||
//
|
||||
// By default zerolog constructs the json event in parts by appending each additional key after the first. It
|
||||
// doesn't have any internal state or struct of the json message representation so duplicate keys can be
|
||||
// inserted without notice and no pruning will occur before writing the log event out to the io.Writer.
|
||||
//
|
||||
// To help prevent these duplicate keys, we will decode the json log event and then immediately re-encode it
|
||||
// again as writing it to the output io.Writer. Since we encode it to a map[string]any, duplicate keys
|
||||
// are pruned. We pay the cost of decoding and encoding the log event for each time, but helps prevent
|
||||
// us from needing to worry about adding duplicate keys in the log event from different areas of code.
|
||||
type consoleWriter struct {
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func (c *consoleWriter) Write(p []byte) (n int, err error) {
|
||||
var evt map[string]any
|
||||
d := json.NewDecoder(bytes.NewReader(p))
|
||||
d.UseNumber()
|
||||
err = d.Decode(&evt)
|
||||
if err != nil {
|
||||
return n, fmt.Errorf("cannot decode event: %s", err)
|
||||
}
|
||||
e := json.NewEncoder(c.out)
|
||||
return len(p), e.Encode(evt)
|
||||
}
|
33
logger/console_test.go
Normal file
33
logger/console_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
func TestConsoleLoggerDuplicateKeys(t *testing.T) {
|
||||
r := bytes.NewBuffer(make([]byte, 500))
|
||||
logger := zerolog.New(&consoleWriter{out: r}).With().Timestamp().Logger()
|
||||
logger.Debug().Str("test", "1234").Int("number", 45).Str("test", "5678").Msg("log message")
|
||||
|
||||
event, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !strings.Contains(event, "\"test\":\"5678\"") {
|
||||
t.Errorf("log event missing key 'test': %s", event)
|
||||
}
|
||||
if !strings.Contains(event, "\"number\":45") {
|
||||
t.Errorf("log event missing key 'number': %s", event)
|
||||
}
|
||||
if !strings.Contains(event, "\"time\":") {
|
||||
t.Errorf("log event missing key 'time': %s", event)
|
||||
}
|
||||
if !strings.Contains(event, "\"level\":\"debug\"") {
|
||||
t.Errorf("log event missing key 'level': %s", event)
|
||||
}
|
||||
}
|
@@ -149,10 +149,21 @@ func createFromContext(
|
||||
logLevel := c.String(logLevelFlagName)
|
||||
logFile := c.String(cfdflags.LogFile)
|
||||
logDirectory := c.String(logDirectoryFlagName)
|
||||
var logFormatJSON bool
|
||||
switch c.String(cfdflags.LogFormatOutput) {
|
||||
case cfdflags.LogFormatOutputValueJSON:
|
||||
logFormatJSON = true
|
||||
case cfdflags.LogFormatOutputValueDefault:
|
||||
// "default" and unset use the same logger output format
|
||||
fallthrough
|
||||
default:
|
||||
logFormatJSON = false
|
||||
}
|
||||
|
||||
loggerConfig := CreateConfig(
|
||||
logLevel,
|
||||
disableTerminal,
|
||||
logFormatJSON,
|
||||
logDirectory,
|
||||
logFile,
|
||||
)
|
||||
@@ -177,6 +188,9 @@ func Create(loggerConfig *Config) *zerolog.Logger {
|
||||
}
|
||||
|
||||
func createConsoleLogger(config ConsoleConfig) io.Writer {
|
||||
if config.asJSON {
|
||||
return &consoleWriter{out: os.Stderr}
|
||||
}
|
||||
consoleOut := os.Stderr
|
||||
return zerolog.ConsoleWriter{
|
||||
Out: colorable.NewColorable(consoleOut),
|
||||
|
Reference in New Issue
Block a user