-
Notifications
You must be signed in to change notification settings - Fork 574
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
Refactored zerolog.ConsoleWriter to allow customization #92
Conversation
The TravisCI build failed with |
Hello, screenshot updated, commit amended & force-pushed. |
This travis issue is meh. Any clue? |
@rs, unfortunately no clue... :/ I've run both of the commands in |
I've found it — the call to I've squashed the commit into the main one and pushed the new branch. |
Oh ok make sense. It’s due to the second line in the script section of the travis file with the tag to test binary logging. |
Ah, right! Maybe I forgot to check the output of the second test script properly when running it locally. |
I've just checked again, and indeed, when I run |
README.md
Outdated
To customize the configuration and formatting: | ||
|
||
```go | ||
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}.Reset() |
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'm not found of this API with the need to call Reset
and SetFormatter
updating a global config. Why can't we store the formatter settings in the ConsoleWriter
type?
I don't like it much either :) I haven't found a better way to keep 100% backwards compatibility with existing user code using output := consolelog.NewConsoleWriter()
logger := zerolog.New(output).With().Timestamp().Logger() In this way, when a user would customize the output = consolelog.NewConsoleWriter(
func(w *consolelog.ConsoleWriter) {
w.TimeFormat = time.Stamp
w.SetFormatter(
zerolog.LevelFieldName,
func(i interface{}) string { return strings.ToUpper(fmt.Sprintf("%-5s", i)) })
},
) it would register the customization only for the particular object — in other words, when you would then create another output with the bare In the prototype, all the formatters, the time format, and the parts order have been indeed stored as properties of But since I imagine users have existing code where the only "initialization" they do is something like After a lot of thinking and fiddling, I've realized that I can export a Maybe there's a better way around this? Or maybe I'm over-thinking the backwards compatibility too much? (Sorry for a delay with the reply, I'm on a vacation.) |
I'm not sure the initializer function design is indicated here. We don't you just expose the |
@karmi: are you still willing to work on this PR? |
Yes, absolutely — I was offline for most of August.
You mean as the package level function, or on the I'll have another look on the codebase, maybe another solution will jump out at me. |
1cea9c5
to
8479412
Compare
In order to allow customizing the console output, the `zerolog.ConsoleWriter` output has been refactored, with the formatting logic extracted to separate functions in `consoleFormatters`, and with the `SetFormatter()` method to re-define the defaults. Also, the default has been changed to be more sparse and visually attractive. The logic has been extracted from an external prototype package at <https://github.com/karmi/consolelog>. A comprehensive example has been added to the README, and a number of tests has been added as well. Closes rs#84
* Removed the global registry (map) of default formatters * Therefore, removed the `init()` function * Therefore, removed the `Reset()` method * Added default formatters as package level variables (`consoleDefaultFormatTimestamp`, ...), not as entries in the global registry * Added custom formatters as fields on the `ConsoleWriter` struct, not as entries in the registry * Removed `SetFormatter()` and `Formatter()` methods, and look up proper formatter (custom one or default) during formatting (`writeFields()`, `writeParts()`) * Adjusted the test examples * Minor source code cleanups Related: rs#92
Travis failed on Go 1.9 with:
I've tried to reproduce locally with Go 1.9.2 and the test didn't fail — I'll amend the commit and force push to trigger another build. |
* Removed the global registry (map) of default formatters * Therefore, removed the `init()` function * Therefore, removed the `Reset()` method * Added default formatters as package level variables (`consoleDefaultFormatTimestamp`, ...), not as entries in the global registry * Added custom formatters as fields on the `ConsoleWriter` struct, not as entries in the registry * Removed `SetFormatter()` and `Formatter()` methods, and look up proper formatter (custom one or default) during formatting (`writeFields()`, `writeParts()`) * Adjusted the test examples * Minor source code cleanups Related: rs#92
I've took another stab at refactoring the functionality, and would like to get some feedback on the latest version of The most important part is removing the global registry ( The default formatters are added as package level variables ( I've plugged this version into a real project where I'm using I can imagine there's yet another approach possible, eg. adding a |
README.md
Outdated
To customize the configuration and formatting: | ||
|
||
```go | ||
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}.Reset() |
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.
You forgot to update the doc to remove the .Reset()
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.
Argh, sorry about that, fixed in fc34c71!
Looks good. Please fix the doc and I'll merge. |
Thanks! Do you want me to rebase & squash the commits or do you prefer to do it on your side? |
Hello, The new For instance, if I set var out io.Writer = os.Stdout
zerolog.MessageFieldName = "msg"
out = zerolog.ConsoleWriter{Out: out}
logger = zerolog.New(out).With().Timestamp().Logger()
logger.Info().Msg("this message won't show") results in (more or less):
... note the |
@wvh, argh, that's bad... I'll look into it! |
I did the simplest thing which came to my mind, and made In this way, it's called from What do you think, @rs? |
No big deal IMHO. The console writer is not meant for production, but for dev anyway. |
@rs, right, that's what I was thinking as well, just wasn't sure. I'll send the patch as a PR. |
In order to prevent incorrect output when somebody uses a different name eg. for the "MessageFieldName", the `consoleDefaultPartsOrder` variable has been switched to a function, which is called in `Write()`, in order to pick up the custom name. Related: rs#92
In order to prevent incorrect output when somebody uses a different name eg. for the "MessageFieldName", the `consoleDefaultPartsOrder` variable has been switched to a function, which is called in `Write()`, in order to pick up the custom name. Related: #92
I guess you could also force people to use the By the way – thanks, I like the new look. |
@wvh , right, that would solve the problem of initialization, but would break a lot of existing code, and that's what I wanted to prevent at all cost with the refactor... |
Previously,
|
Hi @IngmarStein , I've tried locally, and was able to reproduce the race when eg. setting the timestamp option, coming from eg. this line: Line 103 in 8747b7b
I'll look into it, it definitely shouldn't require any synchronization from the calling code. |
When the ConsoleWriter has been customized, the operations accessing variables in the `Write()` method had race condition, as reported in rs#92 (comment). A simple mutex lock has been added around the section. Example code: ```go package main import ( "fmt" "os" "sync" "time" "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) func main() { var wg sync.WaitGroup log.Logger = log.With().Timestamp().Logger().Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}) // output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339} // log := zerolog.New(output).With().Timestamp().Logger() for i := 0; i < 10000; i++ { wg.Add(1) go func(i int) { defer wg.Done() log.Info().Str("foo", "bar").Msg(fmt.Sprintf("Hello from goroutine %d", i)) }(i) } wg.Wait() } ```
As outlined in #84, this patch refactors the
zerolog.ConsoleWriter
in two ways:I had to change the design of the prototype in order to maintain the backwards compatibility: the prototype has defined default formatting functions (
formatters
) on the concrete instance ofConsoleWriter
, but without using an initializer, these would be empty. Therefore, I've added a number of defensive checks intoConsoleWriter.Write()
, and aConsoleWriter.Reset()
method. This is not ideal, but I agree that maintaining backwards compatibility is an important goal, and the behaviour is predictable in my testing.I've added a benchmark test to make sure I inadverently don't harm the performance: the performance has stayed roughly the same. The tests and examples have been run with the
-race
parameter.I've updated the screenshot to reflect the new look, and added a comprehensive example to the README.