Skip to content

Commit 740ed5e

Browse files
authored
feat(#63): support custom log levels (#65)
1 parent d1b9829 commit 740ed5e

File tree

8 files changed

+90
-20
lines changed

8 files changed

+90
-20
lines changed

Makefile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ install:
2222
go install ./cmd/jlv
2323
.PHONY: install
2424

25-
lint: bin/golangci-lint
26-
./bin/golangci-lint run
25+
lint: bin/golangci-lint-${GOLANG_CI_LINT_VER}
26+
./bin/golangci-lint-${GOLANG_CI_LINT_VER} run
2727
.PHONY: lint
2828

2929
test:
@@ -41,8 +41,9 @@ vendor:
4141
go mod vendor
4242
.PHONY: vendor
4343

44-
bin/golangci-lint:
44+
bin/golangci-lint-${GOLANG_CI_LINT_VER}:
4545
curl \
4646
-sSfL \
4747
https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
48-
| sh -s $(GOLANG_CI_LINT_VER)
48+
| sh -s $(GOLANG_CI_LINT_VER)
49+
mv ./bin/golangci-lint ./bin/golangci-lint-${GOLANG_CI_LINT_VER}

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,19 @@ Example configuration:
160160
"$.custom"
161161
],
162162
"width": 0
163-
}
164-
]
163+
},
164+
],
165+
// Mapping of log level.
166+
// Possible values: none, trace, debug, info, warn, error, panic, fatal.
167+
"customLevelMapping": {
168+
// Replace "10" to "trace" in log level.
169+
"10": "trace",
170+
"20": "debug",
171+
"30": "info",
172+
"40": "warn",
173+
"50": "error",
174+
"60": "fatal"
175+
}
165176
}
166177
```
167178

assets/example.log

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@
3030
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "One fails forward toward success.","author": "Charles Kettering"}
3131
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "From small beginnings come great things.","author": null}
3232
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "Learning is a treasure that will follow its owner everywhere","author": "Chinese proverb"}
33-
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "Be as you wish to seem.","author": "Socrates"}
34-
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "The world is always in movement.","author": "V. Naipaul"}
35-
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "Never mistake activity for achievement.","author": "John Wooden"}
36-
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "What worries you masters you.","author": "Haddon Robinson"}
37-
{"time":"1970-01-01T00:00:00.00","level":"INFO","message": "One faces the future with ones past.","author": "Pearl Buck"}
38-
{"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": {}}}}
33+
{"time":"1970-01-01T00:00:00.00","level":"60","message": "Be as you wish to seem.","author": "Socrates"}
34+
{"time":"1970-01-01T00:00:00.00","level":"50","message": "The world is always in movement.","author": "V. Naipaul"}
35+
{"time":"1970-01-01T00:00:00.00","level":"40","message": "Never mistake activity for achievement.","author": "John Wooden"}
36+
{"time":"1970-01-01T00:00:00.00","level":"30","message": "What worries you masters you.","author": "Haddon Robinson"}
37+
{"time":"1970-01-01T00:00:00.00","level":"20","message": "One faces the future with ones past.","author": "Pearl Buck"}
38+
{"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": {}}}}
3939
{"time":"1970-01-01T00:00:00.00","level":"VERBOSE","message": "Goals are the fuel in the furnace of achievement.","author": "Brian Tracy"}
4040
{"time":"1970-01-01T00:00:00.00","level":"FATAL","message": "Be the chief but never the lord.","author": "Lao Tzu"}
4141
{"time":"1970-01-01T00:00:00.00","level":"PANIC","message": "Fate is in your hands and no one elses","author": "Byron Pulsifer"}

internal/pkg/config/config.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ type Config struct {
1919
Path string `json:"-"`
2020

2121
Fields []Field `json:"fields" validate:"min=1"`
22+
23+
CustomLevelMapping map[string]string `json:"customLevelMapping"`
2224
}
2325

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

84+
if cfg.CustomLevelMapping == nil {
85+
cfg.CustomLevelMapping = map[string]string{}
86+
}
87+
8188
return cfg, nil
8289
}
8390

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

123130
return cfg, nil
124131
}
132+
133+
// GetDefaultCustomLevelMapping returns the custom mapping of levels.
134+
func GetDefaultCustomLevelMapping() map[string]string {
135+
// https://github.com/pinojs/pino/blob/main/docs/api.md#loggerlevels-object
136+
return map[string]string{
137+
"10": "trace",
138+
"20": "debug",
139+
"30": "info",
140+
"40": "warn",
141+
"50": "error",
142+
"60": "fatal",
143+
}
144+
}

internal/pkg/config/config_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,15 @@ func ExampleGetDefaultConfig() {
133133
// ],
134134
// "width": 0
135135
// }
136-
// ]
136+
// ],
137+
// "customLevelMapping": {
138+
// "10": "trace",
139+
// "20": "debug",
140+
// "30": "info",
141+
// "40": "warn",
142+
// "50": "error",
143+
// "60": "fatal"
144+
// }
137145
// }
138146
}
139147

internal/pkg/source/entry.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ func (entries LogEntries) Rows() []table.Row {
6767
return rows
6868
}
6969

70-
func parseField(parsedLine any, field config.Field) string {
70+
func parseField(
71+
parsedLine any,
72+
field config.Field,
73+
cfg *config.Config,
74+
) string {
7175
for _, ref := range field.References {
7276
foundField, err := jsonpath.Read(parsedLine, ref)
7377
if err != nil {
@@ -87,7 +91,7 @@ func parseField(parsedLine any, field config.Field) string {
8791
unquotedField = string(jsonField)
8892
}
8993

90-
return formatField(unquotedField, field.Kind)
94+
return formatField(unquotedField, field.Kind, cfg)
9195
}
9296

9397
return "-"
@@ -97,6 +101,7 @@ func parseField(parsedLine any, field config.Field) string {
97101
func formatField(
98102
value string,
99103
kind config.FieldKind,
104+
cfg *config.Config,
100105
) string {
101106
value = strings.TrimSpace(value)
102107

@@ -109,7 +114,7 @@ func formatField(
109114
case config.FieldKindMessage:
110115
return formatMessage(value)
111116
case config.FieldKindLevel:
112-
return string(ParseLevel(formatMessage(value)))
117+
return string(ParseLevel(formatMessage(value), cfg.CustomLevelMapping))
113118
case config.FieldKindTime:
114119
return formatMessage(value)
115120
case config.FieldKindNumericTime:
@@ -145,7 +150,7 @@ func ParseLogEntry(
145150
fields := make([]string, 0, len(cfg.Fields))
146151

147152
for _, f := range cfg.Fields {
148-
fields = append(fields, parseField(parsedLine, f))
153+
fields = append(fields, parseField(parsedLine, f, cfg))
149154
}
150155

151156
return LogEntry{

internal/pkg/source/level.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ import "strings"
66
type Level string
77

88
// ParseLevel parses level from the text value.
9-
func ParseLevel(value string) Level {
9+
//
10+
// nolint: cyclop // Switch-case.
11+
func ParseLevel(value string, customMapping map[string]string) Level {
1012
value = strings.ToLower(value)
1113
value = strings.TrimSpace(value)
1214

15+
if customLevel, ok := customMapping[value]; ok {
16+
return Level(customLevel)
17+
}
18+
1319
switch {
1420
case value == "":
1521
return LevelUnknown

internal/pkg/source/level_test.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/stretchr/testify/assert"
77

8+
"github.com/hedhyw/json-log-viewer/internal/pkg/config"
89
"github.com/hedhyw/json-log-viewer/internal/pkg/source"
910
)
1011

@@ -50,6 +51,24 @@ func TestParseLevel(t *testing.T) {
5051
}, {
5152
Input: " Unknown\t\n",
5253
Expected: source.Level("unknown"),
54+
}, {
55+
Input: "10",
56+
Expected: source.LevelTrace,
57+
}, {
58+
Input: "20",
59+
Expected: source.LevelDebug,
60+
}, {
61+
Input: "30",
62+
Expected: source.LevelInfo,
63+
}, {
64+
Input: "40",
65+
Expected: source.LevelWarning,
66+
}, {
67+
Input: "50",
68+
Expected: source.LevelError,
69+
}, {
70+
Input: "60",
71+
Expected: source.LevelFatal,
5372
}}
5473

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

61-
actual := source.ParseLevel(testCase.Input)
80+
actual := source.ParseLevel(testCase.Input, config.GetDefaultCustomLevelMapping())
6281
assert.Equal(t, testCase.Expected, actual)
6382
})
6483
}

0 commit comments

Comments
 (0)