-
Notifications
You must be signed in to change notification settings - Fork 245
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
Revise log levels #295
Comments
I fully agree that logs should never terminate a program. More so, we should have way to direct log output to a file and not just stdout |
I think we also must ask ourselves a few questions:
|
So, currently, these are just logs: for debugging, testing, integrating and problem solving from production devices.
PS. I think this should be documented in case we decide to implement it. This way or something: https://github.com/joelparkerhenderson/architecture_decision_record |
Now we have all requirements for the logs, thanks! The only concern I have is what log-level will be used by status-react? Because you don't want to collect
and
Also, I don't get this line ragarding
So, we won't be able to use |
I totally agree that this problem should be attacked from the perspective of asking proper questions - how do we use logs, who is consumer of the logs, in what situations? Generally I see logs as a "program asking for human attention". When program prints error message, it means:
So here are my thoughts on important aspects of logging for us: Log levelsError log levelSomething, like, Info log LevelInfo log level is something program prints just to make nervous developer calm and ensure that program works as expected - like So, yes, Info logs should be optional, as they're almost often falls into the Debug bucket. One exception could be added is initialization and finish of program - they could be nice as a timestamp marks of service start/stop. Debug log levelThis is more complicated. Again, in many cases you don't need a lot of debug output, because during debug session in Go it's often faster to add Now, debug output is usually really noisy, so we may want to introduce different "labels" or "filters" - what to debug exactly. For example, if problem related to nodes communication - enable only logging related to this part. If it's related JS execution - enable only these logs, etc. And we might want to even change these in a runtime (via signals or other mechanism). But I think we just need to classify real problems we encounter that need log analysis and work on logs to help with those specific cases. Also we need to provide convinient and easy way to enable/disable debug mode (via env variables, or whatever works better with mobile emulators or real devices). Warning log levelThis one could be safely removed. Warning is something between Error and Info, it's when program trying to say - The same goes for Crit. Crit is just Error. TestsTests should not produce any log output by default. Ideally tests should give only fail/pass output, perhaps coverage. If particular test is used for debugging purposes we need to use debug enabling mechanism (env variables for example). But default should be "no output". Log formatThe next question is a log format. Logs may be read by humans or by machines (elasticsearch or other solutions). In our case log consumers are humans, so we need to adapt logs for them (us). Structured logging is a bad choice as it hurts readability. As we're going to consume logs via terminal, one of the consideration is the length of the message. Now we have a lot of long lines, like:
In a terminal those messages got wrapped, making logs unusable without using file and opening them in editor. To be honest, I have ultrawide 29" monitor and I can see those lines perfectly when I stretch terminal to the full width, but that's too demanding :) I'd love to have logs usable in a typical terminal When and how to write logsThis is also important and related to error handling. There are two approaches with logging errors:
Logging down the stack: Logging up the stack: I'd prefer first approach, but it really depends on the codebase. StacktracesStacktraces could be useful, but I'd prefer avoid them where we can. For example, error log message: If code is not too complicated and design is nice and clear, it's often enough just Simplicity of usageFinally, there is a question of simplicity of log usage. Logging is very basic and ubiquitous operation and should have as little overhead in usage as possible. I like logs being encapsulated as a dependencies of structs/objects, but having One of the approaches I love to use in relatively small projects - is just using the stdlib For example, to add "[ERROR]" log level to be printed in red color along with file:linenumber info, you can create custom log writer for stdlib log: $ cat log.go
package main
import (
"fmt"
"io"
"os"
"path"
"runtime"
"strings"
)
var (
reset = string([]byte{27, 91, 48, 109})
red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109})
)
type LogWriter struct {
path string // exec path, used for stripping absolute file path in log messages
out io.Writer
}
func NewLogWriter() *LogWriter {
dir, err := os.Executable()
if err == nil {
dir = path.Dir(dir) + "/"
}
return &LogWriter{
path: dir,
out: os.Stderr,
}
}
func (w *LogWriter) Write(data []byte) (int, error) {
if len(data) > 7 && string(data[:7]) == "[ERROR]" {
_, file, line, _ := runtime.Caller(3)
str := fmt.Sprintf("%s[ERROR] %s:%d%s:%s", red, strings.TrimPrefix(file, w.path), line, reset, data[7:])
return w.out.Write([]byte(str))
}
return w.out.Write(data)
} at the program initialization you can do the following:
and then using as simple as: import "log"
...
log.Println("[ERROR] Failed to connect to service", err) which will produce folllowing: which is quite nice and readable and enough in many cases. Again, I'm not rooting for this approach, but showing that it's possible to have nice easy to use levelled logging without using bloated logging libraries. -- And as a final note to this unexpectedly large comment, here is the real log levels distributions for
|
Regarding the log format, we can have different formats depending where the program is run. The current format is great for terminals but might not be the best in production, especially, if we would like to export logs to other systems in order to analyze them.
I think this topic is quite important, to have some guidelines and make the codebase more consistent. One more technique I used when writing node.js programs was log categories. When running in a debug mode, I could use env like |
Am not sure how everyone feels about structured logging (see https://medium.com/@tjholowaychuk/apex-log-e8d9627f4a9a). What if we can introduce a structured logger but then having specific sinks where we rendered specific errors for human reading, more so, we can completed ignore certain logs that may not be suitable for use but needed for metrics or consumption over some external service. Just a suggestion though :) |
Doing some investigation task, which involved understanding of a code and running app, I used today trace log entries a lot, but the experience was quite different from how it's assumed to work. Usual understanding of the tracing/logging workflow is:
The obvious problem here is that TRACE loglevel produces extremely high-traffic log output, almost immediately overflowing terminal buffer, leaving the only option — log to file (via In order to search exact line of interest, you should go to the code and take a look at that tracing line anyway. So what I was doing instead:
And it was much faster and cheaper than struggle through thousands of high-traffic irrelevant tracing log lines. I was also thinking in using some of the debuggers, but that was not an attempt to "understand a piece of code", rather to see some events happening in real world situation and see the bigger picture (like start/stop peer handling functions, etc). Considering this, I see no point of having Trace log level at all (at least in our code). We don't use it for now, and loglevels are mostly for compatibility with geth logger, but in context of this discussion I guess it proves the point of getting rid of useless log levels. |
One way to reduce noise or find interesting things in logs would be to use a capable log viewer that affords filtering and highlighting of entries on your live log automatically. E.g.: As long as the device doesn't get bogged down with writing the log entries, this seems like a good compromise in not requiring code changes, but still allowing to easily surface information of interest. This particular log viewer even allows sharing useful highlight rules within a team, so that others can benefit from a fine-tuned collective configuration (it's Windows-only unfortunately, but there should be Linux alternatives, or redirecting console output to a shared file). One question by the way: is there an automated way to generate an archive of all the app logs from the app UI, or on the event of a crash (status-react, status-go and go-ethereum log files), so that the team can receive bug reports from the field in a consistent fashion? |
@azer thanks, but our log usage patterns are different. Basically, we use logs in two ways:
That said, most of our log usage cases are short-lived debug sessions. As we're using Go, for debug sessions, we can instrument part of the code that is being debugged with temporary log print calls, so no need to have advanced leveling or filtering for the log. That approach works best for long running servers and retrospective bugs investigation, but not in this case. In the context of "revising log levels", as I wrote before, I would get rid of "WARN" levels, and kept only "INFO" (just normal print) and "ERROR" levels. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
This issue has been automatically closed. Please re-open if this issue is important to you. |
We have lots of logs and I sometimes see improper logs usage and have some questions about logging while reading tests output, for example.
My suggestion is reduce our log levels down to only a few characterised by their purpose perfectly described by their name:
WakeMeUpInTheMiddleOfTheNight
: status-goERROR
, go-ethereumERROR
.ToInvestigateTomorrow
: status-goWARN
, go-ethereumWARN
.InTests
: status-goDEBUG
, go-ethereumINFO
.DebugGeth
: status-goDEBUG
go-ethereumDEBUG
. Sometimes something weird is happening with geth and you don't get the behavior you expect. In this case it's useful to see what's happening under the hood of geth. This log level is only available for viewing logs, not for writing them (solog
package doesn't exposeDebugGeth
method).About other levels in status-go:
CRIT
level should be avoided because it causes uncontrollable application termination. It's also not log's responsibility to terminate the application.INFO
logs should be revised and moved to one of the three levels defined above. Most of the time they're actuallyDEBUG
but sometimes they'reWARN
.TRACE
andDEBUG
are actually the same thing in our case. We don't need such granularity.PS. Inspired by https://divan.github.io/posts/wakemeupinthemiddleofthenight/ and painful log-digging experience.
This is a proposal rather than a defined specification to work on, let's discuss it.
The text was updated successfully, but these errors were encountered: