-
Notifications
You must be signed in to change notification settings - Fork 0
chore: introduce API changes to ease support of zerolog. #3
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
base: use_zerolog
Are you sure you want to change the base?
chore: introduce API changes to ease support of zerolog. #3
Conversation
* tests: adds more coverage for CTL action. * tests: improves naming.
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 think it will be good if the default still uses the standard library and we can provide a plugin to implement with zerolog, or zap or whatever. Does this PR achieve that?
actions/expirevar.go
Outdated
| // Not supported | ||
| // TODO(jcchavezs): Shall we log a message? | ||
| // tx.WAF.Logger.Error("Expirevar was used but it's not supported", zap.Int("rule", r.Id)) | ||
| // tx.WAF.Logger.Error().Msg("Expirevar was used but it's not supported", zap.Int("rule", r.Id)) |
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.
What is the reason to retain a commented log?
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 question, let's remove it in another PR.
|
@jcchavezs Thanks for raising this PR. Some checks are failing due to incorrect conversion between integer types |
| continue actionLoop | ||
| } | ||
| cval.WriteString(string(c)) | ||
| cval.WriteRune(c) |
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.
Looks good. But this change is unrelated to zerolog and was initially added in this commit -> 187daf6
This is a good suggestion. However, zerolog uses a different API for log message formatting compared to log package in standard library. We need to avoid invoking fmt.Sprintf(message, args..) variadic function if Debug is disabled to avoid heap allocations of arguments by go compiler escape analysis. zerolog APIs are designed to avoid heap allocations and reflection. It could be tricky to use a generic API and to format the log message that could satisfy both zerolog and standard log library APIs and at the same time avoid heap allocations if Debug logs is disabled. |
The zerolog version can be the optimized version, while it is good to work out of the box with the standard log library that can have extra heap allocations as a tradeoff. Notably, being able to implement with zap does seem important - for example Caddy uses zap for logging, and while it's not the end of the world to use a different library with Coraza I think it'd generally be better to be able to plug zap logger in for that connector. So I'd like to make sure the logging implementation is decoupled from the logger plugin interface if at all possible. |
|
Just FYI - A related change in coraza-proxy-wasm to use zerolog is manojgop/coraza-proxy-wasm@e2cd02f. This would need modifications as well |
|
I think one idea would be to mimic the zerolog approach and have a similar
API which can work with standard logger e.g. doing `message key1=value1
key2=value2` for std logger which is plain.
While sticking with zerolog does not seem too crazy, I think @anuraaga made
a point with caddy (using zap) and actually proxy-wasm has an own logger
API which can fit in this API aswell.
…On Wed, 1 Mar 2023, 06:39 manojgop, ***@***.***> wrote:
Just FYI - A related change in coraza-proxy-wasm to use zerolog is
***@***.***
<manojgop/coraza-proxy-wasm@e2cd02f>.
This would need modifications as well
—
Reply to this email directly, view it on GitHub
<#3 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAXOYAVGGGEKN2NX3H3Y7FLWZ3OIZANCNFSM6AAAAAAVLJZQWA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
…awaf#681) Use string builder in parseActions() to optmize memory usage. Use string builder in parseActions() function to efficiently concatenate strings and optimize memory usage. go bench profile (testing/coreruleset) shows less allocations in seclang.parseActions function. Signed-off-by: Manoj Gopalakrishnan <[email protected]>
loggers/debug.go
Outdated
| func (e NopEvent) Stringer(key string, val fmt.Stringer) Event { return e } | ||
|
|
||
| // DebugLogger is used to log SecDebugLog messages | ||
| type DebugLogger interface { |
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.
How about this logger interface? @anuraaga @manojgop @ns-sundar
|
I gave some turnarounds to the API. Please do review and let me know if this feels right so I can backfill tests. |
Could you please check that heap allocation is not there in Debug Log statements (if Debug logs are disabled) if we use zerolog instead of default log. Could you elaborate how can we replace default log package with zerolog (at compile time as a plugin) ? |
Got it. I see that APIs are designed similar to zerolog APIs and with that Debug() log statements will avoid heap allocations if Debug log is disabled. Overall it looks good to me :-) |
|
I think the API is ready @anuraaga @ns-sundar @manojgop. If we merge this now then @manojgop can open a PR against coraza repo itself I guess. doEvaluate() function heap allocation has reduced Log statements are not allocating memory Thanks @manojgop for the screenshots. |
| // Logger is used to log SecDebugLog messages | ||
| // This interface is highly inspired in github.com/rs/zerolog logger and the aim | ||
| // is to avoid allocations while logging. | ||
| type Logger interface { |
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.
Logger implementation would follow this interface, being zap or proxywasm cc @anuraaga
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.
Seems reasonable to me
actions/ctl.go
Outdated
| limit, err := strconv.ParseInt(a.value, 10, 64) | ||
| if err != nil { | ||
| tx.WAF.Logger.Error("[ctl:RequestBodyLimit] Incorrect integer CTL value %q", a.value) | ||
| tx.WAF.Logger.Error(). |
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 is missing Msg so won't be outputed - it seems that zerolog has Send() for msg-less logs.
Note that this is the trickiness of the zerolog API that achieves minimum allocations with the chance of not actually logging by mistake - it's definitely much harder to use than something like zap. But I guess we can deal with it. If there's not a linter for it yet I guess we could write 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.
Ah nice, there's an official one
https://github.com/rs/zerolog/blob/master/cmd/lint/README.md
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.
Lint sounds good. BTW, one of the things I have been doing lately is to cover logs with unit tests but linter seems easy. Let me open an issue for that.
debuglogger/default.go
Outdated
| return | ||
| } | ||
|
|
||
| s := strings.Builder{} |
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.
Think e.logger.Printf would be better than using this builder to avoid the intermediate string.
e.logger.Printf("[%s] %s%s\n", e.level.String(), msg, e.fields)
debuglogger/default.go
Outdated
| } | ||
|
|
||
| func (l defaultLogger) Trace() Event { | ||
| if l.level < LogLevelTrace || l.Logger == nil { |
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.
Don't think Logger can be nil, especially after making it a private field as it probably should be
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.
It was in NOP case but I just changed it based on this convenience https://cs.opensource.google/go/go/+/refs/tags/go1.20.1:src/log/log.go;l=72
Do we necessarily need two logging frameworks? There may be ways to switch to Zerolog that may work with the current log statements, so that we don't need a forklift upgrade.
The standard log library does not provide structured logging, unless you have something else in mind. So, having both will result in inconsistent logging. As a data point, Crowdstrike switched to zerolog in full and saw good benefits.
This is a valid point. But, first, zerolog is widely used. Secondly, zap itself says that zerolog is faster. That said, if you are advocating some kind of neutral interface that will work with multiple loggers, for future-proofing, I am ok -- just wondering what effort that will entail. |
I think @jcchavezs has done most of the effort for it here, we already had a neutral interface for loggers and just have tweaked it now to enable a zero allocation backend. As a core library rather than an app, we try to make it generic so that it is those integrating with it that can make their decisions. If apps can pick zerolog, zap, or even std library based on their existing dependencies or lack of dependencies, it makes Coraza a more useful tool in the toolbox than if we force a decision on it. |
| return err | ||
| } | ||
| return options.WAF.SetDebugLogLevel(lvl) | ||
| return options.WAF.SetDebugLogLevel(debuglogger.LogLevel(lvl)) |
Check failure
Code scanning / CodeQL
Incorrect conversion between integer types
zerolog is optimized for logging and does not allocate memory unlike fmr.Sprintf() for debug logs Signed-off-by: Manoj Gopalakrishnan <[email protected]>
1a585bd to
0f8ae9a
Compare
This PR aims to ease the introduction of zerolog in one hand to still use coraza types but let the API interact with zerolog api aswell.
Ping @anuraaga