Skip to content

Commit eed39f3

Browse files
committed
Delay merging fields until they're used
Also fix a newly introduced logic issue in the merge making the tests pass.
1 parent d5ea65a commit eed39f3

File tree

5 files changed

+36
-34
lines changed

5 files changed

+36
-34
lines changed

‎.github/workflows/test.yml‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ jobs:
1818
- name: Install staticcheck
1919
run: go install honnef.co/go/tools/cmd/staticcheck@latest
2020
shell: bash
21-
- name: Install golint
22-
run: go install golang.org/x/lint/golint@latest
23-
shell: bash
2421
- name: Update PATH
2522
run: echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
2623
shell: bash
@@ -34,7 +31,5 @@ jobs:
3431
run: go vet ./...
3532
- name: Staticcheck
3633
run: staticcheck ./...
37-
- name: Lint
38-
run: golint ./...
3934
- name: Test
4035
run: go test -race ./...

‎entry.go‎

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,26 @@ var Now = time.Now
1818
type EntryFields struct {
1919
Logger *Logger `json:"-"`
2020
Fields Fields `json:"-"`
21-
start time.Time
2221
}
2322

2423
// Entry holds a Entry with a Message, Timestamp and a Level.
2524
// This is what is actually logged.
2625
type Entry struct {
2726
*EntryFields
28-
FieldsUnique Fields `json:"-"`
29-
Level Level `json:"level"`
30-
Timestamp time.Time `json:"timestamp"`
31-
Message string `json:"message"`
27+
Level Level `json:"level"`
28+
Timestamp time.Time `json:"timestamp"`
29+
Message string `json:"message"`
30+
}
31+
32+
// FieldsDistinct returns a list of fields with duplicate names removed,
33+
// keeping the last.
34+
func (e *EntryFields) FieldsDistinct() Fields {
35+
return e.distinctFieldsLastByName()
3236
}
3337

3438
func (e Entry) MarshalJSON() ([]byte, error) {
3539
fields := make(map[string]any)
36-
for _, f := range e.FieldsUnique {
40+
for _, f := range e.FieldsDistinct() {
3741
fields[f.Name] = f.Value
3842
}
3943

@@ -155,8 +159,9 @@ func (e *EntryFields) Fatalf(msg string, v ...any) {
155159
e.Fatal(fmt.Sprintf(msg, v...))
156160
}
157161

158-
// mergedFields returns the fields list collapsed into a single slice.
159-
func (e *EntryFields) mergedFields() Fields {
162+
// distinctFieldsLastByName returns the fields with duplicate names removed,
163+
// keeping the rightmost field (last) with a given name.
164+
func (e *EntryFields) distinctFieldsLastByName() Fields {
160165
fields := make(Fields, 0, len(e.Fields))
161166
for i := len(e.Fields) - 1; i >= 0; i-- {
162167
f := e.Fields[i]
@@ -168,7 +173,10 @@ func (e *EntryFields) mergedFields() Fields {
168173
}
169174
}
170175
if !seen {
171-
fields = append(fields, f)
176+
// Insert first.
177+
fields = append(fields, Field{})
178+
copy(fields[1:], fields[:])
179+
fields[0] = f
172180
}
173181
}
174182

@@ -182,10 +190,9 @@ func (e *EntryFields) mergedFields() Fields {
182190
// finalize returns a copy of the Entry with Fields merged.
183191
func (e *EntryFields) finalize(level Level, msg string) *Entry {
184192
return &Entry{
185-
EntryFields: e,
186-
FieldsUnique: e.mergedFields(),
187-
Level: level,
188-
Message: msg,
189-
Timestamp: Now(),
193+
EntryFields: e,
194+
Level: level,
195+
Message: msg,
196+
Timestamp: Now(),
190197
}
191198
}

‎entry_test.go‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ func TestEntry_WithFields(t *testing.T) {
1313
qt.Assert(t, a.Fields, qt.IsNil)
1414

1515
b := a.WithFields(Fields{{"foo", "bar"}})
16-
qt.Assert(t, a.mergedFields(), qt.IsNil)
16+
qt.Assert(t, a.distinctFieldsLastByName(), qt.IsNil)
1717

1818
c := a.WithFields(Fields{{"foo", "hello"}, {"bar", "world"}})
1919
d := c.WithFields(Fields{{"baz", "jazz"}})
20-
qt.Assert(t, b.mergedFields(), qt.DeepEquals, Fields{{"foo", "bar"}})
21-
qt.Assert(t, c.mergedFields(), qt.DeepEquals, Fields{{"foo", "hello"}, {"bar", "world"}})
22-
qt.Assert(t, d.mergedFields(), qt.DeepEquals, Fields{{"foo", "hello"}, {"bar", "world"}, {"baz", "jazz"}})
20+
qt.Assert(t, b.distinctFieldsLastByName(), qt.DeepEquals, Fields{{"foo", "bar"}})
21+
qt.Assert(t, c.distinctFieldsLastByName(), qt.DeepEquals, Fields{{"foo", "hello"}, {"bar", "world"}})
22+
qt.Assert(t, d.distinctFieldsLastByName(), qt.DeepEquals, Fields{{"foo", "hello"}, {"bar", "world"}, {"baz", "jazz"}})
2323

2424
e := c.finalize(InfoLevel, "upload")
2525
qt.Assert(t, "upload", qt.Equals, e.Message)
@@ -31,24 +31,24 @@ func TestEntry_WithFields(t *testing.T) {
3131
func TestEntry_WithField(t *testing.T) {
3232
a := NewEntry(nil)
3333
b := a.WithField("foo", "baz").WithField("foo", "bar")
34-
qt.Assert(t, a.mergedFields(), qt.IsNil)
35-
qt.Assert(t, b.mergedFields(), qt.DeepEquals, Fields{{"foo", "bar"}})
34+
qt.Assert(t, a.distinctFieldsLastByName(), qt.IsNil)
35+
qt.Assert(t, b.distinctFieldsLastByName(), qt.DeepEquals, Fields{{"foo", "bar"}})
3636
}
3737

3838
func TestEntry_WithError(t *testing.T) {
3939
a := NewEntry(nil)
4040
b := a.WithError(fmt.Errorf("boom"))
41-
qt.Assert(t, a.mergedFields(), qt.IsNil)
42-
qt.Assert(t, b.mergedFields(), qt.DeepEquals, Fields{{"error", "boom"}})
41+
qt.Assert(t, a.distinctFieldsLastByName(), qt.IsNil)
42+
qt.Assert(t, b.distinctFieldsLastByName(), qt.DeepEquals, Fields{{"error", "boom"}})
4343
}
4444

4545
func TestEntry_WithError_fields(t *testing.T) {
4646
a := NewEntry(nil)
4747
b := a.WithError(errFields("boom"))
48-
qt.Assert(t, a.mergedFields(), qt.IsNil)
48+
qt.Assert(t, a.distinctFieldsLastByName(), qt.IsNil)
4949
qt.Assert(t,
5050

51-
b.mergedFields(), qt.DeepEquals, Fields{
51+
b.distinctFieldsLastByName(), qt.DeepEquals, Fields{
5252
{"error", "boom"},
5353
{"reason", "timeout"},
5454
})
@@ -58,14 +58,14 @@ func TestEntry_WithError_fields(t *testing.T) {
5858
func TestEntry_WithError_nil(t *testing.T) {
5959
a := NewEntry(nil)
6060
b := a.WithError(nil)
61-
qt.Assert(t, a.mergedFields(), qt.IsNil)
62-
qt.Assert(t, b.mergedFields(), qt.IsNil)
61+
qt.Assert(t, a.distinctFieldsLastByName(), qt.IsNil)
62+
qt.Assert(t, b.distinctFieldsLastByName(), qt.IsNil)
6363
}
6464

6565
func TestEntry_WithDuration(t *testing.T) {
6666
a := NewEntry(nil)
6767
b := a.WithDuration(time.Second * 2)
68-
qt.Assert(t, b.mergedFields(), qt.DeepEquals, Fields{{"duration", int64(2000)}})
68+
qt.Assert(t, b.distinctFieldsLastByName(), qt.DeepEquals, Fields{{"duration", int64(2000)}})
6969
}
7070

7171
type errFields string

‎handlers/cli/cli.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func (h *Handler) HandleLog(e *log.Entry) error {
6767

6868
color.Fprintf(h.Writer, "%s %-25s", bold.Sprintf("%*s", h.Padding+1, level), e.Message)
6969

70-
for _, field := range e.FieldsUnique {
70+
for _, field := range e.FieldsDistinct() {
7171
if field.Name == "source" {
7272
continue
7373
}

‎handlers/text/text.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (h *Handler) HandleLog(e *log.Entry) error {
6969
ts := time.Since(start) / time.Second
7070
fmt.Fprintf(h.Writer, "\033[%dm%6s\033[0m[%04d] %-25s", color, level, ts, e.Message)
7171

72-
for _, f := range e.FieldsUnique {
72+
for _, f := range e.FieldsDistinct() {
7373
fmt.Fprintf(h.Writer, " \033[%dm%s\033[0m=%v", color, f.Name, f.Value)
7474
}
7575

0 commit comments

Comments
 (0)