-
Notifications
You must be signed in to change notification settings - Fork 18k
testing: Add T.Output() etc #59928
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
Comments
Expanding on this little, there are two cases of logging during testing:
This is just about case 2. For that reason, I thinking testing.Slog() should return a slog.Handler that is essentially a slog.TextHandler, except it always logs to the testing.Log stream. For case 1, people can just log to a bytes.Buffer and poke around as usual, or maybe there can be a log/slog/testslog that has a handler that just appends slog.Records to a big slice you can look at after the test. |
See also previous discussion |
Yeah, I agree having something like "if this test fails, show me some logs" is a productive idea and different from testing against the contents of logged output. The twist seems like - the way that I had some results decorating a I do think other approaches might need a way to access the result of |
This comment has been minimized.
This comment has been minimized.
This proposal has been added to the active column of the proposals project |
Does the In my experience adding the If the goal is to only display log lines when a test fails, I've seen this approach (https://go.dev/play/p/WXHWdRW8s4Z) in a few places: func testLogger(t *testing.T) *slog.Logger {
t.Helper()
buf := new(bytes.Buffer)
opts := &slog.HandlerOptions{AddSource: true}
log := slog.New(slog.NewTextHandler(buf, opts))
t.Cleanup(func() {
if !t.Failed() {
return
}
t.Helper()
t.Log("log output\n", buf.String())
})
return log
} This approach makes the failure message and log lines much more distinct. It also addresses some of the challenges in Maybe |
I'd be totally happy if the output would made to be look different. But I don't think that is possible with the current API. If you log to stdout/stderr, the output looks different, but is then also not correlated with the actual test and interleaved with its
Even if you only get the output of failed tests, that still means if you have more than one of those you have no idea what log output belongs where. And even if you only have one failing test (e.g. by re-running it with So, no. The goal is not only to limit log output to failures - it's also to correctly correlate and interleave it with other test output.
I think both of these would work for me. |
#52751 (comment) would make more third-party (or similarly, deliberately decoupled) solutions possible, as the top post here discusses. |
Looking at the output of https://go.dev/play/p/8s2T3VcEi7C, Looking at that ouptut I realize that stdout is also hidden by
That is already possible today, so that must not be the goal of this proposal. |
For me at least, not when I run it locally. |
What does #52751 provide that isn't already possible with A library like https://github.com/neilotoole/slogt could implement a handler that:
This would match exactly the output proposed in #52751. The problem faced by https://github.com/neilotoole/slogt must not be retrieving the line that called In #52751 (comment) I suggested what was missing was a way to print test output that was indented and displayed in the same place as Looking at the output today (https://go.dev/play/p/ES9_Y5BC5kj), it seems those problems are fixed. It should already be possible to implement a logger that has the behaviour described in https://github.com/neilotoole/slogt#deficiency by writing the log output with: fmt.Printf(" %v: %v\n", filepath.Base(source), logMsg) I believe the formatting of test output has changed a few times. It seems possible it could change again in the future. A |
@Merovius what version of Go? Is that from go1.18? The output you shared is what I remember as well, but I can no longer reproduce with the two latest versions of Go. |
|
TBF, with |
While I think using hooks in |
Let me try to summarize. The request seems to be for a The request isn't merely for structured logging that behaves like The discussion about source locations (file:line information) doesn't seem relevant to this proposal. We already have an accepted proposal, #52751, that adds general source location marking to So if a
The So does that make everyone, as @Merovius says, "happy enough"? |
Writing to os.Stdout does not play well with (the lack of) -v, nor does it play well with parallel tests. That said, I think instead it would be fine to use something like
and then use Perhaps all that is needed is some kind of |
|
If it's a general writer we should probably line-buffer up to some max like 4k to handle Write callers that - unlike the log packages - do not guarantee one line per write. |
Name bikeshed: |
t.Output sounds fine to me. |
Here's a problem: When you use
then all uses of I don't know how annoying or error-prone that's going to be. |
I agree that if you write code to send the log messages to the wrong test, they show up in the wrong test. We can't fix every possible mistake. Writing to os.Stdout definitely does the wrong thing with parallel subtests though, and we can fix that. |
Seems like it's a moving target - while this is probably the wrong thing for tables of unit tests, this seems like the right thing for wider integration tests.
FWIW, I'd mention Kubernetes' ktesting as another interesting exploration of how to integrate |
The current proposal is to add the methods
Writes to
@carlmjohnson, this is your issue. If you're okay with this change, could you modify the issue title, and edit your top post to include the above or link to this comment? |
@apparentlymart That works for me. It's not that |
I assume use of |
Yes, it should. |
Change https://go.dev/cl/646956 mentions this issue: |
It sounds like we're converged on |
Have all remaining concerns about this proposal been addressed? The proposal is to add the following to // Output returns a Writer that writes to the same test output stream as TB.Log.
// The output is indented like TB.Log lines, but Output does not
// add source locations or newlines. The output is internally line
// buffered, and a call to TB.Log or the end of the test will implicitly
// flush the buffer, followed by a newline. It is an error to call Output
// after a test function returns.
func Output() io.Writer |
Based on the discussion above, this proposal seems like a likely accept. The proposal is to add the following to // Output returns a Writter that writes to the same test output stream as TB.Log.
// The output is indented like TB.Log lines, but Output does not
// add source locations or newlines. The output is internally line
// buffered, and a call to TB.Log or the end of the test will implicitly
// flush the buffer, followed by a newline. It is an error to call Output
// after a test function returns.
func Output() io.Writer |
Should we also specify what happens if someone writes to the I don't have any objection to that being disallowed, but I wonder about exactly how it is disallowed:
To be clear, I think this is more an implementation question than a blocker for accepting the proposal, but I was prompted to ask by the proposed documentation in the previous comment that talks about calling (I would be happy with any of those answers for what it's worth, but lightly prefer the second option just because it seems the most likely to let the incorrect test code promptly reach some suitable conclusion without necessarily disrupting the execution of subsequent tests. Whether test code will typically check the |
I think the earlier comment means all writes will panic after a test complete, to mirror the behavior of t.Log (and you're not getting a fresh io.Writer on every call to t.Output). |
Oh, good catch. The fact that you shouldn't call Write after the test finishes should certainly be documented. I agree that by analogy to t.Log, it makes sense for writes to the output to panic after the test returns. I'll note that this behavior isn't documented. It's not even documented that you shouldn't do this. The implementation does go to some trouble to emit log lines if a subtest has exited, but the parent hasn't, but will ultimately panic if it can't get the log out. |
No change in consensus, so accepted. 🎉 The proposal is to add the following to // Output returns a Writter that writes to the same test output stream as TB.Log.
// The output is indented like TB.Log lines, but Output does not
// add source locations or newlines. The output is internally line
// buffered, and a call to TB.Log or the end of the test will implicitly
// flush the buffer, followed by a newline. After a test function returns,
// neither Output nor the Write method may be called.
func Output() io.Writer In the implementation, calling |
https://go.dev/cl/646956 implements most of this proposal. |
Log's behaviour is to allow output of a complete subtest to get appended to the output of an incomplete parent test. The call to Log only panics if no incomplete parent was found when the subtest is complete. Output matches this behaviour in https://go.dev/cl/646956. The last sentence of the doc comment has been adjusted to reflect this:
|
Change https://go.dev/cl/672395 mentions this issue: |
Output is a method on T, B and F. It provides an io.Writer that writes to the same test output stream as TB.Log. The new output writer is used to refactor the implementation of Log. It maintains the formatting provided by Log while making call site information optional. Additionally, it provides buffering of log messages. This fixes and expands on https://go-review.googlesource.com/c/go/+/646956. For #59928. Change-Id: I08179c35a681f601cf125c0f4aeb648bc10c7a9f GitHub-Last-Rev: e6e2027 GitHub-Pull-Request: #73703 Reviewed-on: https://go-review.googlesource.com/c/go/+/672395 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Alan Donovan <adonovan@google.com> Reviewed-by: Alan Donovan <adonovan@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Auto-Submit: Jonathan Amsterdam <jba@google.com>
In a test, you often want to mock out the logger. It would be nice to be able to call t.Slog() and get a log/slog logger that send output to t.Log() with the correct caller information.
See https://github.com/neilotoole/slogt for an example of a third party library providing this functionality, but note that it cannot provide correct caller information:
It seems like this needs to be done on the Go side to fix the callsite.
The text was updated successfully, but these errors were encountered: