@@ -26,8 +26,7 @@ func BaseCommandAttributes(cmd *cobra.Command, streams Streams) []attribute.KeyV
26
26
// Note: this should be the last func to wrap/modify the PersistentRunE/RunE funcs before command execution.
27
27
//
28
28
// 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 ) {
31
30
// If PersistentPreRunE is nil, make it execute PersistentPreRun and return nil by default
32
31
ogPersistentPreRunE := cmd .PersistentPreRunE
33
32
if ogPersistentPreRunE == nil {
@@ -55,19 +54,27 @@ func (cli *DockerCli) InstrumentCobraCommands(cmd *cobra.Command, mp metric.Mete
55
54
}
56
55
cmd .RunE = func (cmd * cobra.Command , args []string ) error {
57
56
// 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 )
60
58
cmdErr := ogRunE (cmd , args )
61
- stopCobraCmdTimer (cmdErr )
59
+ stopInstrumentation (cmdErr )
62
60
return cmdErr
63
61
}
64
62
65
63
return ogPersistentPreRunE (cmd , args )
66
64
}
67
65
}
68
66
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 )
71
78
durationCounter , _ := meter .Float64Counter (
72
79
"command.time" ,
73
80
metric .WithDescription ("Measures the duration of the cobra command" ),
@@ -76,12 +83,20 @@ func startCobraCommandTimer(cmd *cobra.Command, meter metric.Meter, attrs []attr
76
83
start := time .Now ()
77
84
78
85
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
+
79
91
duration := float64 (time .Since (start )) / float64 (time .Millisecond )
80
92
cmdStatusAttrs := attributesFromError (err )
81
93
durationCounter .Add (ctx , duration ,
82
94
metric .WithAttributes (attrs ... ),
83
95
metric .WithAttributes (cmdStatusAttrs ... ),
84
96
)
97
+ if mp , ok := mp .(MeterProvider ); ok {
98
+ mp .ForceFlush (ctx )
99
+ }
85
100
}
86
101
}
87
102
0 commit comments