forked from gruntwork-io/terratest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
134 lines (118 loc) · 5.17 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// A CLI command to parse parallel terratest output to produce test summaries and break out interleaved test output.
//
// This command will take as input a terratest log output from either stdin (through a pipe) or from a file, and output
// to a directory the following files:
// outputDir
// |-> TEST_NAME.log
// |-> summary.log
// |-> report.xml
// where:
// - `TEST_NAME.log` is a log for each test run that only includes the relevant logs for that test.
// - `summary.log` is a summary of all the tests in the suite, including PASS/FAIL information.
// - `report.xml` is the test summary in junit XML format to be consumed by a CI engine.
//
// Certain tradeoffs were made in the decision to implement this functionality as a separate parsing command, as opposed
// to being built into the logger module as part of `Logf`. Specifically, this implementation avoids the difficulties of
// hooking into go's testing framework to be able to extract the summary logs, at the expense of a more complicated
// implementation in handling various corner cases due to logging flexibility. Here are the list of pros and cons of
// this approach that were considered:
//
// Pros:
// - Robust to unexpected failures in testing code, like `ctrl+c`, panics, OOM kills and the like since the parser is
// not tied to the testing process. This approach is less likely to miss these entries, and can be surfaced to the
// summary view for easy viewing in CI engines (no need to scroll), like the panic example.
// - Can combine `go test` output (e.g `--- PASS` entries) with the log entries for the test in a single log file.
// - Can extract out the summary view (those are all `go test` log entries).
// - Can build `junit.xml` report that CI engines can use for test insights.
//
// Cons:
// - Complicated implementation that is potentially brittle. E.g if someone decides to change the logging format then
// this will all break. If we hook during the test, then the implementation is easier because those logs are all emitted
// at certain points in code, the information of which is lost in the final log and have to parse out.
// - Have to store all the logs twice (the full interleaved version, and the broken out version) because the parsing
// depends on logs being available. (NOTE: this is avoidable with a pipe).
package main
import (
"fmt"
"os"
"path/filepath"
"github.com/gruntwork-io/gruntwork-cli/entrypoint"
"github.com/gruntwork-io/gruntwork-cli/errors"
"github.com/gruntwork-io/gruntwork-cli/logging"
"github.com/gruntwork-io/terratest/modules/logger/parser"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var logger = logging.GetLogger("terratest_log_parser")
const CUSTOM_USAGE_TEXT = `Usage: terratest_log_parser [--help] [--log-level=info] [--testlog=LOG_INPUT] [--outputdir=OUTPUT_DIR]
A tool for parsing parallel terratest output to produce a test summary and to break out the interleaved logs by test for better debuggability.
Options:
--log-level LEVEL Set the log level to LEVEL. Must be one of: [panic fatal error warning info debug]
(default: "info")
--testlog value Path to file containing test log. If unset will use stdin.
--outputdir value Path to directory to output test output to. If unset will use the current directory.
--help, -h show help
`
func run(cliContext *cli.Context) error {
filename := cliContext.String("testlog")
outputDir := cliContext.String("outputdir")
logLevel := cliContext.String("log-level")
level, err := logrus.ParseLevel(logLevel)
if err != nil {
return errors.WithStackTrace(err)
}
logger.SetLevel(level)
var file *os.File
if filename != "" {
logger.Infof("reading from file")
file, err = os.Open(filename)
if err != nil {
logger.Fatalf("Error opening file: %s", err)
}
} else {
logger.Infof("reading from stdin")
file = os.Stdin
}
defer file.Close()
outputDir, err = filepath.Abs(outputDir)
if err != nil {
logger.Fatalf("Error extracting absolute path of output directory: %s", err)
}
parser.SpawnParsers(logger, file, outputDir)
return nil
}
func main() {
app := entrypoint.NewApp()
cli.AppHelpTemplate = CUSTOM_USAGE_TEXT
entrypoint.HelpTextLineWidth = 120
app.Name = "terratest_log_parser"
app.Author = "Gruntwork <www.gruntwork.io>"
app.Description = `A tool for parsing parallel terratest output to produce a test summary and to break out the interleaved logs by test for better debuggability.`
app.Action = run
currentDir, err := os.Getwd()
if err != nil {
logger.Fatalf("Error finding current directory: %s", err)
}
defaultOutputDir := filepath.Join(currentDir, "out")
logInputFlag := cli.StringFlag{
Name: "testlog, l",
Value: "",
Usage: "Path to file containing test log. If unset will use stdin.",
}
outputDirFlag := cli.StringFlag{
Name: "outputdir, o",
Value: defaultOutputDir,
Usage: "Path to directory to output test output to. If unset will use the current directory.",
}
logLevelFlag := cli.StringFlag{
Name: "log-level",
Value: logrus.InfoLevel.String(),
Usage: fmt.Sprintf("Set the log level to `LEVEL`. Must be one of: %v", logrus.AllLevels),
}
app.Flags = []cli.Flag{
logLevelFlag,
logInputFlag,
outputDirFlag,
}
entrypoint.RunApp(app)
}