Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#63): support custom log levels #65

Merged
merged 1 commit into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading