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

[log] Refactor and measure performance using benchmark #90

Merged
merged 37 commits into from
Dec 9, 2018
Merged

Conversation

at15
Copy link
Member

@at15 at15 commented Sep 6, 2018

Trying to fix all those issues https://github.com/dyweb/gommon/issues?q=is%3Aopen+is%3Aissue+label%3Apkg%2Flog

Will first write the doc and list some changes in design, which could be the base of the long pending blog dyweb/blog#28

TODO

Fixed

- will have a design folder next to it, so the doc folder is no longer flat
- basically covered background, what was the goals, good thing about
current implementation, bad thing etc.
@codecov
Copy link

codecov bot commented Sep 6, 2018

Codecov Report

Merging #90 into master will increase coverage by 0.06%.
The diff coverage is 30.29%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #90      +/-   ##
==========================================
+ Coverage   48.16%   48.22%   +0.06%     
==========================================
  Files          52       56       +4     
  Lines        1796     2030     +234     
==========================================
+ Hits          865      979     +114     
- Misses        847      956     +109     
- Partials       84       95      +11
Impacted Files Coverage Δ
util/cast/cast.go 61.9% <ø> (ø) ⬆️
log/generator.go 66.66% <ø> (ø) ⬆️
generator/generate.go 0% <ø> (ø) ⬆️
generator/shell.go 0% <ø> (ø) ⬆️
generator/pkg.go 0% <ø> (ø) ⬆️
util/httputil/unix.go 0% <0%> (ø)
log/handlers/cli/handler.go 20% <0%> (+4.81%) ⬆️
util/testutil/http.go 0% <0%> (ø) ⬆️
log/field.go 75% <100%> (+75%) ⬆️
util/runtimeutil/caller.go 92% <100%> (ø) ⬆️
... and 20 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ec45062...f1a6678. Read the comment docs.

- get `{"message":"page not found"}`
- just trim the path and keep the first /
- disable server handler check in unix server listen test, it failed CI
because when close the server/remove socket file it seems the server
didn't shut down gracefully ... https://travis-ci.org/dyweb/gommon/jobs/431772857
- first do benchmark to get a basic understanding of other logging
libraries performance and what feature is lacking is gommon so we can
modify the interface
- then handle fields, structured logging makes log parsing much more
easier, and is more generic since parser don't need to know the exact
context, i.e. http access log etc.
- finally handle the tree of logger problem, along with the skip runtime
caller, it's essential for clean logging in testing and util functions
go test -bench=.
goos: linux
goarch: amd64
pkg: github.com/dyweb/gommon/log/_benchmarks
BenchmarkWithoutFieldsJSON/gommon-8         	20000000	        96.9 ns/op
BenchmarkWithoutFieldsJSON/Zap-8            	10000000	       176 ns/op
BenchmarkWithoutFieldsJSON/Zap.Sugar-8      	 5000000	       257 ns/op
BenchmarkWithoutFieldsJSON/zerorlog-8       	20000000	       118 ns/op
PASS
ok  	github.com/dyweb/gommon/log/_benchmarks	8.031s
go test -bench=. -benchmem
goos: linux
goarch: amd64
pkg: github.com/dyweb/gommon/log/_benchmarks
BenchmarkWithoutFieldsJSON/gommon-8         	10000000	       126 ns/op	     496 B/op	       4 allocs/op
BenchmarkWithoutFieldsJSON/Zap-8            	10000000	       205 ns/op	       0 B/op	       0 allocs/op
BenchmarkWithoutFieldsJSON/Zap.Sugar-8      	 3000000	       353 ns/op	     128 B/op	       2 allocs/op
BenchmarkWithoutFieldsJSON/zerorlog-8       	10000000	       131 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/dyweb/gommon/log/_benchmarks	6.617s
- previously make is called inside formatHead so it will allocate on
heap for sure, after moving the make to handler and increase the hard
coded prefix ... some allocation can be avoided, at least we know the
direction for optimization now (either use a pool or use a fixed length
make, if it's large enough it can handle most log messages on stack)
BenchmarkWithoutFieldsText/gommon-8         	30000000	       129 ns/op	     272 B/op	       3 allocs/op
BenchmarkWithoutFieldsText/std-8            	10000000	       401 ns/op	     128 B/op	       2 allocs/op
BenchmarkWithoutFieldsText/zap-8            	20000000	       273 ns/op	      72 B/op	       3 allocs/op
BenchmarkWithoutFieldsText/zap.sugar-8      	20000000	       309 ns/op	     200 B/op	       5 allocs/op
BenchmarkWithoutFieldsText/zerolog-8        	 3000000	      1395 ns/op	    2011 B/op	      36 allocs/op
BenchmarkWithoutFieldsText/apex-8           	 3000000	      1551 ns/op	     320 B/op	      15 allocs/op
BenchmarkWithoutFieldsText/logrus-8         	 1000000	      3209 ns/op	     769 B/op	      15 allocs/op
BenchmarkWithoutFieldsJSON/gommon-8         	50000000	        79.6 ns/op	     288 B/op	       3 allocs/op
BenchmarkWithoutFieldsJSON/zap-8            	20000000	       179 ns/op	       0 B/op	       0 allocs/op
BenchmarkWithoutFieldsJSON/zap.sugar-8      	20000000	       259 ns/op	     128 B/op	       2 allocs/op
BenchmarkWithoutFieldsJSON/zerolog-8        	50000000	       113 ns/op	       0 B/op	       0 allocs/op
BenchmarkWithoutFieldsJSON/apex-8           	 2000000	      1920 ns/op	     512 B/op	      10 allocs/op
BenchmarkWithoutFieldsJSON/logrus-8         	 2000000	      2990 ns/op	    1218 B/op	      22 allocs/op
- originally I though it merge the fields when output, but actually when
you call logger.With(f1, f2), it encode f1, f2 right away into the
underlying, it makes adding context cheap but couple context with the
core (similar to handler in gommon/log) so that each core actually has
state (handler in gommon/log are stateless, for now), exchange space for
time
- a drawback of this if a key is used two times, you end up having two
fields instead of 1, zerolog has the same problem, and they mentioned in
README that json allow duplicated key and json encoding/decoding library
will handle it
- when use `interface{}` as parameter, it will cause allocation, I know
it is caued by `interface{}` based on the `16B` size but don't quite get
why it escape ...
- json format with context (logger w/ fields)
  - logrus and apex are actually entry w/ context where entry hold a
pointer to the logger it should use ...
- was planning to change identity to a struct, but since it could be nil
and it is not used most of the time, decided to change it back to
pointer
- [ ] TODO: updated gommon generator template, so all code that use
struct logger factory need update
- the 'context` is actually just fields attached to logger
- there aren't many tests so we didn't break any test ....
- fan out log in serial
- previously was not using empty value for optional parameters and endup
having 6 functions, this made handler implementation error prone due to
copy and paste also made handler func impossible
- now there is just one log method in Handler call HandleLog
- it has less allocation than `Info` because it accepts string instead of
`interface{}`
- this should makes a gommon/log drop in replacement for some library
that requires a standard library like interface `Print` and `Printf`
- [ ] TODO: I don't want to add `Println` because `\n` does not make
sense for most handlers, json, text w/ fields etc. there will be a `\n`
for most most handlers, not after the message but after the fields
- just copy and paste from standard library and remove check on html and
jsonp, looked at zap and zerolog, they are basically the same ...
- at first I thought allocate a fixed size on stack would remove the
allocation, but I was wrong, because the slice is passed to io.Writer
and a call on interface is marked as `parameter to indirect call` and
so even a fixed size slice can escape ...
- since we can't avoid the alloc due to call on io.Writer, we just need
to make sure to allocate a slice large enough for message and fields so
we don't need to allocate during the middle of append
- [ ] TODO: these are just magic numbers, which will waste
resource/won't work when the size of fields key/value are too small/too
big ...
- [ ] TODO: no context with fields json still have two alloc
````
BenchmarkWithContextFieldsJSON/gommon-8         	30000000	       117 ns/op	     352 B/op	       3 allocs/op
BenchmarkWithContextFieldsJSON/gommon.F-8       	50000000	        74.0 ns/op	     224 B/op	       1 allocs/op
BenchmarkNoContextWithFieldsJSON/gommon.F-8     	50000000	        92.2 ns/op	     352 B/op	       2 allocs/op
````
- at first I suspect it's because varadic arg but it seems not ...
  - could be the `Fields` type
- this happens to zap as well but didn't happen to zerolog because
latter don't have any usage of interface methods when attaching fields
  - this only works because zerolog only support json, so it can just
encode things right away and don't need handler interface, the drawback
is for all the other format, it has to decode the bytes into
map[string]interface{} and then print them out ...
- actually they are alredy there in pretty.go under same package ...
@at15 at15 mentioned this pull request Nov 26, 2018
6 tasks
- in v1 we followed logrus and add a filter for filtering log
- in v2 we added parent and children but it will cause gc problem
- registry will make the tree explicit, logger has to manually register
themselves to registry and registry have to manually register to other
registry, short lived logger should not register them self
- registry also have identity and is like folder while logger is like
file (log structured file system XD)
- the map for children logger will be removed in following commit, and
generator logic will change as well
- now only registry is used to keep the relationship between loggers
across package and projects
- add WalkRegistry
- remove deprecated logger_tree logic
- for single message w/ 1 allocation, increased from 60ns -> 236
- remove some outdated doc on log package, most of them are still very
outdated though ...
@at15 at15 changed the title [WIP][log] Refactor and measure performance using benchmark [log] Refactor and measure performance using benchmark Dec 9, 2018
@at15 at15 merged commit 5d61d30 into master Dec 9, 2018
This was referenced Dec 9, 2018
at15 added a commit that referenced this pull request Dec 9, 2018
- just #90 refactor on log
@at15 at15 deleted the log/v2.5/init branch December 9, 2018 19:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant