-
Notifications
You must be signed in to change notification settings - Fork 42
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
Fix/54 proper logger #131
Fix/54 proper logger #131
Conversation
30ae38c
to
02bc305
Compare
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.
Nice job with this! 👏 It will surely help troubleshoot thorny issues like #125, though now running with --verbose
is super noisy. It would be good to place the cdp:recv
event a level below Debug
(Trace
) that could be activated with a second --verbose
, though that's probably not supported right now and would need changes in k6. Otherwise putting it behind an environment variable (XK6_BROWSER_TRACE
) might be a good idea until -vv
is supported. From the logrus side it would be a matter of simply calling Tracef()
instead of Debugf()
. Since those make the majority of logged events (70% by my rough estimate), and we usually don't care about them unless we're debugging the CDP layer itself, it would make debugging our code less noisy.
I left a few minor suggestions, and here are some general comments:
- Using structured logging for all this like you suggested over Slack would be much better. E.g. all places where
err:%v
is logged would be better withWithError(err)
instead. It would also cut down on all the repetition, since you could initialize the logger with all fields upfront (in a constructor or at the start of the method) and then simply callDebug()
with just the message. But I don't think this is a requirement for this PR and we can improve on that later. - Another related thing I was thinking last week would be to abandon our
common.Logger
altogether and uselogrus
directly. Some of the thingsLogger.Logf
does are of questionable importance (theelapsed
field,debugOverride
is alwaysfalse
, the fallback tofmt.Printf()
...), though standardizing thecategory
field and logging the goroutine ID seem useful.
OTOH, if we ever decide to switch logging libraries, as we've been planning to do in k6 for years now, having a custom logging API would be very convenient. So maybe we should keep it but simplify it considerably. - If we do decide to keep our wrapper, we might want to abstract away the logging of the calling function names. This adds more work when functions are renamed/moved, and doing it with LSP won't pick it up. We can easily get the caller function name with some
runtime
introspection, see this SO answer. I actually had a patch I never pushed that does this when I was debugging some things last week, and it worked quite well. Obviously it won't pick up all the nuances like adding:go
for goroutines or custom categories based onif
branches, but those aren't the biggest concern when refactoring.
@@ -408,12 +474,14 @@ func (c *Connection) Execute(ctx context.Context, method string, params easyjson | |||
for { | |||
select { | |||
case <-evCancelCtx.Done(): | |||
c.logger.Debugf("connection:Execute:<-evCancelCtx.Done()", "wsURL:%q err:%v", c.wsURL, evCancelCtx.Err()) |
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.
Same here, I would use WithError(evCancelCtx.Err())
.
@@ -84,23 +84,26 @@ func (l *Logger) Logf(level logrus.Level, category string, msg string, args ...i | |||
if now == elapsed { | |||
elapsed = 0 | |||
} | |||
defer func() { | |||
l.lastLogCall = now |
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 wonder if we need to keep track of this or elapsed
at all... Why is it useful to know the time elapsed between two (possibly unrelated) Logf()
calls? 🤷♂️
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.
elapsed
was helpful for me when I was debugging the context error.
For example, it showed me where the code was stuck for 20 seconds and failed.
aebddcb
to
3994ff0
Compare
Thanks! ❤️
We use environment flags only on the testing side right now, so we can add a However, we use flags for the test runs (like the
That would be awesome. We can do something like this: // common/logger.go
func (l *Logger) WithError(err error) *logrus.Entry {
return l.logger.WithError(err)
}
// common/browser.go
b.logger.WithError(err).Debugf(
"Browser:onAttachedToTarget:background_page", "sid:%v tid:%v, returns",
ev.SessionID, ev.TargetInfo.TargetID) However, as I wrote you on Slack:
💡 Maybe we should write a custom formatter for using our custom logger on top of Logrus? That can possibly solve the issues I explained above.
Can you send me the patch so I can look at it? For example, with this code: func NewLogger(ctx context.Context, logger *logrus.Logger, debugOverride bool, categoryFilter *regexp.Regexp) *Logger {
l := Logger{
ctx: ctx,
logger: logger,
mu: sync.Mutex{},
debugOverride: debugOverride,
categoryFilter: categoryFilter,
lastLogCall: 0,
}
logger.SetReportCaller(true)
logger.SetFormatter(&logrus.JSONFormatter{
CallerPrettyfier: caller(),
FieldMap: logrus.FieldMap{
logrus.FieldKeyFile: "caller",
},
})
return &l
}
func caller() func(*runtime.Frame) (function string, file string) {
return func(f *runtime.Frame) (function string, file string) {
return f.Func.Name(), fmt.Sprintf("%s:%d", f.File, f.Line)
}
} I receive log messages like this:
It's kind of noisy. Maybe we can strip some parts of it. |
Useless trivia: According to UrbanDictionary: Brint, is a sweet, funny, caring, loving, and gentle guy. but when you make him angry he will snap at revenge and dispise you greatly.
* This way we can attach the logger to the browser process. * This also enables the debug mode on the supplied logger when the debug launch options enables it. So that the components in the chain can detect this. For example, connection only finds a targetID in debug mode for logging purposes.
3994ff0
to
c6a8e46
Compare
But
🤔 That might work, though then we're really depending even more on Logrus, which we probably want to replace.
Sure, it's just a simple change in diff --git a/common/helpers.go b/common/helpers.go
index ec8c20d..851e227 100644
--- a/common/helpers.go
+++ b/common/helpers.go
@@ -52,7 +52,9 @@ func debugLog(msg string, args ...interface{}) {
elapsed = 0
}
magenta := color.New(color.FgMagenta).SprintFunc()
- args = []interface{}{fmt.Sprintf("[%d] %s", goRoutineID(), fmt.Sprintf(msg, args...))}
+ pc, fn, line, _ := runtime.Caller(1)
+ args = []interface{}{fmt.Sprintf("[%d %s(%s:%d)] %s",
+ goRoutineID(), runtime.FuncForPC(pc).Name(), fn, line, fmt.Sprintf(msg, args...))}
args = append(args, fmt.Sprintf("%s ms", magenta(elapsed)))
fmt.Println(args...)
debugLastCall = now I wasn't aware of Logrus' But really, these are all improvements we can do after this PR, including proper structured logging, |
First todo: +1 - Tracef
2nd todo: What do you think about
Let's pass this on for now, then.
+1 but for later, ok.
Please see the 2nd todo above. |
Sounds good (maybe |
It's only used with a logger. If we need to use this function in the future, we can move it back.
I added XK6_BROWSER_LOG=warn xk6 run -q examples/colorscheme.js I also added another flag called XK6_BROWSER_CALLER=1 xk6 run -q examples/colorscheme.js |
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.
Nicely done! I think we should eventually fix how the caller is determined/rendered, but we can do that later.
func (l *Logger) ReportCaller() { | ||
caller := func() func(*runtime.Frame) (string, string) { | ||
return func(f *runtime.Frame) (function string, file string) { | ||
return f.Func.Name(), fmt.Sprintf("%s:%d", f.File, f.Line) | ||
} | ||
l.lastLogCall = now | ||
} | ||
l.log.SetFormatter(&logrus.TextFormatter{ | ||
CallerPrettyfier: caller(), | ||
FieldMap: logrus.FieldMap{ | ||
logrus.FieldKeyFile: "caller", | ||
}, | ||
}) | ||
l.log.SetReportCaller(true) |
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.
Do we need this custom field and prettifier? From what I see it only changes the field name.
With:
func="github.com/grafana/xk6-browser/common.(*Logger).Logf" caller="github.com/grafana/xk6-browser@v0.0.0-00010101000000-000000000000/common/logger.go:116"
Without (just calling l.log.SetReportCalled(true)
):
func="github.com/grafana/xk6-browser/common.(*Logger).Logf" file="github.com/grafana/xk6-browser@v0.0.0-00010101000000-000000000000/common/logger.go:116"
Is the idea to leave it for further customization (cleaning up the github.com/...
prefix like you mentioned)?
Also, there are a lot of places where the caller is Logger.Logf
. That might be tricky to resolve, so this is fine as is.
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.
Yeah, it's because we're using our custom logger. Maybe we should get rid of it as you said before.
For now, I can do something like this, WDYT?
// ReportCaller adds source file and function names to the log entries.
func (l *Logger) ReportCaller() {
// strip the module informaiton
strip := func(s string) string {
s = strings.TrimPrefix(s, "github.com/grafana/xk6-browser")
s = s[strings.Index(s, "/")+1:]
return s
}
caller := func() func(*runtime.Frame) (string, string) {
return func(f *runtime.Frame) (fn string, loc string) {
const stackLevel = 8
_, file, no, ok := runtime.Caller(stackLevel)
if ok {
fn = fmt.Sprintf("%s:%d", strip(file), no)
}
return fn, ""
}
}
l.log.SetFormatter(&logrus.TextFormatter{
CallerPrettyfier: caller(),
})
l.log.SetReportCaller(true)
}
Output:
time="2021-11-30T12:12:21+03:00" level=debug msg="sid:AFA9A388DA1617DF05688B6718BCD591 tid:045F8A2BB4D7E102A47820CF00E5C9DF" func="common/session.go:75" category="Session:close" elapsed="0 ms" goroutine=68
Maybe we should make this the default one and get rid of the XK6_BROWSER_CALLER
?
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.
Yeah, we wouldn't need XK6_BROWSER_CALLER
if the caller information could be added by default on levels >= Debug
.
Can that stackLevel = 8
be a constant? I mean, will that always be the case? I don't have time to look into it now, but if you think it solves it for all cases, LGTM.
And the func="common/session.go:75"
doesn't make much sense, there's no function name :) Maybe this should be caller="common.(*Session).close [common/session.go:75]"
?
In any case, don't spend too much time on this. Feel free to create an issue for it and we can tackle it soon-ish.
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.
Yep, we need function name there as well, but at least it goes to the file and line when I click on it on my terminal 😅
I created another issue with some rough commits that solve some of the issues:
- Use logger directly: Use logger directly #137
- PR: Use logger directly (improvements) #138
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 that stackLevel = 8 be a constant? I mean, will that always be the case? I don't have time to look into it now, but if you think it solves it for all cases, LGTM.
For this Logrus version, yes!
This PR is for #54 (Proper logger):
Debugf
to the entities in the extensionWith this, I'm able to see the problems in #125 and #49.
So I think they are helpful for us to diagnose issues in the future as well.
Closes #54