-
Notifications
You must be signed in to change notification settings - Fork 2k
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
cli: enable/disable specific debug targets #11081
base: main
Are you sure you want to change the base?
Conversation
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.
All in all, great work. You write much better go than I do. I left a lot of comments, but that is not indicative of any misgivings as much as feedback. Great job!
} | ||
|
||
// IsValid checks whether the key exists in the map | ||
func (m targetMap) IsValid(value string) bool { |
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.
Does this need to be exported? And should targetMap
be *targetMap
? Also, might this be better named Exists
?
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.
Good catch -- definitely no need to export.
Since maps are already implicitly byref in nature, I think a value receiver makes the most sense here.
As for the function naming, you're right, this is a bit confusing. I initially had a separation between "supported targets" (the list of all explicit targets that can be specified) versus "valid targets" (which also includes the meta sets "all" and "none"). But I stripped it out at the last minute before posting the draft PR.
Will reintroduce the "supported target" concept, which should clear up the distinction and improve readability.
continue | ||
} | ||
|
||
enabled = true |
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.
This is funny. Here we have 2 general programming principles at odds with each other namely default variables to negative case vs. direct boolean function calls. Nothing to change, but just a funny observation.
t = strings.TrimPrefix(t, "-") | ||
enabled = false | ||
} | ||
if !ret.IsValid(t) { |
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.
If IsValid
is only used to check if something does not exist, then you might consider renaming it to DoesNotExist
, and getting rid of this !MethodCall
. The not symbol before boolean functions hurts readability, and I've always been taught to avoid it if at all possible. However, sometimes it's not possible.
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.
Good point on readability -- will address in upcoming commit.
} | ||
|
||
// parseTargets parses a comma delimited target string into a targetMap | ||
func (c *OperatorDebugCommand) parseIntervalTargets(intervalTargets string) targetMap { |
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.
Same pointer observation on return val. Also, this appears to be virtually identical to the method above. I wonder if you couldn't extract a template method, and reduce duplication of logic.
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.
Good catch - was going to refactor these in a later PR but I'll push a commit shortly to take care of it now.
|
||
// Fetch data from vault | ||
if c.targetEnabled("vault") { | ||
vaultAddr := getVaultAddrFromSelf(self) |
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.
Does this need similar logic as the consul stuff in the preceding lines, or is the interface different?
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.
@DerekStrickland made many good points and I'm adding more :P. I'm giving some conflicting advice here - In places I suggested getting rid of functions, but made nit-picky comments about their content mostly for guiding style.
A question about whether we should track targets vs interval-targets separately and make users track the distinction.
serverIDs []string | ||
targets targetMap | ||
intervalTargets targetMap | ||
targetLock sync.Mutex |
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 don't believe you need this lock. targets
and intervalTargets
is set early on in Run()
and isn't modified afterwards, so it doesn't need a mutex.
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.
You are correct -- I was exploring options for enabling or disabling targets based on what is discovered during the capture (OSS vs Enterprise, what features are detected in the configuration, etc). I'll remove the lock and revisit if that functionality is added in the future.
// String returns a CSV of valid enabled targets | ||
func (t targetMap) String() string { |
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.
This struct and its methods live among OperatorDebugCommand
methods, breaking the flow of methods and making it a bit harder to follow. I'd suggest moving targets at the end of the file or so.
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.
Agreed -- good point
var targetString string | ||
|
||
for target, enabled := range t { | ||
if enabled && target != "all" { |
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.
Why not emit all
? If all
isn't supposed to be a key here, then this skip seems like papering over a bug and makes it harder to understand what's going on.
targetString = strings.Join(targets, ",") | ||
|
||
return targetString |
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.
Generally, I'd suggest avoiding declaring variables so far away from where they are used. Also, you probably can get away with avoiding declaring the variable completely:
targetString = strings.Join(targets, ",") | |
return targetString | |
return strings.Join(targets, ",") |
} | ||
|
||
// Enable or disable targets in map based on user input CSV | ||
var enabled bool |
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.
enabled
seems to be only used inside the loop, you don't need to declare it outside.
// targetMap generic map for targets | ||
type targetMap map[string]bool |
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.
The comment doesn't add more info than the type name IMO. Also, I wonder if it makes sense to drop Map
here; is calling it targets
ok here?
// targetMap generic map for targets | |
type targetMap map[string]bool | |
// targets is a wrapped set to capture enabled debugging targets | |
type targets map[string]bool |
targets = append(targets, target) | ||
} | ||
} | ||
sort.Strings(targets) |
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.
👍 on sorting for repeatability and consistency.
// Build starting target map | ||
switch { | ||
case targets == "": | ||
return ret |
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.
Should we return targetDefaultsNone()
here? If we return an empty uninitialized targetMap
, I fear that follow up calls with IsValid
will generate noisy invalid keys messages because the map is empty.
case targets == "": | ||
return ret | ||
case strings.Contains(targets, "none"): | ||
ret = targetDefaultsNone() |
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.
What should happen when a user passed none, allocations
? Should we generate an error? Should we just return targetDefaultsNone()
here as well without further processing?
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.
As a draft PR I hadn't fully explained the concept yet, but the idea here is that the target flags are additive / subtractive. If you pass no target flag at all, then the default behavior is the same as it was before and "all" targets are enabled. To clarify I will add a description to the PR and expand the documentation in a future commit.
To your question, none,allocations
should result in a map containing only allocations
. To verify and help self-document I will add a test to ensure that this is always the case.
As another example, I have an upcoming PR which adds an eventstream target that will be disabled by default. To add this target to a capture you would use -target=eventstream
. This results in a map containing all target defaults plus eventstream. Similar to your question, if you wanted only eventstream then you would use -target=none,eventstream
.
command/operator_debug.go
Outdated
-targets=<metrics,-metrics,...,all,none> | ||
Comma separated list of targets to capture. Defaults to "all". | ||
"-<target>" disables capture for that target. | ||
|
||
-interval-targets=<metrics,-metrics,...,all,none> | ||
Comma separated list of targets to capture during each interval. Defaults | ||
to "all". "-<target>" disables capture for that target. |
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.
Do we document which targets are intervals and which ones aren't? Would it make sense to just have a single -targets
argument without having users worry about the distinction?
@davemay99 this PR is over a year old at this point. Do we want to close this or would you like someone on the team to try to carry it for 1.4.x? |
No description provided.