Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@jcchavezs
Copy link

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

Copy link

@anuraaga anuraaga left a 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?

// 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))

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?

Copy link
Author

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.

@manojgop
Copy link
Owner

manojgop commented Mar 1, 2023

@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)
Copy link
Owner

@manojgop manojgop Mar 1, 2023

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

@manojgop
Copy link
Owner

manojgop commented Mar 1, 2023

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?

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.

@anuraaga
Copy link

anuraaga commented Mar 1, 2023

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.

@manojgop
Copy link
Owner

manojgop commented Mar 1, 2023

Just FYI - A related change in coraza-proxy-wasm to use zerolog is manojgop/coraza-proxy-wasm@e2cd02f. This would need modifications as well

@jcchavezs
Copy link
Author

jcchavezs commented Mar 1, 2023 via email

…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 {
Copy link
Author

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

@jcchavezs
Copy link
Author

I gave some turnarounds to the API. Please do review and let me know if this feels right so I can backfill tests.

@manojgop
Copy link
Owner

manojgop commented Mar 1, 2023

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) ?

@manojgop
Copy link
Owner

manojgop commented Mar 1, 2023

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 :-)

@jcchavezs
Copy link
Author

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.

image

doEvaluate() function heap allocation has reduced

image

Log statements are not allocating memory

image

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 {
Copy link
Author

@jcchavezs jcchavezs Mar 1, 2023

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

Copy link

@anuraaga anuraaga left a 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().
Copy link

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

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

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.

return
}

s := strings.Builder{}
Copy link

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)

}

func (l defaultLogger) Trace() Event {
if l.level < LogLevelTrace || l.Logger == nil {
Copy link

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

Copy link
Author

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

@ns-sundar
Copy link

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.

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 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.

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.

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.

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.

@anuraaga
Copy link

anuraaga commented Mar 2, 2023

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

Incorrect conversion of an integer with architecture-dependent bit size from [strconv.Atoi](1) to a lower bit size type int8 without an upper bound check.
manojgop and others added 3 commits March 2, 2023 10:23
zerolog is optimized for logging and does not allocate memory unlike fmr.Sprintf()
for debug logs

Signed-off-by: Manoj Gopalakrishnan <[email protected]>
@jcchavezs jcchavezs force-pushed the use_zerolog_frictionless branch from 1a585bd to 0f8ae9a Compare March 2, 2023 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants