-
Notifications
You must be signed in to change notification settings - Fork 114
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 listeners to trace file metadata #571
Conversation
9bafbd8
to
05698d5
Compare
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.
Some design suggestion for more flexibility by consumer
Request changes on Project
: it should NOT own FileMetadata, leave use of parsing events to the consumer - here you want to count extends, but there's other scenario we want to list files being processed, or ... mode details TBD
types/project.go
Outdated
@@ -58,6 +58,18 @@ type Project struct { | |||
// DisabledServices track services which have been disable as profile is not active | |||
DisabledServices Services `yaml:"-" json:"-"` | |||
Profiles []string `yaml:"-" json:"-"` | |||
|
|||
// Metadata for telemetry | |||
FileMetada *FileMetada `yaml:"-" json:"-"` |
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 Listener
allows to track events during parsing, Project
should not need to host such a metadata attribute. Just let the consumer manage collecting metrics with it's own data structure
cli/options.go
Outdated
|
||
// Callbacks to retrieve metadata information during parse defined before | ||
// creating the project | ||
Listeners map[string][]loader.Listener |
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 makes it a bit complicated to write a listener for more than one event type. Could declare Listeners []Listener
as Listener
is a func(event string, details any
, it already receives the event type, and writing a listener then is just a question of a switch
and can select any interesting event and ignore all others.
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.
@ndeloof by writing the way you are suggesting, you are passing the map functionality inside every registered listener by writing in all further listeners with a switch. When in reality with the current solution we take advantage of the map data structure to handle this. Also, when processing 1 listener, with your recomendation, you will need to iterate over all listeners, when in reality you just care for a subset of them. I can see one advantage in having a []Listener
instead of map[string][]Listener
which is, in case you want to have a particular Listener that is triggered for all events
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 the other way: if I want to collect multiple events, with your design I'll have to register mutliple listeners for all the events I'm interested in, while with a generic one I have a native filtering capability using switch
:
func listener(event string ...) {
switch event {
case "extends":
countExtends++
case "include":
countInclude++
case "env_file":
countEnvFile++
}
Also, if you're interested by a single event type, we can offer a Filter:
cli.WithListener( cli.FilterEvent("extends", func(...)) )
by the way, Listener
doesn't need to be a slice, could be a single reference, and we can offer a composition helper
cli.WithListener( cli.CombineListeners(func(...), func(...), func(...)) )
tl;dr keep the original design simple/naive and offer helpers to give more flexibility to the consumer
loader/extends.go
Outdated
@@ -60,6 +60,8 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a | |||
if !ok { | |||
return s, nil | |||
} | |||
opts.ProcessEvent("extends", name) |
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.
in addition to service name, would be useful to track the file so we can detect all files involved in a compose model
Listener
's metadata parameter could be declared a map[string]any
so it is easy to pass more than just one attribute here, and add more as this evolves without impacting consumers
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 was a leftover from a test :) I intended to pass the extends
(the whole extends metadata from the service) I will change this. I left a any
in case for some reason in the future someone does not want to necessarily pass a map[string]any
. What do you think @ndeloof ?
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 benefit of a map[string]any
is that this allows to "add more" without impact on previously reported keys. Using any
the consumer would need to adjust to any change that's made to the original struct
loader/extends_test.go
Outdated
options.Listeners = map[string][]Listener{ | ||
"extends": {func(event string, metadata any) { | ||
extendsCount++ | ||
}}, |
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.
}}, | |
options.Listeners = []Listeners{ | |
func(event string, metadata map[string]any { | |
if event == "extends" { | |
count ++ | |
} | |
}}, |
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.
Agree with Nicolas' suggestions - I think a generic listener that gets the event type + metadata is fine, consumers can ignore what they don't care about. Other than that and last cleanup though, lgtm!
05698d5
to
a6e592d
Compare
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.
LGTM
cli/options.go
Outdated
func (o *ProjectOptions) WithListener(listeners []loader.Listener) { | ||
o.Listeners = listeners |
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.
nit: Better make this a appending function so it can be used multiple times
func (o *ProjectOptions) WithListener(listeners []loader.Listener) { | |
o.Listeners = listeners | |
func (o *ProjectOptions) WithListener(listener loader.Listener) { | |
o.Listeners = append(o.Listeners, listener) |
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.
a6e592d
to
0411918
Compare
Signed-off-by: jhrotko <[email protected]>
0411918
to
6e4fb2b
Compare
@@ -480,6 +491,13 @@ func withConvertWindowsPaths(options *ProjectOptions) func(*loader.Options) { | |||
} | |||
} | |||
|
|||
// save listeners from ProjectOptions (compose) to loader.Options | |||
func withListener(options *ProjectOptions) func(*loader.Options) { |
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 there could be more than one listener to save
func withListener(options *ProjectOptions) func(*loader.Options) { | |
func withListeners(options *ProjectOptions) func(*loader.Options) { |
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.
indeed. I plan to add include
metric support using this mechanism so will fix this at same time in a follow-up PR
Description
This PR intends to retrieve metadata from the parsing file. This was an issue when tracking the occurrences of
extends
in a project for telemetry since at the time the metrics are sent the Project is fully resolved thus no extends instances.This will be one out of three PRs.
docker/compose#11492
Interaction
Fix
by adding the listener to increment the number of processed extends, found a bug where when going down the search tree, we were not updating the leafs after the merge. In a case where we had chained extends, we would over look the tree, since we would only update the
services
at the head of the recursion inApplyExtends