-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add plugin execution duration metric #5046
base: master
Are you sure you want to change the base?
Conversation
92c5499
to
acf23c1
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5046 +/- ##
==========================================
- Coverage 61.33% 61.16% -0.18%
==========================================
Files 298 298
Lines 20692 20750 +58
==========================================
Hits 12691 12691
- Misses 7100 7158 +58
Partials 901 901 |
acf23c1
to
8931b01
Compare
The first commit is from here, for the sake of testing |
8366dd5
to
1cdf21d
Compare
8e1b02d
to
b064920
Compare
@krissetto Please rebase :) |
b064920
to
0e58311
Compare
will fix this soon |
ba18362
to
57f59c8
Compare
d57c8fa
to
8d7ff82
Compare
if runErr != nil { | ||
return nil, cobra.ShellCompDirectiveError | ||
} | ||
runErr = runCommand.Run() | ||
if verifiedDockerCli, ok := dockerCli.(*command.DockerCli); ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just shadow dockerCli
in this case instead of calling it verifiedDockerCli
? Not a huge fan, sorry (I was wondering what made it "verified", and then realized it was just the typecast cli)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thing is if we shadow dockerCli
here, then in the else condition we can't call dockerCli.Err()
as it'd be a zero value and not the original value of dockerCli
. I tried avoiding this where possible (see the other places where i do this cast), but here since we need the else
i wasn't sure how to proceed if not renaming the var.
Maybe there are better ways?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add a (can be un-exported) pluginInstrumenter
interface and match that?
|
||
// wrappedCmd is used to wrap an exec.Cmd in order to instrument the | ||
// command with otel by using the TimedRun() func | ||
type wrappedCmd struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of creating a wrapped exec.Cmd
type, how hard would it be to just have pluginmanager.PluginRunCommand
return a func
that starts a timer, runs the command, and ends the timer?
Okay if we end up going with a wrapper, I just try hard to avoid those/introducing more state since now it has a pointer to a cli, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm not sure that'd work..in the sense that we do things with the exec.Cmd
that pluginmanager.PluginRunCommand
returns aside from just running it (see the tryPluginRun()
func as an example). So i wanted to keep the return value of InstrumentPluginCommand
something close to what pluginmanager.PluginRunCommand
currently returns. I guess we could rework all that but i'm not sure it'd be worth the extra work.
I'm not a fan of the wrapped type here either, but it seemed like a way to keep code changes relatively minimal on the usage side of things, and to allow code that doesn't need the otel bits to continue to function as usual. Happy to change the impl if we can find some simpler way to handle this. The pointer to DockerCli is really only needed to get the meter provider
and the startPluginCommandTimer
func atm.
The main idea of this impl was to try and keep the approach as close as possible to the one used by InstrumentCobraCommand
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. Let me take a closer look, but if this is indeed the least invasive way that's okay by me. I also don't love having to remember to call a new method TimedRun
, which is why I'd have rather returned a func or something.
Signed-off-by: Christopher Petito <[email protected]>
8d7ff82
to
a5644cd
Compare
needs a rebase @krissetto 🙈 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM ✅
I had some nits/small comments, but I don't think they're blockers and might even be contentious, so I just pushed a branch here for comments.
Feel free to take them, I just wanted to simplify the code a bit, but we can merge this PR as-is and discuss other cleanups/refactors later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I want to have a closer look at this PR to see if we can find some alternatives for some parts 😅
if runErr != nil { | ||
return nil, cobra.ShellCompDirectiveError | ||
} | ||
runErr = runCommand.Run() | ||
if verifiedDockerCli, ok := dockerCli.(*command.DockerCli); ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add a (can be un-exported) pluginInstrumenter
interface and match that?
runCommand := verifiedDockerCli.InstrumentPluginCommand(pluginRunCommand) | ||
runErr = runCommand.TimedRun() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InstrumentPluginCommand
returns a non-exported type, and we always execute TimedRun()
afterwards, except for the tryPluginRun
function, where we construct it, and then later execute TimedRun
.
It's unfortunate that exec.Cmd
doesn't implement an interface, otherwise we could've made that the return, and we can't just define an interface that only has a func Run() error
Perhaps we should either make this function perform the TimedRun
; puny-code;
if cli is a pluginInstrumenter {
cli.RunInstrumented(pluginRunCommand)
} else {
pluginRunCommand.Run()
}
But that could even be a function, because effectively it's not a method of the CLI;
func RunPluginCommandInstrumented(cli, pluginCommand)
Hmm.. but then we get to startPluginCommandTimer
, which is even more generic, but missing the baseAttrs
🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unfortunate that exec.Cmd doesn't implement an interface, otherwise we could've made that the return, and we can't just define an interface that only has a func Run() error
My thoughts exactly, it's kinda annoying.
We can surely find a better way to do this, my original goal with the first implementation was to keep the approach as close as possible to that used to instrument the cobra commands, but maybe that's not a great idea all things considered. The dockerCli
struct / Cli
interface situation in the CLI (since we don't always pass around just the interface or just the struct) also caused some minor headaches and ugliness that would be nice to avoid if possible.
- What I did
Added utils for instrumenting plugin commands, and instrumented them to record their execution time.
Note: This is disconnected from the specific metrics and traces created by the plugins themselves and it not meant to replace those, it only represents a measurement of the binary execution of the plugin from the CLI's point of view
- How I did it
- How to verify it
- Description for the changelog
- A picture of a cute animal (not mandatory but encouraged)