11package main
22
33import (
4+ "context"
45 "fmt"
56 "os"
67 "os/exec"
@@ -15,10 +16,14 @@ import (
1516 "github.com/docker/cli/cli/version"
1617 "github.com/docker/cli/cmd/docker/internal/appcontext"
1718 "github.com/docker/docker/api/types/versions"
19+ "github.com/moby/buildkit/util/tracing/detect"
1820 "github.com/pkg/errors"
1921 "github.com/sirupsen/logrus"
2022 "github.com/spf13/cobra"
2123 "github.com/spf13/pflag"
24+ "go.opentelemetry.io/otel"
25+ "go.opentelemetry.io/otel/propagation"
26+ "go.opentelemetry.io/otel/trace"
2227)
2328
2429func newDockerCommand (dockerCli * command.DockerCli ) * cli.TopLevelCommand {
@@ -40,7 +45,25 @@ func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
4045 return fmt .Errorf ("docker: '%s' is not a docker command.\n See 'docker --help'" , args [0 ])
4146 },
4247 PersistentPreRunE : func (cmd * cobra.Command , args []string ) error {
43- return isSupported (cmd , dockerCli )
48+ if err := isSupported (cmd , dockerCli ); err != nil {
49+ return err
50+ }
51+
52+ name := cmd .Name ()
53+ for p := cmd .Parent (); p != nil && p != cmd .Root (); p = p .Parent () {
54+ name = p .Name () + " " + name
55+ }
56+
57+ ctx , _ := otel .Tracer ("" ).Start (cmd .Context (), name )
58+ cmd .SetContext (ctx )
59+ dockerCli .WithContext (ctx )
60+
61+ return nil
62+ },
63+ PersistentPostRun : func (cmd * cobra.Command , args []string ) {
64+ // TODO: There doesn't seem to be a way to determine if the command returned an an error
65+ // so we can set the span status here.
66+ trace .SpanFromContext (cmd .Context ()).End ()
4467 },
4568 Version : fmt .Sprintf ("%s, build %s" , version .Version , version .GitCommit ),
4669 DisableFlagsInUseLine : true ,
@@ -54,6 +77,10 @@ func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
5477 cmd .SetOut (dockerCli .Out ())
5578 cmd .SetErr (dockerCli .Err ())
5679
80+ // Cobra's context may be nil in some cases, so initialize it here.
81+ ctx := context .TODO ()
82+ cmd .SetContext (ctx )
83+
5784 opts , helpCmd = cli .SetupRootCommand (cmd )
5885 registerCompletionFuncForGlobalFlags (dockerCli , cmd )
5986 cmd .Flags ().BoolP ("version" , "v" , false , "Print version information and quit" )
@@ -227,6 +254,29 @@ func runDocker(dockerCli *command.DockerCli) error {
227254 return err
228255 }
229256
257+ // Buildkit's detect package currently follows the old otel spec which defaulted to gRPC.
258+ // Since the spec changed to default to http/protobuf.
259+ // If these env vars are not set then we set them to the new default so detect will give us the expected protocol.
260+ // This is the same as on the dockerd side.
261+ // This can be removed after buildkit's detect package is updated.
262+ if os .Getenv ("OTEL_EXPORTER_OTLP_TRACES_PROTOCOL" ) == "" && os .Getenv ("OTEL_EXPORTER_OTLP_PROTOCOL" ) == "" {
263+ os .Setenv ("OTEL_EXPORTER_OTLP_TRACES_PROTOCOL" , "http/protobuf" )
264+ }
265+ if v := os .Getenv ("OTEL_SERVICE_NAME" ); v == "" {
266+ os .Setenv ("OTEL_SERVICE_NAME" , cmd .Root ().Name ())
267+ }
268+
269+ tp , err := detect .TracerProvider ()
270+ if err != nil {
271+ logrus .WithError (err ).Debug ("Failed to initialize tracing" )
272+ }
273+
274+ otel .SetTextMapPropagator (propagation .NewCompositeTextMapPropagator (propagation.TraceContext {}, propagation.Baggage {}))
275+ if tp != nil {
276+ otel .SetTracerProvider (tp )
277+ defer detect .Shutdown (context .Background ())
278+ }
279+
230280 var envs []string
231281 args , os .Args , envs , err = processAliases (dockerCli , cmd , args , os .Args )
232282 if err != nil {
0 commit comments