diff --git a/samples/dapr/ServiceA/DaprServiceA.csproj b/samples/dapr/ServiceA/DaprServiceA.csproj index db4fe8dab7c..7bb2583264f 100644 --- a/samples/dapr/ServiceA/DaprServiceA.csproj +++ b/samples/dapr/ServiceA/DaprServiceA.csproj @@ -12,4 +12,8 @@ + + + + diff --git a/samples/dapr/ServiceA/Program.cs b/samples/dapr/ServiceA/Program.cs index b3fe47699f3..78d800cad97 100644 --- a/samples/dapr/ServiceA/Program.cs +++ b/samples/dapr/ServiceA/Program.cs @@ -5,6 +5,8 @@ var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); diff --git a/samples/dapr/ServiceB/DaprServiceB.csproj b/samples/dapr/ServiceB/DaprServiceB.csproj index db4fe8dab7c..7bb2583264f 100644 --- a/samples/dapr/ServiceB/DaprServiceB.csproj +++ b/samples/dapr/ServiceB/DaprServiceB.csproj @@ -12,4 +12,8 @@ + + + + diff --git a/samples/dapr/ServiceB/Program.cs b/samples/dapr/ServiceB/Program.cs index 43d9a74f39d..642612f1b82 100644 --- a/samples/dapr/ServiceB/Program.cs +++ b/samples/dapr/ServiceB/Program.cs @@ -3,6 +3,8 @@ var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); diff --git a/src/Aspire.Dashboard/Otlp/Model/OtlpApplication.cs b/src/Aspire.Dashboard/Otlp/Model/OtlpApplication.cs index 943c1bf6fb6..be34c0ad031 100644 --- a/src/Aspire.Dashboard/Otlp/Model/OtlpApplication.cs +++ b/src/Aspire.Dashboard/Otlp/Model/OtlpApplication.cs @@ -53,7 +53,11 @@ public OtlpApplication(Resource resource, IReadOnlyDictionary a.Value.ApplicationName == ApplicationName).Count(); _logger = logger; diff --git a/src/Aspire.Dashboard/Otlp/Model/OtlpHelpers.cs b/src/Aspire.Dashboard/Otlp/Model/OtlpHelpers.cs index dd6fc0bd51f..416405a44cc 100644 --- a/src/Aspire.Dashboard/Otlp/Model/OtlpHelpers.cs +++ b/src/Aspire.Dashboard/Otlp/Model/OtlpHelpers.cs @@ -16,6 +16,8 @@ public static class OtlpHelpers { public static string? GetServiceId(this Resource resource) { + string? serviceName = null; + for (var i = 0; i < resource.Attributes.Count; i++) { var attribute = resource.Attributes[i]; @@ -23,9 +25,17 @@ public static class OtlpHelpers { return attribute.Value.GetString(); } + if (attribute.Key == OtlpApplication.SERVICE_NAME) + { + serviceName = attribute.Value.GetString(); + } } - return null; + // + // NOTE: The service.instance.id value is a recommended attribute, but not required. + // See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/README.md#service-experimental + // + return serviceName; } public static string ToShortenedId(string id) => diff --git a/src/Aspire.Hosting.Dapr/DaprDistributedApplicationLifecycleHook.cs b/src/Aspire.Hosting.Dapr/DaprDistributedApplicationLifecycleHook.cs index 74cc34eee59..b0805ec646d 100644 --- a/src/Aspire.Hosting.Dapr/DaprDistributedApplicationLifecycleHook.cs +++ b/src/Aspire.Hosting.Dapr/DaprDistributedApplicationLifecycleHook.cs @@ -3,6 +3,7 @@ using Aspire.Hosting.ApplicationModel; using Aspire.Hosting.Lifecycle; +using Microsoft.Extensions.Configuration; using System.Globalization; using System.Net.Sockets; using static Aspire.Hosting.Dapr.CommandLineArgs; @@ -11,6 +12,7 @@ namespace Aspire.Hosting.Dapr; internal sealed class DaprDistributedApplicationLifecycleHook : IDistributedApplicationLifecycleHook { + private readonly IConfiguration _configuration; private readonly DaprOptions _options; private readonly DaprPortManager _portManager; @@ -20,9 +22,12 @@ internal sealed class DaprDistributedApplicationLifecycleHook : IDistributedAppl : Path.Combine("/usr", "local", "bin", "dapr"); private const int DaprHttpPortStartRange = 50001; + private const string DashboardOtlpUrlVariableName = "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL"; + private const string DashboardOtlpUrlDefaultValue = "http://localhost:18889"; - public DaprDistributedApplicationLifecycleHook(DaprOptions options, DaprPortManager portManager) + public DaprDistributedApplicationLifecycleHook(IConfiguration configuration, DaprOptions options, DaprPortManager portManager) { + _configuration = configuration; this._options = options; this._portManager = portManager; } @@ -126,6 +131,27 @@ public Task BeforeStartAsync(DistributedApplicationModel appModel, CancellationT component.Annotations.Add(new NameAnnotation { Name = sidecarOptions?.AppId ?? "Unknown" }); component.Annotations.AddRange(ports.Select(port => new ServiceBindingAnnotation(ProtocolType.Tcp, name: port.Key, port: port.Value.Port))); + // NOTE: Telemetry is enabled by default. + if (this._options.EnableTelemetry != false) + { + component.Annotations.Add( + new EnvironmentCallbackAnnotation( + env => + { + + // + // NOTE: Setting OTEL_EXPORTER_OTLP_ENDPOINT will not override any explicit OTLP configuration in a specified Dapr sidecar configuration file. + // The ambient Dapr sidecar configuration file does not configure an OTLP exporter (but could have been updated by the user to do so). + // + // TODO: It would be nice, at some point, to consolidate determination of the OTLP endpoint as it's now repeated in a few places. + // + + env["OTEL_EXPORTER_OTLP_ENDPOINT"] = this._configuration[DashboardOtlpUrlVariableName] ?? DashboardOtlpUrlDefaultValue; + env["OTEL_EXPORTER_OTLP_INSECURE"] = "true"; + env["OTEL_EXPORTER_OTLP_PROTOCOL"] = "grpc"; + })); + } + component.Annotations.Add( new ExecutableArgsCallbackAnnotation( updatedArgs => diff --git a/src/Aspire.Hosting.Dapr/DaprOptions.cs b/src/Aspire.Hosting.Dapr/DaprOptions.cs index b4f871c35af..e1726471991 100644 --- a/src/Aspire.Hosting.Dapr/DaprOptions.cs +++ b/src/Aspire.Hosting.Dapr/DaprOptions.cs @@ -12,4 +12,12 @@ public sealed record DaprOptions /// Gets or sets the path to the Dapr CLI. /// public string? DaprPath { get; init; } + + /// + /// Gets or sets whether Dapr sidecars export telemetry to the Aspire dashboard. + /// + /// + /// Telemetry is enabled by default. + /// + public bool? EnableTelemetry { get; init; } }