Add rusage-logfile flag to optionally send rusage to a file

Currently the rusage is reported to stdout and rather cumbersome to parse. The
new flag rusage-logfile can be used to specify a file to which the log will be
written instead.

Signed-off-by: Dan Čermák <dcermak@suse.com>
This commit is contained in:
Dan Čermák 2021-05-28 17:09:27 +02:00
parent 4fa566ead2
commit a6b3b6f672
No known key found for this signature in database
GPG Key ID: 8F8C178E966641D3
6 changed files with 44 additions and 3 deletions

View File

@ -334,6 +334,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budOptions) error {
Isolation: isolation, Isolation: isolation,
Labels: iopts.Label, Labels: iopts.Label,
Layers: layers, Layers: layers,
LogRusage: iopts.LogRusage,
Manifest: iopts.Manifest, Manifest: iopts.Manifest,
MaxPullPushRetries: maxPullPushRetries, MaxPullPushRetries: maxPullPushRetries,
NamespaceOptions: namespaceOptions, NamespaceOptions: namespaceOptions,
@ -349,6 +350,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budOptions) error {
ReportWriter: reporter, ReportWriter: reporter,
Runtime: iopts.Runtime, Runtime: iopts.Runtime,
RuntimeArgs: runtimeFlags, RuntimeArgs: runtimeFlags,
RusageLogFile: iopts.RusageLogFile,
SignBy: iopts.SignBy, SignBy: iopts.SignBy,
SignaturePolicyPath: iopts.SignaturePolicy, SignaturePolicyPath: iopts.SignaturePolicy,
Squash: iopts.Squash, Squash: iopts.Squash,
@ -357,7 +359,6 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budOptions) error {
TransientMounts: iopts.Volumes, TransientMounts: iopts.Volumes,
OciDecryptConfig: decConfig, OciDecryptConfig: decConfig,
Jobs: &iopts.Jobs, Jobs: &iopts.Jobs,
LogRusage: iopts.LogRusage,
} }
if iopts.IgnoreFile != "" { if iopts.IgnoreFile != "" {
excludes, err := parseDockerignore(iopts.IgnoreFile) excludes, err := parseDockerignore(iopts.IgnoreFile)

View File

@ -217,6 +217,8 @@ type BuildOptions struct {
Jobs *int Jobs *int
// LogRusage logs resource usage for each step. // LogRusage logs resource usage for each step.
LogRusage bool LogRusage bool
// File to which the Rusage logs will be saved to instead of stdout
RusageLogFile string
// Excludes is a list of excludes to be used instead of the .dockerignore file. // Excludes is a list of excludes to be used instead of the .dockerignore file.
Excludes []string Excludes []string
// From is the image name to use to replace the value specified in the first // From is the image name to use to replace the value specified in the first

View File

@ -116,6 +116,7 @@ type Executor struct {
stagesSemaphore *semaphore.Weighted stagesSemaphore *semaphore.Weighted
jobs int jobs int
logRusage bool logRusage bool
rusageLogFile *os.File
imageInfoLock sync.Mutex imageInfoLock sync.Mutex
imageInfoCache map[string]imageTypeAndHistoryAndDiffIDs imageInfoCache map[string]imageTypeAndHistoryAndDiffIDs
fromOverride string fromOverride string
@ -183,6 +184,18 @@ func NewExecutor(logger *logrus.Logger, store storage.Store, options define.Buil
writer = ioutil.Discard writer = ioutil.Discard
} }
var rusageLogFile *os.File
if options.LogRusage && !options.Quiet {
if options.RusageLogFile == "" {
rusageLogFile = os.Stdout
} else {
rusageLogFile, err = os.OpenFile(options.RusageLogFile, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return nil, err
}
}
}
exec := Executor{ exec := Executor{
logger: logger, logger: logger,
stages: make(map[string]*StageExecutor), stages: make(map[string]*StageExecutor),
@ -241,6 +254,7 @@ func NewExecutor(logger *logrus.Logger, store storage.Store, options define.Buil
terminatedStage: make(map[string]struct{}), terminatedStage: make(map[string]struct{}),
jobs: jobs, jobs: jobs,
logRusage: options.LogRusage, logRusage: options.LogRusage,
rusageLogFile: rusageLogFile,
imageInfoCache: make(map[string]imageTypeAndHistoryAndDiffIDs), imageInfoCache: make(map[string]imageTypeAndHistoryAndDiffIDs),
fromOverride: options.From, fromOverride: options.From,
manifest: options.Manifest, manifest: options.Manifest,
@ -530,6 +544,12 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
} }
} }
cleanupImages = nil cleanupImages = nil
if b.rusageLogFile != nil && b.rusageLogFile != os.Stdout {
// we deliberately ignore the error here, as this
// function can be called multiple times
b.rusageLogFile.Close()
}
return lastErr return lastErr
} }

View File

@ -704,8 +704,8 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
fmt.Fprintf(s.executor.out, "error gathering resource usage information: %v\n", err) fmt.Fprintf(s.executor.out, "error gathering resource usage information: %v\n", err)
return return
} }
if !s.executor.quiet && s.executor.logRusage { if s.executor.rusageLogFile != nil {
fmt.Fprintf(s.executor.out, "%s\n", rusage.FormatDiff(usage.Subtract(resourceUsage))) fmt.Fprintf(s.executor.rusageLogFile, "%s\n", rusage.FormatDiff(usage.Subtract(resourceUsage)))
} }
resourceUsage = usage resourceUsage = usage
} }

View File

@ -84,6 +84,7 @@ type BudResults struct {
TLSVerify bool TLSVerify bool
Jobs int Jobs int
LogRusage bool LogRusage bool
RusageLogFile string
} }
// FromAndBugResults represents the results for common flags // FromAndBugResults represents the results for common flags
@ -199,6 +200,10 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
if err := fs.MarkHidden("log-rusage"); err != nil { if err := fs.MarkHidden("log-rusage"); err != nil {
panic(fmt.Sprintf("error marking the log-rusage flag as hidden: %v", err)) panic(fmt.Sprintf("error marking the log-rusage flag as hidden: %v", err))
} }
fs.StringVar(&flags.RusageLogFile, "rusage-logfile", "", "destination file to which rusage should be logged to instead of stdout (= the default).")
if err := fs.MarkHidden("rusage-logfile"); err != nil {
panic(fmt.Sprintf("error marking the rusage-logfile flag as hidden: %v", err))
}
fs.StringVar(&flags.Manifest, "manifest", "", "add the image to the specified manifest list. Creates manifest if it does not exist") fs.StringVar(&flags.Manifest, "manifest", "", "add the image to the specified manifest list. Creates manifest if it does not exist")
fs.BoolVar(&flags.NoCache, "no-cache", false, "Do not use existing cached images for the container build. Build from the start with a new set of cached layers.") fs.BoolVar(&flags.NoCache, "no-cache", false, "Do not use existing cached images for the container build. Build from the start with a new set of cached layers.")
fs.String("os", runtime.GOOS, "set the OS to the provided value instead of the current operating system of the host") fs.String("os", runtime.GOOS, "set the OS to the provided value instead of the current operating system of the host")

View File

@ -2423,6 +2423,19 @@ EOM
expect_output --substring ".*\(system\).*\(user\).*\(elapsed\).*input.*output" expect_output --substring ".*\(system\).*\(user\).*\(elapsed\).*input.*output"
} }
@test "bud with-rusage-logfile" {
_prefetch alpine
run_buildah bud --log-rusage --rusage-logfile "foo.log" --layers --pull=false --format docker --signature-policy ${TESTSDIR}/policy.json ${TESTSDIR}/bud/shell
# the logfile should exist
if [ ! -e "foo.log" ]; then die "foo.log not present!"; fi
# expect that foo.log only contains lines that were formatted using pkg/rusage.FormatDiff()
formatted_lines=$(grep ".*\(system\).*\(user\).*\(elapsed\).*input.*output" foo.log | wc -l)
line_count=$(cat foo.log | wc -l)
if [[ "$formatted_lines" -ne "$line_count" ]]; then
die "Got ${formatted_lines} lines formatted with pkg/rusage.FormatDiff() but foo.log has ${line_count} lines"
fi
}
@test "bud-caching-from-scratch" { @test "bud-caching-from-scratch" {
_prefetch alpine _prefetch alpine
# run the build once # run the build once