-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
[service] Make graph
package public
#8111
Conversation
Points to discuss before merging this:
cc some people that have expressed interest on this @jaronoff97 @codeboten @ptodev @jpkrohling |
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #8111 +/- ##
==========================================
- Coverage 90.55% 90.25% -0.31%
==========================================
Files 301 301
Lines 15317 15550 +233
==========================================
+ Hits 13870 14034 +164
- Misses 1161 1227 +66
- Partials 286 289 +3
☔ View full report in Codecov by Sentry. |
I may be missing some context since I'm coming into this late, but I think this change is a good step forward. Please let me know if I've missed anything, but I did have a few thoughts on the discussion points you brought up.
Since the overall goal here is to make it possible to create pipelines outside of a service, I think it would make sense to break this out of the service package. Could we make a top-level
Could you link to the validation we could move? After looking through the PR and the code, everything looks like it's in the correct spot, but I could be missing something. The only other validation I found is in
I'm not in a good position to speak to how well this resolves the original issue, but in general I think it makes sense to provide the ability to create pipelines without having to involve all of the service machinery. |
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 general I would try to make this part of pipelines
and consistent with extensions
.
service/graph/graph.go
Outdated
ConnectorBuilder *connector.Builder | ||
|
||
// PipelineConfigs is a map of component.ID to PipelineConfig. | ||
PipelineConfigs pipelines.Config | ||
} | ||
|
||
// Graph of collector components. |
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.
Is "Graph" the right name? Just Pipelines
maybe in the pipelines
package?
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.
There are a few places where we still call this Pipelines
so I moved this to pipelines
and renamed to Pipelines
. @djaglowski what do you think?
…ines.Config into separate argument
@bogdandrutu any thoughts about this?
You are right, I mistakenly thought this would apply to the graph but it does not, so this question is solved. |
If I understand correctly, the proposal is to expose I can appreciate that the approach would allow anyone to write their own service, but are we sure it's a good idea? Doesn't this encourage fragmentation of an important high level package? When I look at all the information passed to // Passed directly through to graph.Build
Receivers *receiver.Builder
Processors *processor.Builder
Exporters *exporter.Builder
Connectors *connector.Builder
Pipelines pipelines.Config
// Built and managed by service package
Extensions *extension.Builder
Extensions extensions.Config
// Own telemetry related settings
Telemetry telemetry.Config
AsyncErrorChannel chan error
LoggingOptions []zap.Option
useOtel *bool
// Simple data struct used in several ways
BuildInfo component.BuildInfo Here are my observations & concerns then:
|
@djaglowski A version of this is what I originally tried last year, see #5107. The main concern AIUI was that one could ignore the I don't see a way to fulfill the following three goals at the same time
We could make the telemetry set up be independent of the service (although then it's unusual for the telemetry configuration to live under the |
@mx-psi, I am late to this area of the code but #5107 seems like the better approach.
Makes sense to me - a telemetry configuration that is independent of service would solve these problems:
and still allow a solution to this one:
Ultimately, I don't want to bog this down if others more experienced with this part of the code feel strongly but in my opinion this would be exposing the graph package for an unrelated use case. |
I agree with @djaglowski here, I think #5107 is a more straightforward approach that achieves the goal here. Similar to Dan, I don't have a ton of experience with this code so take my opinion with a grain of salt. I'd be interested as well, in how allowing the specifying a custom telemetry provider would look for custom implementations on top of the collector, would it have to be a new part of the builder? I'm a bit confused as to how someone would supply custom telemetry providers with #8111 's implementation, but I'm definitely misunderstanding something here and am going to spend some more time to clear that. |
The Grafana Agent has a similar need to integrate Collector's metrics with the Agent's. We currently do this by forking the Collector and customising it. I believe this PR would be sufficient for our needs so that we don't need a fork in order to integrate the metrics. Alternatively, the other proposal to "make the telemetry set up be independent of the service" would be sufficient for our needs as well. There is no issue on our end with using a Service, as long as we can integrate its telemetry with ours. |
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 am excited by the stated goal -- to support configuring telemetry for embedded scenarios.
It would be nice to see a little more explanation for how the telemetry.Settings
type will change after this PR to support our goals. That struct has a ZapOption
list. OTOH, the hook I'm looking for would allow me to supply the already-configured TracerProvider
, MeterProvider
, and (one day) LoggerProvider
. Will at least TracerProvider and MeterProvider become options in telemetry.Settings
as a future step?
func Build(ctx context.Context, set Settings) (*Graph, error) { | ||
pipelines := &Graph{ | ||
// New builds a collector pipeline set. | ||
func New(ctx context.Context, set Settings, cfg Config) (*Pipelines, error) { |
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 was mildly confused by the PR because the Config
type is already in the new package (and it's not a struct, like most Config types are in this code base). Then, there is now a Settings
alongside Config
and in the context of this PR it's no longer clear why we have both a settings and a config type. Can you explain?
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 no longer clear why we have both a settings and a config type
In the opentelemetry-collector codebase, Config
types specify configuration that would be set by the user in the Collector YAML configuration, while Settings
specifies configuration that would be set only in the context of using the Go API directly.
This change was applied because of @bogdandrutu's suggestion here #8111 (comment) to make it in line with the extensions.
it's not a struct, like most Config types are in this code base
I think the important point of Config
types is that they can be unmarshaled; I am not sure if there are other examples of configuration not being a struct but in my mind this is not a problem.
@mx-psi For the record, at Lightstep we have a similar scenario and would like to know how we'll solve the same sort of problem. I would like us to explore a solution that requires little extra configuration for the Collector: simply a boolean option to use the OTel global providers. The collector will get the global providers when this setting is selected, and that's the end of the story. We can set the providers from an extension without changing the collector build in this way. |
@mx-psi I was discussing the point above w/ a co-worker who made me think this point is not well understood. I am suggesting that the Collector needs only a boolean option This mechanism is built for exactly this, and I've used it in production. For example), this is an OTel Collector exporter used by an OTel-Go metrics SDK. The metrics SDK configures the exporter w/ the global provider, which is installed somewhere else. The OTel-Go global mechanism supports delegation so that registration of the globals can be deferred--and this is necessary in my example. In my example, the exporter being constructed will be used by the global provider after it is constructed. It gets the global provider during its constructor, and after the SDK is constructed (using it), the global is set to the new SDK and the exporter is able to emit metrics via itself. |
How about specifying a tracer/meter name? This way, |
I also think this would make more sense than using global providers. Otherwise, if someone wants to start more than one service in the same executable, and if global providers are used, then we wouldn't be able to treat the telemetry from the different services individually. |
Thanks all for the comments!
I think we need @bogdandrutu here if we want to go with #5107 (which sadly means waiting a bit since he has limited availability, but I don't think I can do justice to his arguments). As I said before, I think the main concern is the possibility of ignoring the telemetry configuration by a telemetry provider. An alternative would be to just pass the @djaglowski @jaronoff97 If you have any preference on #5107 vs specifying telemetry settings and processor directly and exposing the telemetry initialization logic separately, I would be interested on what you think is best between the two.
My goal with this PR is to allow specifying the telemetry provider arbitrarily when using the Go API directly. This PR does not cover using the builder for this and I personally don't use it for my use case; I guess if this is what you are interested in it makes more sense to me to expose this at a higher level like the service and so #5107 makes more sense. I would like to treat builder changes separately though and get the core service API changes in first.
@jmacd My feeling is that we have intentionally avoided using globals on the Collector for self-telemetry or any other feature (the only exception to my knowledge being the featuregate registry), and I don't see a good reason to use the global providers when we can directly pass the telemetry providers; passing the providers directly provides more flexibility and avoids the issues @ptodev mentions about treating telemetry differently for different services in a binary. I also don't think we can get a tracer/meter directly as @jpkrohling and @ptodev discuss; the whole Collector components API is based around the idea of passing providers, not the tracers/meters directly. |
I think i may be partial to Josh's recommendation here to use the global system. I'm not sure how we would handle telemetry provider registration otherwise. i.e. if I have an extension that is meant to set a custom meter and tracer provider, how would I ensure it's the first thing called so that anything after uses those providers? if its not the first thing called, then some components may be instantiated with a provider that will be later overridden. It's my understanding that the global system fixes that problem. I agree that separating these changes from the builder makes sense, so doing something that can make this change without needing to make a builder change is ideal. |
@jaronoff97 If you are talking about Collector extensions (like e.g. the Furthermore, all telemetry is based on using the providers that you get passed through the If by extensions you mean generally other code running alongside the Collector, then I don't see how passing the telemetry settings in a struct would make anything different: you would just call |
The advertised method for building a collector produces a main binary, and I am looking for an optional way to "pass the telemetry provider" initialized with the global values, precisely because I am configuring an extension that will provide the global that I want. When the |
@jmacd Sorry, I still don't understand what the benefit is of using globals. What can you do when using the global providers that you wouldn't be able to do if passing providers explicitly? Is it just a matter of ease of use for your use case, or is there something that breaks with a different solution? Also, re:extension, could you help clarify my questions in #8111 (comment)? If you are interested on changing the telemetry provider via a Collector extension there would need to be more pervasive changes on how telemetry providers are handled and on the Collector lifecycle more generally. |
I am not arguing against passing providers explicitly. If a user is not using the collector builder and is adopting the components directly, then they would of course pass in their providers. The question is whether the value that will be passed in comes from, when the collector is built by the builder. When I am using a collector builder and have generated a standard binary, it means configuration will be parsed to setup the providers. The configuration will turn into an OTel SDK being configured for traces/metrics/logs, which I expect to be some kind of standard configuration. One promise of OTel's APIs is that we are able to use alternative OTel SDKs. As a vendor, I have a large internal code base and every binary uses the same telemetry providers; they are OTel SDKs, but they are specially configured and I want to pass these SDKs in. I have an extension that is built into the collector that will be able to supply those providers when they are started. The delegation mechanism I am seeking to use is as follows:
And nothing about this plan prevents configuring telemetry providers on a component-by-component basis. |
I've been discussing Lightstep's use case with @jmacd in more detail offline. As I understand it, their solution needs:
While this PR would allow doing that, I agree with Dan's concern on #8111 (comment) that this would increase fragmentation. I want to reconsider the feasibility of telemetry providers to do this; I think it should be possible to do this but the codebase has changed a lot since I raised #5107 and needs some refactor before something like it is possible. It also does not easily fulfill goal (2), for which I think a similar approach to the one used for components configuration may be desirable. Therefore, I will be working on #8170 first, which I believe is a net improvement on its own, and retake this after it is completed. If you want to help reviewing this effort, #8171 is the first PR for this. |
My biggest concern here is that we provide an optional way to use the global mechanism that was designed for OTel-Go because it correctly handles dependency injection and out-of-order initialization, i.e., explicitly because it allows me to configure my telemetry providers after the services have started and bind them at that time. |
This PR was marked stale due to lack of activity. It will be closed in 14 days. |
Closed as inactive. Feel free to reopen if this PR is still being worked on. |
Will keep this closed for now, #8171 is the next step on the alternative to this, which is currently blocked by open-telemetry/opentelemetry-go-contrib#4228 |
Description:
Makes the
service/internal/graph
package public and documents its public methods.Link to tracking Issue: Fixes #4970