-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Add a human-optimized text encoder #123
Conversation
final.bytes = append(final.bytes, msg...) | ||
} | ||
|
||
// A TextOption is used to set options for a text encoder. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than add separate options for NoTime
and TimeFormat
etc, I would prefer to:
- change the
JSONOption
interface method to be something likeapplyJSON
and have theTextOption
interface useapplyText
. This will allow a single formatter to apply to both. - Make
TextFormatter
implement - Now the same
NoTime
andTimeFormatter
types can be used as both text options and JSON options
This reduces the interface and allows the user to use the same options for both encoders.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I can do that. It makes the JSON options more complex, but it's eminently doable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To document an offline conversation we had quite a while ago:
Consolidating these is possible, but I think it'll make them appreciably more complex. For example, the JSON encoder supports an option that sets the key for the entry's message (e.g., {"msg": "foo"}
or {"the_message": "foo"}
). That's not a meaningful concept for the text encoder.
I don't mind whichever direction @sectioneight wants to go with this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll choose the path of least resistance here :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to figure out how best to clean up the API in a separate PR.
I'm leaning towards separating time formatters from knowing about keys, There can be separate options for formatting time, and for customizing the time key. That way the options that customize keys (like MessageKey
and TimestampKey
) only implement JSONOption
but the time formatting options NoTime
, EpochFormatter
, RFC3339Formatter
can apply to both JSON encoders and text encoders.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@prashantv can you open an issue describing the desired changes to the APIs so we make sure to follow up in the future? I'm one of those weird people that actually likes following up on loose ends
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done,
#149
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
u r awsm
This seems useful. Is this still being worked on? Would you like help shepherding it over the finish line? |
@sectioneight Very much so - I'm out on paternity leave tomorrow, and I'm not sure when I'm going to get back to this. |
Cool, will do. @akshayjshah do you have any local/unpushed changes before I take over this branch? |
Nope, this is it - all yours. |
fe7eb32
to
29876f2
Compare
Ok @prashantv -- I took a pass at implementing the state. I'm not sure if the semantics are correct yet -- is the |
38c3add
to
00a304c
Compare
type textEncoder struct { | ||
bytes []byte | ||
timeFmt string | ||
nested *atomic.Bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went with an atomic Bool here -- I'm pretty sure these encoders are supposed to be goroutine safe but I don't see any sync primitives on modifying e.g. enc.bytes
in the various methods.
Is locking taken care of by the logging and should the encoder not have to worry about it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the atomics after I realized encoders are only used once. Not sure why GH is still showing this...
textLogger.Info("With Marshaler", zap.Marshaler("m", zap.LogMarshalerFunc(m))) | ||
|
||
// Output: | ||
// [I] With Marshaler m={loggable=yes number=1} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sort of feels like it's less useful as an example but as a unit test. However, it doesn't look like the unit test framework is set up to do "end-to-end" (e.g. assert the actual output) and I wanted to show that I handles this case properly.
Is there a better place to put this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to text_logger_test.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still see it here -- has it been removed from example_test.go
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$ grep Marshaler example_test.go
// no results
10a0c8f
to
f979bf7
Compare
When logging to console, JSON isn't the most friendly output - it's nice for machines but not optimal for humans. This PR adds a text encoder that prioritizes human readability, along with a few time-handling options for that encoder. In advance of #116, this PR also includes support for unsigned ints.
f979bf7
to
0bbb077
Compare
|
||
func (enc *textEncoder) AddBool(key string, val bool) { | ||
enc.addKey(key) | ||
if val { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can simplify to just use AppendBool
here (same as #148)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
} | ||
|
||
func (enc *textEncoder) AddFloat64(key string, val float64) { | ||
enc.addKey(key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can simplify to just use strconv.AppendFloat
here. In the JSON encoder, the output for nan/infinity is quoted, so a normal would get: "num": 1
but nan would get "num": "+Inf"
For the text encoder, we don't need to quote the inf/nan, and AppendFloat
already handles these correctly,
https://play.golang.org/p/W3FP95QIMc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
enc.firstNested = true | ||
enc.bytes = append(enc.bytes, '{') | ||
err := obj.MarshalLog(enc) | ||
enc.bytes = append(enc.bytes, '}') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should reset firstNested
to false
here. I think a failing test case for this would:
- add an object where the
MarhsalLog
adds no fields - then add a normal field (like string)
So we'd want something like emptyObj={} something=val
but we might get emptyObj={}something=val
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Fixed and added negative test
Ok @prashantv, I think I've addressed all comments (and nice catch on the nested error!) |
Still getting used to the new github review flow. Are there more comments to be addressed? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, I think some of the examples can be removed now that they're in text_logger_test.go
// [I] This is a text log. | ||
} | ||
|
||
func ExampleNew_textEncoderSingleCurlyBrace() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove this example now that the integration test covers it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's removed. Ugh, I still don't understand the new GH UI
textLogger.Info("With Marshaler", zap.Marshaler("m", zap.LogMarshalerFunc(m))) | ||
|
||
// Output: | ||
// [I] With Marshaler m={loggable=yes number=1} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still see it here -- has it been removed from example_test.go
?
@prashantv updated by moving out of example_test -- does your approval mean a self-merge is now appropriate? Or what's your team's process on GitHub? |
Yep, for contributors who are part of the organization or project, typically approval means self-merge. For external contributors, the reviewer tends to merge. Since you are in the org, feel free to self-merge! Thanks for wrapping this up @sectioneight |
Forgot to mention, suggest "Squash and merge" -- and feel free to edit the title/body of the squashed commit, rather than leaving the default list of all commits. |
Add a text encoder that's friendlier for human consumption. By default, the
TextEncoder
output looks like this:The timestamp format is configurable via functional options, and the encoder includes support for unsigned ints (in advance of #116).
Resolves #59.