-
-
Notifications
You must be signed in to change notification settings - Fork 995
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
WIP: Improve *-all error message output #722
base: main
Are you sure you want to change the base?
Conversation
I know using global variables is probably not the best way to achieve this, but I tried it earlier by incorporating the variables into the Also the go channel is currently not really needed, but might improve readability and future refactoring, as the concurrency is now explicitly coded. Do you have any suggestions for unit tests? |
Nice, thanks for the PR! Could you share an example of what the log output will look like now (a small snippet, not the whole thing, of course)? Please note that we're going to hold off on merging anything until #466 is resolved, as that's very high priority. Once that one is in, please pull the latest from master, and give us a ping to review. |
@brikis98 I just rebased from master and pushed again. The output currently looks like this: At the end of the exuction of an
Still, having all errors and the related modules printed at the end of the runner is a great benefit when running large executions. Please let me know what you think. |
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.
Thanks!
A few questions/thoughts:
-
Why use channels for this? Could we instead collect all the data in the
error
returned by*-all
commands and render the error at the end from that value? E.g., we already have aMutliError
struct in theerrors
package. It seems like working with a return value is easier than channels, global vars, etc. -
Could you add some tests for this? E.g., Add a new test in
integration_test.go
that runs against a fixture that has a deliberate error, and make sure thestdout
you get back shows that error properly?
Thanks for the feedback @brikis98! I did some refactoring and removed the channels. For sure I will add tests once this last bit is resolved. Do you have any idea why I still get the E.g. this is my current Error output at the end:
|
Not sure I follow. You seem to be getting an error about a missing variable. What does stdout or stderr have to do with it? |
What I posted is the new detailed output of my change. The idea is to have a summary of all module errors including their error messages (stderr) at the end of the execution. If you check out https://github.com/gruntwork-io/terragrunt/pull/722/files#diff-86e77ee353cd3bacb4a1f0c492bf9e2cR169 of my change, you can see that I am capturing the stderr and outputting it in the Somehow the So my question would be, if you have any idea why the |
Ohhhh, I gotcha, thx for providing the context 😁 The behavior you're seeing is probably from this: https://github.com/gruntwork-io/terragrunt/blob/master/cli/cli_app.go#L606-L607 |
Yep, thats it, thanks a lot for pointing me in the right direction! In the comment here it says:
So I assume it will not be a big issue moving this back to stdout, or what was the reasoning behind this? Also I found this part where the logger is set by default to stderr, which also leads to some pollution of my error output. I now adjusted both parts and now the detailed error output is clean. Can you think of any issues these changes could cause from the top of your head? Of course I will run the tests to make sure there are no obvious issues. |
Yes. Consider someone running the following:
They expect that the value of the output variable In general, if you run |
Thanks for clarifying, of course that makes perfect sense. In this case we would either have to live with the more verbose detailed error message at the end of the execution, or I would have to come up with some way of extracting the auto-init output from the detailed error message. I will look into it again. |
Alright, I made it work by saving the The current detailed error messages now look like this:
What do you think? Should we also make this output optional or use it by default? |
So that's the output you see at the end of an If so, I think that looks like a terrific improvement. I assume those errors are grouped by module? |
Yep, that is correct. As the I will try and add some tests now. |
Alright, I just added module and integration tests. Do you have further improvement suggestions? |
I did some refactoring to separate the normal and detailed errors in the |
Apologies for the delay! Been completely buried. I really appreciate this PR and will try to review this as soon as I can. 🍺 |
Don't worry, take your time! Just get back to me when you can 👍 |
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.
OK, we're back from our company offsite and going through PRs now. Thank you for your patience!
@@ -233,6 +270,8 @@ func (module *runningModule) moduleFinished(moduleErr error) { | |||
module.Module.TerragruntOptions.Logger.Printf("Module %s has finished with an error: %v", module.Module.Path, moduleErr) | |||
} | |||
|
|||
fmt.Fprintf(module.Writer, "%s\n%v\n\n%v\n", OutputMessageSeparator, module.Module.Path, module.OutStream.String()) |
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.
Why fmt.Fprintf
here?
@@ -80,6 +81,9 @@ type TerragruntOptions struct { | |||
// If you want stderr to go somewhere other than os.stderr | |||
ErrWriter io.Writer | |||
|
|||
// Stores output of auto-init so it can be removed later form other streams |
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.
// Stores output of auto-init so it can be removed later form other streams | |
// Stores output of auto-init so it can be removed later from other streams |
@@ -154,6 +168,8 @@ func runModules(modules map[string]*runningModule) error { | |||
waitGroup.Add(1) | |||
go func(module *runningModule) { | |||
defer waitGroup.Done() | |||
module.Module.TerragruntOptions.ErrWriter = io.MultiWriter(&module.OutStream, &module.ErrStream) | |||
module.Module.TerragruntOptions.Writer = &module.OutStream |
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'm a bit confused by this... You're overwriting TerragruntOptions.ErrWriter
to write to both module.OutStream
and module.ErrStream
... But what was the Terragrunt.LOptions.ErrWriter
value set to before that? Are module.OutStream
and module.ErrStream
initialized to anything? Will this buffer those errors until the very end or stream to stdout
/ stderr
?
Same questions go for TerragruntOptions.Writer
, with the additional one of what happens when you point a second item to module.OutStream
?
// generateDetailedErrorMessage extracts the clean stderr from a module and formats it for printing | ||
func generateDetailedErrorMessage(module *runningModule) error { | ||
// remove the auto-init pollution from the error stream | ||
cleanErrorOutput := strings.Replace(module.ErrStream.String(), module.Module.TerragruntOptions.InitStream.String(), "", -1) |
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.
Hm, string replacement feels... A bit hacky. Is there any way to have the AutoInit write to stdout
/ stderr
, but to not write to module.ErrStream
? That way, you could use module.ErrStream
directly, without having to clean anything out...
@@ -80,6 +81,9 @@ type TerragruntOptions struct { | |||
// If you want stderr to go somewhere other than os.stderr | |||
ErrWriter io.Writer | |||
|
|||
// Stores output of auto-init so it can be removed later form other streams | |||
InitStream bytes.Buffer |
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.
Perhaps there should be an AutoInitWriter
and AutoInitErrWriter
here instead? By default, these would be set to the same as Writer
and ErrWriter
. However, for xxx-all
commands, these could be set to separate values that write to stdout
/ stderr
but are not stored in those streams later used to print clean error messages?
Also, should this value be in the Clone
method? If not, explicitly add a comment there explaining why.
Hi all,
Thanks for this awesome project, we are using it in production with 50+ AWS accounts in our two organizations every day!
This PR aims to improve the readability of the
*-all
error messages. With our huge codebases, it can get really hard to find a small module, which errors out during a bigapply-all
run. The PR tries to improve this, by printing the root-cause error messages at the end of the execution run. Root-cause in this case means all module errors, excluding dependency errors.Please let me know if you have any suggestions to improve this further.