Skip to content

Commit

Permalink
feat(#63): support custom log levels
Browse files Browse the repository at this point in the history
  • Loading branch information
hedhyw committed May 23, 2024
1 parent d1b9829 commit 6f6b7ac
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 20 deletions.
9 changes: 5 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ install:
go install ./cmd/jlv
.PHONY: install

lint: bin/golangci-lint
./bin/golangci-lint run
lint: bin/golangci-lint-${GOLANG_CI_LINT_VER}
./bin/golangci-lint-${GOLANG_CI_LINT_VER} run
.PHONY: lint

test:
Expand All @@ -41,8 +41,9 @@ vendor:
go mod vendor
.PHONY: vendor

bin/golangci-lint:
bin/golangci-lint-${GOLANG_CI_LINT_VER}:
curl \
-sSfL \
https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
| sh -s $(GOLANG_CI_LINT_VER)
| sh -s $(GOLANG_CI_LINT_VER)
mv ./bin/golangci-lint ./bin/golangci-lint-${GOLANG_CI_LINT_VER}
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,19 @@ Example configuration:
"$.custom"
],
"width": 0
}
]
},
],
// Mapping of log level.
// Possible values: none, trace, debug, info, warn, error, panic, fatal.
"customLevelMapping": {
// Replace "10" to "trace" in log level.
"10": "trace",
"20": "debug",
"30": "info",
"40": "warn",
"50": "error",
"60": "fatal"
}
}
```

Expand Down
12 changes: 6 additions & 6 deletions assets/example.log
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "One fails forward toward success.","author": "Charles Kettering"}
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "From small beginnings come great things.","author": null}
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "Learning is a treasure that will follow its owner everywhere","author": "Chinese proverb"}
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "Be as you wish to seem.","author": "Socrates"}
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "The world is always in movement.","author": "V. Naipaul"}
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "Never mistake activity for achievement.","author": "John Wooden"}
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "What worries you masters you.","author": "Haddon Robinson"}
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "One faces the future with ones past.","author": "Pearl Buck"}
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "Big json.", "array": [0, 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], "nested": {"time":"1970-01-01T00:00:00.00","level":"INFO","message": "Big json.", "array": [0, 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], "nested": {"time":"1970-01-01T00:00:00.00","level":"INFO","message": "Big json.", "array": [0, 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], "nested": {}}}}
{"time":"1970-01-01T00:00:00.00","level":"60","message": "Be as you wish to seem.","author": "Socrates"}
{"time":"1970-01-01T00:00:00.00","level":"50","message": "The world is always in movement.","author": "V. Naipaul"}
{"time":"1970-01-01T00:00:00.00","level":"40","message": "Never mistake activity for achievement.","author": "John Wooden"}
{"time":"1970-01-01T00:00:00.00","level":"30","message": "What worries you masters you.","author": "Haddon Robinson"}
{"time":"1970-01-01T00:00:00.00","level":"20","message": "One faces the future with ones past.","author": "Pearl Buck"}
{"time":"1970-01-01T00:00:00.00","level":"10","message": "Big json.", "array": [0, 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], "nested": {"time":"1970-01-01T00:00:00.00","level":"INFO","message": "Big json.", "array": [0, 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], "nested": {"time":"1970-01-01T00:00:00.00","level":"INFO","message": "Big json.", "array": [0, 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], "nested": {}}}}
{"time":"1970-01-01T00:00:00.00","level":"VERBOSE","message": "Goals are the fuel in the furnace of achievement.","author": "Brian Tracy"}
{"time":"1970-01-01T00:00:00.00","level":"FATAL","message": "Be the chief but never the lord.","author": "Lao Tzu"}
{"time":"1970-01-01T00:00:00.00","level":"PANIC","message": "Fate is in your hands and no one elses","author": "Byron Pulsifer"}
Expand Down
22 changes: 21 additions & 1 deletion internal/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type Config struct {
Path string `json:"-"`

Fields []Field `json:"fields" validate:"min=1"`

CustomLevelMapping map[string]string `json:"customLevelMapping"`
}

// FieldKind describes the type of the log field.
Expand Down Expand Up @@ -47,7 +49,8 @@ type Field struct {
// GetDefaultConfig returns the configuration with default values.
func GetDefaultConfig() *Config {
return &Config{
Path: "default",
Path: "default",
CustomLevelMapping: GetDefaultCustomLevelMapping(),
Fields: []Field{{
Title: "Time",
Kind: FieldKindNumericTime,
Expand Down Expand Up @@ -78,6 +81,10 @@ func Read(paths ...string) (*Config, error) {
return nil, fmt.Errorf("validating config: %s: %w", cfg.Path, err)
}

if cfg.CustomLevelMapping == nil {
cfg.CustomLevelMapping = map[string]string{}
}

return cfg, nil
}

Expand Down Expand Up @@ -122,3 +129,16 @@ func readConfigFromFile(path string) (cfg *Config, err error) {

return cfg, nil
}

// GetDefaultCustomLevelMapping returns the custom mapping of levels.
func GetDefaultCustomLevelMapping() map[string]string {
// https://github.com/pinojs/pino/blob/main/docs/api.md#loggerlevels-object
return map[string]string{
"10": "trace",
"20": "debug",
"30": "info",
"40": "warn",
"50": "error",
"60": "fatal",
}
}
10 changes: 9 additions & 1 deletion internal/pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,15 @@ func ExampleGetDefaultConfig() {
// ],
// "width": 0
// }
// ]
// ],
// "customLevelMapping": {
// "10": "trace",
// "20": "debug",
// "30": "info",
// "40": "warn",
// "50": "error",
// "60": "fatal"
// }
// }
}

Expand Down
13 changes: 9 additions & 4 deletions internal/pkg/source/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ func (entries LogEntries) Rows() []table.Row {
return rows
}

func parseField(parsedLine any, field config.Field) string {
func parseField(
parsedLine any,
field config.Field,
cfg *config.Config,
) string {
for _, ref := range field.References {
foundField, err := jsonpath.Read(parsedLine, ref)
if err != nil {
Expand All @@ -87,7 +91,7 @@ func parseField(parsedLine any, field config.Field) string {
unquotedField = string(jsonField)
}

return formatField(unquotedField, field.Kind)
return formatField(unquotedField, field.Kind, cfg)
}

return "-"
Expand All @@ -97,6 +101,7 @@ func parseField(parsedLine any, field config.Field) string {
func formatField(
value string,
kind config.FieldKind,
cfg *config.Config,
) string {
value = strings.TrimSpace(value)

Expand All @@ -109,7 +114,7 @@ func formatField(
case config.FieldKindMessage:
return formatMessage(value)
case config.FieldKindLevel:
return string(ParseLevel(formatMessage(value)))
return string(ParseLevel(formatMessage(value), cfg.CustomLevelMapping))
case config.FieldKindTime:
return formatMessage(value)
case config.FieldKindNumericTime:
Expand Down Expand Up @@ -145,7 +150,7 @@ func ParseLogEntry(
fields := make([]string, 0, len(cfg.Fields))

for _, f := range cfg.Fields {
fields = append(fields, parseField(parsedLine, f))
fields = append(fields, parseField(parsedLine, f, cfg))
}

return LogEntry{
Expand Down
8 changes: 7 additions & 1 deletion internal/pkg/source/level.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ import "strings"
type Level string

// ParseLevel parses level from the text value.
func ParseLevel(value string) Level {
//
// nolint: cyclop // Switch-case.
func ParseLevel(value string, customMapping map[string]string) Level {
value = strings.ToLower(value)
value = strings.TrimSpace(value)

if customLevel, ok := customMapping[value]; ok {
return Level(customLevel)
}

switch {
case value == "":
return LevelUnknown
Expand Down
21 changes: 20 additions & 1 deletion internal/pkg/source/level_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/hedhyw/json-log-viewer/internal/pkg/config"
"github.com/hedhyw/json-log-viewer/internal/pkg/source"
)

Expand Down Expand Up @@ -50,6 +51,24 @@ func TestParseLevel(t *testing.T) {
}, {
Input: " Unknown\t\n",
Expected: source.Level("unknown"),
}, {
Input: "10",
Expected: source.LevelTrace,
}, {
Input: "20",
Expected: source.LevelDebug,
}, {
Input: "30",
Expected: source.LevelInfo,
}, {
Input: "40",
Expected: source.LevelWarning,
}, {
Input: "50",
Expected: source.LevelError,
}, {
Input: "60",
Expected: source.LevelFatal,
}}

for _, testCase := range testCases {
Expand All @@ -58,7 +77,7 @@ func TestParseLevel(t *testing.T) {
t.Run(testCase.Input, func(t *testing.T) {
t.Parallel()

actual := source.ParseLevel(testCase.Input)
actual := source.ParseLevel(testCase.Input, config.GetDefaultCustomLevelMapping())
assert.Equal(t, testCase.Expected, actual)
})
}
Expand Down

0 comments on commit 6f6b7ac

Please sign in to comment.