Sample Application
Sample Application
Section titled “Sample Application”The .NET Aspire sample demonstrates Activities, Logs, and Metrics generation working together with the Aspire Dashboard.
Solution layout
Section titled “Solution layout”| Project | Purpose |
|---|---|
SampleApp.AppHost | The Aspire orchestrator. |
SampleApp.APIService | Backend service exposing the telemetry interfaces and the generated TelemetryNames registration. |
SampleApp.APIService.UnitTests | TUnit + NSubstitute unit tests over the generated interfaces. |
SampleApp.Shared | Shared DTOs (e.g. WeatherForecast) plus IWeatherForecastTelemetry — a library-owned ActivitySource and Meter, aggregated by each host. |
SampleApp.Web | Frontend client that also generates its own telemetry. |
SampleApp.ServiceDefaults | Aspire service defaults; registers the generated meter/activity source names. |
The sample targets net10.0 and enables EmitCompilerGeneratedFiles, so generated output can be inspected under:
obj/<Configuration>/net10.0/generated/Purview.Telemetry.SourceGenerator/Purview.Telemetry.SourceGenerator.TelemetrySourceGenerator/Telemetry interfaces
Section titled “Telemetry interfaces”IEntityStoreTelemetry (APIService)
Section titled “IEntityStoreTelemetry (APIService)”The multi-target interface from the README — one method emits an Activity, an Info log, and an AutoCounter simultaneously:
[ActivitySource][Logger][Meter]interface IEntityStoreTelemetry{ [Activity] [Info] [AutoCounter] Activity? GettingEntityFromStore(int entityId, [Baggage] string serviceUrl);
[Event] [Trace] void GetDuration(Activity? activity, int durationInMS);
[Context] void RetrievedEntity(Activity? activity, float totalValue, int lastUpdatedByUserId);
[Warning] void EntityNotFound(int entityId);
[Histogram] void RecordEntitySize(int sizeInBytes);}IWeatherServiceTelemetry (APIService)
Section titled “IWeatherServiceTelemetry (APIService)”Demonstrates single-method multi-targeting, Activity status codes, and enumerable expansion:
[ActivitySource][Logger][Meter]public interface IWeatherServiceTelemetry{ [Activity(ActivityKind.Client)] [Trace] Activity? GettingWeatherForecast([Baggage] string someRandomBaggageInfo, int requestedCount);
[Event] void ForecastReceived(Activity? activity, int minTempInC, int maxTempInC);
[Event(ActivityStatusCode.Error)] void FailedToRetrieveForecast(Activity? activity, Exception exception);
[Event(ActivityStatusCode.Ok)] void TemperaturesReceived(Activity? activity, TimeSpan elapsed);
[AutoCounter] [Warning] [Event] void ItsTooCold(Activity? activity, int minTempInC, int tooColdCount);
[Histogram] void HistogramOfTemperature(int temperature);
[Error] [AutoCounter] void RequestedCountIsOutOfRange(int requestCount);
[Info] void TemperaturesWithinRange([ExpandEnumerable(maximumValueCount: 100)] int[] temperaturesInC);}IWeatherAPIClientTelemetry (Web)
Section titled “IWeatherAPIClientTelemetry (Web)”Demonstrates [ExcludeTargets], an instrument prefix, and HttpStatusCode tags:
[ActivitySource][Logger][Meter(InstrumentPrefix = "weather")]public interface IWeatherAPIClientTelemetry{ [Activity(ActivityKind.Client)] [Info] [AutoCounter] Activity? GetWeatherForecasts(int? count);
[Event] [Error] [AutoCounter] void FailedToGetForecast(Activity? activity, Exception ex, [ExcludeTargets(Targets.Activities)] int? count);
[Event] void RequestComplete(Activity? activity, HttpStatusCode statusCode, bool isSuccessStatusCode);
[AutoCounter] void RequestSuccess();
[Event] [Warning] void NoForecastsRecieved(Activity? activity);
[Event(ActivityStatusCode.Ok)] [Debug] void ForecastsRecieved( Activity? activity, int forecastCount, [ExpandEnumerable(100), ExcludeTargets(Targets.Activities)] WeatherForecast[] weatherForecasts );}Registering names with Aspire
Section titled “Registering names with Aspire”SampleApp.ServiceDefaults is referenced by the projects that own the telemetry, so it cannot discover their names itself. Instead, each host project opts into the generated TelemetryNames class and passes the arrays in. In SampleApp.APIService/Properties/AssemblyInfo.cs:
[assembly: TelemetryGeneration(GenerateTelemetryNamesClass = true)]builder.AddServiceDefaults(TelemetryNames.MeterNames, TelemetryNames.ActivitySourceNames);The class aggregates the host’s own names with the names recorded by every assembly it references. SampleApp.Shared ships its own IWeatherForecastTelemetry (an ActivitySource and a Meter, both named after that assembly), consumed by its WeatherForecastValidator which WeatherService calls. The library registers its services with builder.Services.AddWeatherForecastValidation() but never registers its names with OpenTelemetry — those arrive through the project reference:
public static readonly string[] MeterNames = new string[] { "SampleApp.APIService", "SampleApp.Shared" };
public static readonly string[] ActivitySourceNames = new string[] { "SampleApp.APIService", "SampleApp.Shared" };SampleApp.Web references SampleApp.Shared for the DTOs only, so it aggregates the same names without emitting from them — the aggregate follows the reference graph, not usage, and registering a source that stays quiet costs nothing.
See Generation.
Multi-project sample
Section titled “Multi-project sample”samples/SampleApp.MultiProject is a smaller console solution dedicated to this aggregation, for solutions with many projects and differing telemetry requirements. Four assemblies own telemetry — two of them reachable from the host only transitively — and a ServiceDefaults-style project that every one of them references receives the complete set of names:
just run-mpSee its README for the reference graph and the generated output.
Unit testing
Section titled “Unit testing”SampleApp.APIService.UnitTests uses TUnit with NSubstitute to verify WeatherService behaviour against the generated interfaces without emitting real telemetry. See Testing for the pattern.
Running
Section titled “Running”just build-sjust test-sWith dotnet run --project samples/SampleApp/SampleApp.AppHost, the Aspire Dashboard shows the generated Activities, Logs, and Metrics in real time.
See also
Section titled “See also”- Getting Started
- Multi-Targeting
- Generated Output — real generated code from this sample