Setup panicwrap
This commit is contained in:
parent
38d4f2a1bd
commit
7b64c2597b
|
@ -0,0 +1,29 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// These are the environmental variables that determine if we log, and if
|
||||
// we log whether or not the log should go to a file.
|
||||
const EnvLog = "TF_LOG"
|
||||
const EnvLogFile = "TF_LOG_PATH"
|
||||
|
||||
// logOutput determines where we should send logs (if anywhere).
|
||||
func logOutput() (logOutput io.Writer, err error) {
|
||||
logOutput = nil
|
||||
if os.Getenv(EnvLog) != "" {
|
||||
logOutput = os.Stderr
|
||||
|
||||
if logPath := os.Getenv(EnvLogFile); logPath != "" {
|
||||
var err error
|
||||
logOutput, err = os.Create(logPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
76
main.go
76
main.go
|
@ -6,7 +6,9 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/ActiveState/tail"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/mitchellh/panicwrap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -14,7 +16,79 @@ func main() {
|
|||
}
|
||||
|
||||
func realMain() int {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
var wrapConfig panicwrap.WrapConfig
|
||||
|
||||
if !panicwrap.Wrapped(&wrapConfig) {
|
||||
// Determine where logs should go in general (requested by the user)
|
||||
logWriter, err := logOutput()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Couldn't setup log output: %s", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
// We always send logs to a temporary file that we use in case
|
||||
// there is a panic. Otherwise, we delete it.
|
||||
logTempFile, err := ioutil.TempFile("", "terraform-log")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Couldn't setup logging tempfile: %s", err)
|
||||
return 1
|
||||
}
|
||||
logTempFile.Close()
|
||||
defer os.Remove(logTempFile.Name())
|
||||
|
||||
// Tell the logger to log to this file
|
||||
os.Setenv(EnvLog, "1")
|
||||
os.Setenv(EnvLogFile, logTempFile.Name())
|
||||
|
||||
if logWriter != nil {
|
||||
// Start tailing the file beforehand to get the data
|
||||
t, err := tail.TailFile(logTempFile.Name(), tail.Config{
|
||||
Follow: true,
|
||||
Logger: tail.DiscardingLogger,
|
||||
MustExist: true,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Couldn't setup logging tempfile: %s", err)
|
||||
return 1
|
||||
}
|
||||
go func() {
|
||||
for line := range t.Lines {
|
||||
logWriter.Write([]byte(line.Text + "\n"))
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Create the configuration for panicwrap and wrap our executable
|
||||
wrapConfig.Handler = panicHandler(logTempFile)
|
||||
exitStatus, err := panicwrap.Wrap(&wrapConfig)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Couldn't start Terraform: %s", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
// If >= 0, we're the parent, so just exit
|
||||
if exitStatus >= 0 {
|
||||
return exitStatus
|
||||
}
|
||||
|
||||
// We're the child, so just close the tempfile we made in order to
|
||||
// save file handles since the tempfile is only used by the parent.
|
||||
logTempFile.Close()
|
||||
}
|
||||
|
||||
// Call the real main
|
||||
return wrappedMain()
|
||||
}
|
||||
|
||||
func wrappedMain() int {
|
||||
logOutput, err := logOutput()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error starting Terraform: %s", err)
|
||||
return 1
|
||||
}
|
||||
if logOutput != nil {
|
||||
log.SetOutput(logOutput)
|
||||
}
|
||||
|
||||
// Get the command line args. We shortcut "--version" and "-v" to
|
||||
// just show the version.
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/panicwrap"
|
||||
)
|
||||
|
||||
// This output is shown if a panic happens.
|
||||
const panicOutput = `
|
||||
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
Terraform crashed! This is always indicative of a bug within Terraform.
|
||||
A crash log has been placed at "crash.log" relative to your current
|
||||
working directory. It would be immensely helpful if you could please
|
||||
report the crash with Terraform[1] so that we can fix this.
|
||||
|
||||
[1]: https://github.com/hashicorp/terraform/issues
|
||||
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
`
|
||||
|
||||
// panicHandler is what is called by panicwrap when a panic is encountered
|
||||
// within Terraform. It is guaranteed to run after the resulting process has
|
||||
// exited so we can take the log file, add in the panic, and store it
|
||||
// somewhere locally.
|
||||
func panicHandler(logF *os.File) panicwrap.HandlerFunc {
|
||||
return func(m string) {
|
||||
// Right away just output this thing on stderr so that it gets
|
||||
// shown in case anything below fails.
|
||||
fmt.Fprintf(os.Stderr, fmt.Sprintf("%s\n", m))
|
||||
|
||||
// Create the crash log file where we'll write the logs
|
||||
f, err := os.Create("crash.log")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to create crash log file: %s", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Seek the log file back to the beginning
|
||||
if _, err = logF.Seek(0, 0); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to seek log file for crash: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Copy the contents to the crash file. This will include
|
||||
// the panic that just happened.
|
||||
if _, err = io.Copy(f, logF); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write crash log: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Tell the user a crash occurred in some helpful way that
|
||||
// they'll hopefully notice.
|
||||
fmt.Printf("\n\n")
|
||||
fmt.Println(strings.TrimSpace(panicOutput))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue