-
Notifications
You must be signed in to change notification settings - Fork 110
Add OpenTelemetry Collector extension/component #603
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
base: main
Are you sure you want to change the base?
Conversation
@dotnet-policy-service agree |
I'm not sure how ready this is yet. I've been playing with the API a bit to give it enough flexibility around the certificates, images and config files. Feedback very welcome, I wanted to get this up as is before I added any tests and worked on the documentation, 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.
Would it make sense that, rather than having a default collector image set, that it's a required property to be provided and there's a method that will set the defaults for you?
So, AddOpenTelemetryCollector
is used if you want to specify a collector, whereas AddOpenTelemetryDefaultCollection
uses the one that you have currently specified as a default.
<PropertyGroup> | ||
<TargetFramework>net9.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
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.
<PropertyGroup> | |
<TargetFramework>net9.0</TargetFramework> | |
<ImplicitUsings>enable</ImplicitUsings> | |
<Nullable>enable</Nullable> | |
</PropertyGroup> |
This will all be provided by the props files above.
public EnvironmentVariableHook(ILogger<EnvironmentVariableHook> logger) | ||
{ | ||
_logger = logger; | ||
} |
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.
Use a primary constructor
var resources = appModel.GetProjectResources(); | ||
var collectorResource = appModel.Resources.OfType<CollectorResource>().FirstOrDefault(); | ||
|
||
if (collectorResource == null) |
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 (collectorResource == null) | |
if (collectorResource is null) |
return Task.CompletedTask; | ||
} | ||
|
||
if (resources.Count() == 0) |
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 (resources.Count() == 0) | |
if (!resources.Any()) |
foreach (var resourceItem in resources) | ||
{ | ||
_logger.LogDebug($"Forwarding Telemetry for {resourceItem.Name} to the collector"); | ||
if (resourceItem == null) continue; |
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 (resourceItem == null) continue; | |
if (resourceItem is null) continue; |
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.
File no longer needed
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.
File no longer needed
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.
/// <summary> | ||
/// The version of the collector, defaults to latest | ||
/// </summary> | ||
public string CollectorVersion { get; set; } = "latest"; |
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.
We should pin to a version so it's known stable per the package version
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 convinced that pinning is the right approach for that, especially since there isn't a v1 yet. It's generally accepted that people don't pin even in production.
/// <summary> | ||
/// The image of the collector, defaults to ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib | ||
/// </summary> | ||
public string CollectorImage { get; set; } = "ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib"; |
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 feel like this should be split to registry/image like we do with other container resources. And is there a reason you're using ghcr.io not Docker Hub?
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.
ghcr is the recommended default since the limits went into dockerhub.
Do you have an example of the pattern other integrations use for the registry split.
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.
Aspire/src/CommunityToolkit.Aspire.Hosting.Ollama/OllamaContainerImageTags.cs
Lines 5 to 6 in 0c0afa1
public const string Registry = "docker.io"; | |
public const string Image = "ollama/ollama"; |
Interesting approach, I'm honestly not sure, but maybe a I was thinking that the approach I showed was more common, but happy if not. I kinda wanted a minimal default command though. |
The problem with a method like |
I agree it's not ideal, but I'd prefer to not have them need to know what the image link is to collector. So I believe the best option is to use the default image we recommend in Otel right now (which is |
7ac9a9d
to
a38c078
Compare
I'm coming back at this now @aaronpowell, with a hope to move it forward so I don't need to maintain a separate one (happy to be codeowner here). On the devcerts, it seems those issues haven't got a resolution, would it be ok to leave the devcerts in this project for now, and consolidate/move later? This project would actually fix both of those issues as the users could run the non-ssl collector and forward their data through the collector as a way to unblock them. The big outstanding issue is the collector image model, can you let me know what you'd suggest there. It should be |
If you can pop it in the
What's the release frequency that they are going with? The main reason that we pin to a version is so that we don't risk any unexpected changes to command line args or other behaviour getting introduced that impact someone who hasn't opt-ed in to using a newer version. If they are releasing new versions daily, then pinned isn't ideal, but generally speaking, pinning a version is going to be preferred, and people can always specify a version override if we don't release an updated package quick enough (Ollama is a good example of where we get caught on that front). |
The release cycle for the collector is every 2 weeks. The compatibility issues wouldn't be commandline args, its more likely the config of collector components that might change, but we haven't done a breaking change in about 6 months and those are expected to be the last. The biggest issue with pinning a version is security issues, since the amount of components in the collector means that the updates contain those updates regularly. I'll happily take guidance on what the best way to get this published is. |
0ae0e03
to
7c26b63
Compare
I've done a few major updates, so it probably needs a full re-review.
This does require the user to implement |
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.
Pull Request Overview
This PR adds an OpenTelemetry Collector integration to the .NET Aspire Community Toolkit, enabling users to add an OpenTelemetry Collector container to their Aspire applications for observability data collection and forwarding.
- Introduces a new hosting integration with configurable settings for the OpenTelemetry Collector
- Provides automatic HTTPS certificate handling for secure connections in development mode
- Implements environment variable hooks to automatically configure project resources to send telemetry data to the collector
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
File | Description |
---|---|
src/Shared/DevCertHostingExtensions.cs | Shared utility for injecting ASP.NET Core HTTPS dev certificates into resources via environment variables |
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/OpenTelemetryCollectorSettings.cs | Configuration settings class for the OpenTelemetry Collector |
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/EnvironmentVariableHook.cs | Lifecycle hook to automatically configure project resources with collector endpoints |
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector.csproj | Project file for the OpenTelemetry Collector integration |
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/CollectorResource.cs | Resource definition for the OpenTelemetry Collector container |
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/CollectorExtensions.cs | Extension methods for adding and configuring the OpenTelemetry Collector |
CommunityToolkit.Aspire.slnx | Solution file updated to include the new project |
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/EnvironmentVariableHook.cs
Outdated
Show resolved
Hide resolved
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/EnvironmentVariableHook.cs
Outdated
Show resolved
Hide resolved
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/CollectorExtensions.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
/// This method <strong>does not</strong> configure an HTTPS endpoint on the resource. | ||
/// Use <see cref="ResourceBuilderExtensions.WithHttpsEndpoint{TResource}"/> to configure an HTTPS endpoint. | ||
/// </remarks> | ||
public static IResourceBuilder<TResource> RunWithHttpsDevCertificate<TResource>( |
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.
We don't ship this in any of the intergrations as yet and I think we'd rewrite it at this point to behave a bit better given the advances in the model.
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 there something "wrong" in the design of how this has been implemented that should be revised? Having a way to bootstrap the dev certificate into a container has been a pain point with a few integrations and having them working with OTEL (DAB and Go Feature Flags off the top of my head).
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 can self-contain this inside of the Collector to reduce the impact? This was code taken from an example built by the Aspire team with a few amendments to allow some resilience.
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 internal
, so it'll only be available in the assemblies it's packaged with, and I want to use it for others, so I'm find with where it is situated.
I'm more questioning @davidfowl's comment, as I'm not sure what he's looking for or suggesting.
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 main change here would be to use the event callbacks of 9.4.
Then would you be able to introduce an example project so I can get a better idea of how this works.
/// <returns></returns> | ||
public static IResourceBuilder<CollectorResource> WithAppForwarding(this IResourceBuilder<CollectorResource> builder) | ||
{ | ||
builder.ApplicationBuilder.Services.TryAddLifecycleHook<EnvironmentVariableHook>(); |
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.
Lifecycle hooks are mostly considered legacy with the event system now in place. The hook should be replacable with the OnAfterEndpointsAllocated
callback.
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 trying to gather from the documentation whether the OnAfterEndpointsAllocated
is resource specific, or part of the wider DistributedAppModel. My concern being that this needs to be applied after the Aspire code has added the dashboard environment variables.
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/EnvironmentVariableHook.cs
Outdated
Show resolved
Hide resolved
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/EnvironmentVariableHook.cs
Outdated
Show resolved
Hide resolved
/// <summary> | ||
/// The image of the collector, defaults to ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib | ||
/// </summary> | ||
public string CollectorImage { get; set; } = "ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib"; |
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.
Aspire/src/CommunityToolkit.Aspire.Hosting.Ollama/OllamaContainerImageTags.cs
Lines 5 to 6 in 0c0afa1
public const string Registry = "docker.io"; | |
public const string Image = "ollama/ollama"; |
/// This method <strong>does not</strong> configure an HTTPS endpoint on the resource. | ||
/// Use <see cref="ResourceBuilderExtensions.WithHttpsEndpoint{TResource}"/> to configure an HTTPS endpoint. | ||
/// </remarks> | ||
public static IResourceBuilder<TResource> RunWithHttpsDevCertificate<TResource>( |
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 there something "wrong" in the design of how this has been implemented that should be revised? Having a way to bootstrap the dev certificate into a container has been a pain point with a few integrations and having them working with OTEL (DAB and Go Feature Flags off the top of my head).
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/CollectorExtensions.cs
Outdated
Show resolved
Hide resolved
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/CollectorResource.cs
Outdated
Show resolved
Hide resolved
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/CollectorResource.cs
Outdated
Show resolved
Hide resolved
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/CollectorResource.cs
Outdated
Show resolved
Hide resolved
src/CommunityToolkit.Aspire.Hosting.OpenTelemetryCollector/EnvironmentVariableHook.cs
Show resolved
Hide resolved
I've done everything suggested other than moving the EnvironmentVariableHook. Since this needs to occur after all the resources had endpoints allocated, I'm struggling to see how I might achieve that. I did have a thought of using OnAfterEndpointsAllocatedEvent, but that's deprecated, and then thought of using |
Yes, sorry, I was mistaken, Is the logic that you'd have multiple collectors registered, or is it a singleton sort of thing? |
The collector is a service that intercepts the OTLP telemetry from all the other services, so it intercepts all the other project resources and overrides the OTLP environment variable to point it to the collector instead. For that reason, I thought that using the resource focused events wasn't something I could do. This is instead of telling the user to access every resource and reference it to the collector. |
**Closes #602 **
PR Checklist
Other information