@@ -26,8 +26,7 @@ func BaseCommandAttributes(cmd *cobra.Command, streams Streams) []attribute.KeyV
2626// Note: this should be the last func to wrap/modify the PersistentRunE/RunE funcs before command execution.
2727//
2828// can also be used for spans!
29- func (cli * DockerCli ) InstrumentCobraCommands (cmd * cobra.Command , mp metric.MeterProvider ) {
30- meter := getDefaultMeter (mp )
29+ func (cli * DockerCli ) InstrumentCobraCommands (ctx context.Context , cmd * cobra.Command ) {
3130 // If PersistentPreRunE is nil, make it execute PersistentPreRun and return nil by default
3231 ogPersistentPreRunE := cmd .PersistentPreRunE
3332 if ogPersistentPreRunE == nil {
@@ -55,19 +54,27 @@ func (cli *DockerCli) InstrumentCobraCommands(cmd *cobra.Command, mp metric.Mete
5554 }
5655 cmd .RunE = func (cmd * cobra.Command , args []string ) error {
5756 // start the timer as the first step of every cobra command
58- baseAttrs := BaseCommandAttributes (cmd , cli )
59- stopCobraCmdTimer := startCobraCommandTimer (cmd , meter , baseAttrs )
57+ stopInstrumentation := cli .StartInstrumentation (cmd )
6058 cmdErr := ogRunE (cmd , args )
61- stopCobraCmdTimer (cmdErr )
59+ stopInstrumentation (cmdErr )
6260 return cmdErr
6361 }
6462
6563 return ogPersistentPreRunE (cmd , args )
6664 }
6765}
6866
69- func startCobraCommandTimer (cmd * cobra.Command , meter metric.Meter , attrs []attribute.KeyValue ) func (err error ) {
70- ctx := cmd .Context ()
67+ // StartInstrumentation instruments CLI commands with the individual metrics and spans configured.
68+ // It's the main command OTel utility, and new command-related metrics should be added to it.
69+ // It should be called immediately before command execution, and returns a stopInstrumentation function
70+ // that must be called with the error resulting from the command execution.
71+ func (cli * DockerCli ) StartInstrumentation (cmd * cobra.Command ) (stopInstrumentation func (error )) {
72+ baseAttrs := BaseCommandAttributes (cmd , cli )
73+ return startCobraCommandTimer (cli .MeterProvider (), baseAttrs )
74+ }
75+
76+ func startCobraCommandTimer (mp metric.MeterProvider , attrs []attribute.KeyValue ) func (err error ) {
77+ meter := getDefaultMeter (mp )
7178 durationCounter , _ := meter .Float64Counter (
7279 "command.time" ,
7380 metric .WithDescription ("Measures the duration of the cobra command" ),
@@ -76,12 +83,20 @@ func startCobraCommandTimer(cmd *cobra.Command, meter metric.Meter, attrs []attr
7683 start := time .Now ()
7784
7885 return func (err error ) {
86+ // Use a new context for the export so that the command being cancelled
87+ // doesn't affect the metrics, and we get metrics for cancelled commands.
88+ ctx , cancel := context .WithTimeout (context .Background (), exportTimeout )
89+ defer cancel ()
90+
7991 duration := float64 (time .Since (start )) / float64 (time .Millisecond )
8092 cmdStatusAttrs := attributesFromError (err )
8193 durationCounter .Add (ctx , duration ,
8294 metric .WithAttributes (attrs ... ),
8395 metric .WithAttributes (cmdStatusAttrs ... ),
8496 )
97+ if mp , ok := mp .(MeterProvider ); ok {
98+ mp .ForceFlush (ctx )
99+ }
85100 }
86101}
87102
0 commit comments