diff --git a/builder/dockerfile/builder.go b/builder/dockerfile/builder.go index 917e06575e..aec68571c8 100644 --- a/builder/dockerfile/builder.go +++ b/builder/dockerfile/builder.go @@ -50,6 +50,7 @@ func NewBuildManager(b builder.Backend) *BuildManager { // Build starts a new build from a BuildConfig func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (*builder.Result, error) { + buildsTriggered.Inc() if config.Options.Dockerfile == "" { config.Options.Dockerfile = builder.DefaultDockerfileName } @@ -144,6 +145,7 @@ func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*buil addNodesForLabelOption(dockerfile.AST, b.options.Labels) if err := checkDispatchDockerfile(dockerfile.AST); err != nil { + buildsFailed.WithValues(metricsDockerfileSyntaxError).Inc() return nil, err } @@ -153,12 +155,14 @@ func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*buil } if b.options.Target != "" && !dispatchState.isCurrentStage(b.options.Target) { + buildsFailed.WithValues(metricsBuildTargetNotReachableError).Inc() return nil, errors.Errorf("failed to reach build target %s in Dockerfile", b.options.Target) } b.warnOnUnusedBuildArgs() if dispatchState.imageID == "" { + buildsFailed.WithValues(metricsDockerfileEmptyError).Inc() return nil, errors.New("No image was generated. Is your Dockerfile empty?") } return &builder.Result{ImageID: dispatchState.imageID, FromImage: dispatchState.baseImage}, nil @@ -181,6 +185,7 @@ func (b *Builder) dispatchDockerfileWithCancellation(dockerfile *parser.Result) case <-b.clientCtx.Done(): logrus.Debug("Builder: build cancelled!") fmt.Fprint(b.Stdout, "Build cancelled") + buildsFailed.WithValues(metricsBuildCanceled).Inc() return nil, errors.New("Build cancelled") default: // Not cancelled yet, keep going... diff --git a/builder/dockerfile/evaluator.go b/builder/dockerfile/evaluator.go index 8fb80aca6b..8402af9087 100644 --- a/builder/dockerfile/evaluator.go +++ b/builder/dockerfile/evaluator.go @@ -134,6 +134,7 @@ func (b *Builder) dispatch(options dispatchOptions) (*dispatchState, error) { // To ensure the user is given a decent error message if the platform // on which the daemon is running does not support a builder command. if err := platformSupports(strings.ToLower(cmd)); err != nil { + buildsFailed.WithValues(metricsCommandNotSupportedError).Inc() return nil, err } @@ -155,6 +156,7 @@ func (b *Builder) dispatch(options dispatchOptions) (*dispatchState, error) { processFunc := createProcessWordFunc(options.shlex, cmd, envs) words, err := getDispatchArgsFromNode(ast, processFunc, msg) if err != nil { + buildsFailed.WithValues(metricsErrorProcessingCommandsError).Inc() return nil, err } args = append(args, words...) @@ -163,6 +165,7 @@ func (b *Builder) dispatch(options dispatchOptions) (*dispatchState, error) { f, ok := evaluateTable[cmd] if !ok { + buildsFailed.WithValues(metricsUnknownInstructionError).Inc() return nil, fmt.Errorf("unknown instruction: %s", upperCasedCmd) } if err := f(newDispatchRequestFromOptions(options, b, args)); err != nil { @@ -283,6 +286,7 @@ func checkDispatch(ast *parser.Node) error { // least one argument if upperCasedCmd == "ONBUILD" { if ast.Next == nil { + buildsFailed.WithValues(metricsMissingOnbuildArgumentsError).Inc() return errors.New("ONBUILD requires at least one argument") } } @@ -290,6 +294,6 @@ func checkDispatch(ast *parser.Node) error { if _, ok := evaluateTable[cmd]; ok { return nil } - + buildsFailed.WithValues(metricsUnknownInstructionError).Inc() return errors.Errorf("unknown instruction: %s", upperCasedCmd) } diff --git a/builder/dockerfile/metrics.go b/builder/dockerfile/metrics.go new file mode 100644 index 0000000000..5aa953aa79 --- /dev/null +++ b/builder/dockerfile/metrics.go @@ -0,0 +1,44 @@ +package dockerfile + +import ( + "github.com/docker/go-metrics" +) + +var ( + buildsTriggered metrics.Counter + buildsFailed metrics.LabeledCounter +) + +// Build metrics prometheus messages, these values must be initialized before +// using them. See the example below in the "builds_failed" metric definition. +const ( + metricsDockerfileSyntaxError = "dockerfile_syntax_error" + metricsDockerfileEmptyError = "dockerfile_empty_error" + metricsCommandNotSupportedError = "command_not_supported_error" + metricsErrorProcessingCommandsError = "error_processing_commands_error" + metricsBuildTargetNotReachableError = "build_target_not_reachable_error" + metricsMissingOnbuildArgumentsError = "missing_onbuild_arguments_error" + metricsUnknownInstructionError = "unknown_instruction_error" + metricsBuildCanceled = "build_canceled" +) + +func init() { + buildMetrics := metrics.NewNamespace("builder", "", nil) + + buildsTriggered = buildMetrics.NewCounter("builds_triggered", "Number of triggered image builds") + buildsFailed = buildMetrics.NewLabeledCounter("builds_failed", "Number of failed image builds", "reason") + for _, r := range []string{ + metricsDockerfileSyntaxError, + metricsDockerfileEmptyError, + metricsCommandNotSupportedError, + metricsErrorProcessingCommandsError, + metricsBuildTargetNotReachableError, + metricsMissingOnbuildArgumentsError, + metricsUnknownInstructionError, + metricsBuildCanceled, + } { + buildsFailed.WithValues(r) + } + + metrics.Register(buildMetrics) +} diff --git a/daemon/daemon.go b/daemon/daemon.go index 35ff2a66ef..823fb04f9c 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -732,13 +732,15 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe // FIXME: this method never returns an error info, _ := d.SystemInfo() - engineVersion.WithValues( + engineInfo.WithValues( dockerversion.Version, dockerversion.GitCommit, info.Architecture, info.Driver, info.KernelVersion, info.OperatingSystem, + info.OSType, + info.ID, ).Set(1) engineCpus.Set(float64(info.NCPU)) engineMemory.Set(float64(info.MemTotal)) diff --git a/daemon/metrics.go b/daemon/metrics.go index 65d92901ce..dd67a0f71e 100644 --- a/daemon/metrics.go +++ b/daemon/metrics.go @@ -12,7 +12,7 @@ var ( containerStates metrics.LabeledGauge imageActions metrics.LabeledTimer networkActions metrics.LabeledTimer - engineVersion metrics.LabeledGauge + engineInfo metrics.LabeledGauge engineCpus metrics.Gauge engineMemory metrics.Gauge healthChecksCounter metrics.Counter @@ -35,12 +35,14 @@ func init() { } networkActions = ns.NewLabeledTimer("network_actions", "The number of seconds it takes to process each network action", "action") - engineVersion = ns.NewLabeledGauge("engine", "The version and commit information for the engine process", metrics.Unit("info"), + engineInfo = ns.NewLabeledGauge("engine", "The information related to the engine and the OS it is running on", metrics.Unit("info"), "version", "commit", "architecture", - "graph_driver", "kernel", - "os", + "graphdriver", + "kernel", "os", + "os_type", + "daemon_id", // ID is a randomly generated unique identifier (e.g. UUID4) ) engineCpus = ns.NewGauge("engine_cpus", "The number of cpus that the host system of the engine has", metrics.Unit("cpus")) engineMemory = ns.NewGauge("engine_memory", "The number of bytes of memory that the host system of the engine has", metrics.Bytes)