diff --git a/.github/workflows/release-udp-exporter.yml b/.github/workflows/release-udp-exporter.yml
new file mode 100644
index 00000000..558e95b8
--- /dev/null
+++ b/.github/workflows/release-udp-exporter.yml
@@ -0,0 +1,85 @@
+name: Release ADOT OTLP UDP Exporter
+
+on:
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'Version number for deployment e.g. 0.1.0'
+ required: true
+ type: string
+
+jobs:
+ build-test-publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Set up .NET CLI
+ uses: actions/setup-dotnet@v2
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Download and run X-Ray Daemon
+ run: |
+ mkdir xray-daemon
+ cd xray-daemon
+ wget https://s3.us-west-2.amazonaws.com/aws-xray-assets.us-west-2/xray-daemon/aws-xray-daemon-linux-3.x.zip
+ unzip aws-xray-daemon-linux-3.x.zip
+ ./xray -o -n us-west-2 -f ./daemon-logs.log --log-level debug &
+
+ - name: Create NuGet.Config with multiple sources
+ working-directory: sample-applications/udp-exporter-test-app
+ run: |
+ cat > NuGet.Config << EOF
+
+
+
+
+
+
+
+
+ EOF
+
+ # Show the created config
+ cat NuGet.Config
+
+
+ - name: Build & Package the UDP exporter locally
+ working-directory: exporters/AWS.OpenTelemetry.Exporter.Otlp.Udp
+ run: |
+ dotnet pack -c Release
+
+ - name: Run Sample App in Background
+ working-directory: sample-applications/udp-exporter-test-app
+ run: |
+ # Install the locally built version of the UDP exporter
+ dotnet add package AWS.OpenTelemetry.Exporter.Otlp.Udp
+ # Start validation app
+ dotnet run &
+ # Wait for validation app to initialize
+ sleep 5
+
+ - name: Call Sample App Endpoint
+ run: |
+ curl localhost:8080/test
+
+ - name: Verify X-Ray daemon received traces
+ run: |
+ sleep 10
+ echo "X-Ray daemon logs:"
+ cat xray-daemon/daemon-logs.log
+
+ # Check if the daemon received and processed some data
+ if grep -q "sending.*batch" xray-daemon/daemon-logs.log; then
+ echo "✅ X-Ray daemon processed trace data (AWS upload errors are expected)"
+ exit 0
+ elif grep -q "processor:.*segment" xray-daemon/daemon-logs.log; then
+ echo "✅ X-Ray daemon processed segment data (AWS upload errors are expected)"
+ exit 0
+ else
+ echo "❌ No evidence of traces being received by X-Ray daemon"
+ exit 1
+ fi
+
+ # TODO: Steps to publish to NuGet
diff --git a/sample-applications/udp-exporter-test-app/Controllers/AppController.cs b/sample-applications/udp-exporter-test-app/Controllers/AppController.cs
new file mode 100644
index 00000000..dcb22376
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/Controllers/AppController.cs
@@ -0,0 +1,122 @@
+using System;
+using Amazon.S3;
+using Microsoft.AspNetCore.Mvc;
+using System.Diagnostics;
+using System.Net.Http;
+using Microsoft.AspNetCore.Http.Extensions;
+using OpenTelemetry;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Instrumentation;
+using System.Diagnostics.Metrics;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+
+namespace dotnet_sample_app.Controllers
+{
+ [ApiController]
+ [Route("[controller]")]
+ public class AppController : ControllerBase
+ {
+ private readonly AmazonS3Client s3Client = new AmazonS3Client();
+ private readonly HttpClient httpClient = new HttpClient();
+ private static Random rand = new Random(DateTime.Now.Millisecond);
+
+ public static readonly ActivitySource tracer = new(
+ "dotnet-sample-app");
+
+ public AppController() {}
+
+ [HttpGet]
+ [Route("/outgoing-http-call")]
+ public string OutgoingHttp()
+ {
+ using var activity = tracer.StartActivity("outgoing-http-call");
+ activity?.SetTag("language", "dotnet");
+ activity?.SetTag("signal", "trace");
+
+ var res = httpClient.GetAsync("https://aws.amazon.com/").Result;
+ string statusCode = res.StatusCode.ToString();
+
+ // Request Based Metrics
+ Startup.metricEmitter.emitReturnTimeMetric(MimicLatency());
+ int loadSize = MimicPayLoadSize();
+ Startup.metricEmitter.apiRequestSentMetric();
+ Startup.metricEmitter.updateTotalBytesSentMetric(loadSize);
+
+ return GetTraceId();
+ }
+
+ [HttpGet]
+ [Route("/test")]
+ public string AWSSDKCall()
+ {
+ using var activity = tracer.StartActivity("test_parent_span");
+ activity?.SetTag("language", "dotnet");
+ activity?.SetTag("signal", "trace");
+ activity?.SetTag("test.attribute", "test_value");
+
+ string traceId = activity?.TraceId.ToString() ?? "no-trace-available";
+
+ return traceId;
+ }
+
+ [HttpGet]
+ [Route("/")]
+ public string Default()
+ {
+ return "Application started!";
+ }
+
+ [HttpGet]
+ [Route("/outgoing-sampleapp")]
+ public string OutgoingSampleApp()
+ {
+ using var activity = tracer.StartActivity("outgoing-sampleapp");
+ activity?.SetTag("language", "dotnet");
+ activity?.SetTag("signal", "trace");
+ string statusCode = "";
+
+ if (Program.cfg.SampleAppPorts.Length == 0) {
+ var res = httpClient.GetAsync("https://aws.amazon.com/").Result;
+ statusCode = res.StatusCode.ToString();
+ }
+ else {
+ foreach (string port in Program.cfg.SampleAppPorts) {
+ if (!String.IsNullOrEmpty(port)) {
+ string uri = string.Format("http://127.0.0.1:{0}/outgoing-sampleapp", port);
+ var res = httpClient.GetAsync(uri).Result;
+ statusCode = res.StatusCode.ToString();
+ }
+ }
+ }
+
+ // Request Based Metrics
+ Startup.metricEmitter.emitReturnTimeMetric(MimicLatency());
+ int loadSize = MimicPayLoadSize();
+ Startup.metricEmitter.apiRequestSentMetric();
+ Startup.metricEmitter.updateTotalBytesSentMetric(loadSize);
+
+ return GetTraceId();
+ }
+
+ private string GetTraceId()
+ {
+ var traceId = Activity.Current.TraceId.ToHexString();
+ var version = "1";
+ var epoch = traceId.Substring(0, 8);
+ var random = traceId.Substring(8);
+ return "{" + "\"traceId\"" + ": " + "\"" + version + "-" + epoch + "-" + random + "\"" + "}";
+ }
+
+ private static int MimicPayLoadSize()
+ {
+ return rand.Next(101);
+ }
+
+ private static int MimicLatency()
+ {
+ return rand.Next(100,500);
+ }
+ }
+
+}
diff --git a/sample-applications/udp-exporter-test-app/Controllers/Config.cs b/sample-applications/udp-exporter-test-app/Controllers/Config.cs
new file mode 100644
index 00000000..0903c6c6
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/Controllers/Config.cs
@@ -0,0 +1,45 @@
+using System.IO;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
+
+namespace dotnet_sample_app.Controllers
+{
+ public class Config
+ {
+ public string Host;
+ public string Port;
+ public int TimeInterval;
+ public int RandomTimeAliveIncrementer;
+ public int RandomTotalHeapSizeUpperBound;
+ public int RandomThreadsActiveUpperBound;
+ public int RandomCpuUsageUpperBound;
+ public string[] SampleAppPorts;
+
+ public Config() {
+ this.Host = "0.0.0.0";
+ this.Port = "8080";
+ this.TimeInterval = 1;
+ this.RandomTimeAliveIncrementer = 1;
+ this.RandomTotalHeapSizeUpperBound = 100;
+ this.RandomThreadsActiveUpperBound = 10;
+ this.RandomCpuUsageUpperBound = 100;
+ this.SampleAppPorts = new string[0];
+ }
+
+ public static Config ReadInFile(string file) {
+ var deserializer = new DeserializerBuilder()
+ .WithNamingConvention(PascalCaseNamingConvention.Instance)
+ .Build();
+
+ Config returnConfig = null;
+ try {
+ returnConfig = deserializer.Deserialize(File.ReadAllText(file));
+ }
+ catch {
+ returnConfig = new Config();
+ }
+ return returnConfig;
+
+ }
+ }
+}
diff --git a/sample-applications/udp-exporter-test-app/Controllers/MetricEmitter.cs b/sample-applications/udp-exporter-test-app/Controllers/MetricEmitter.cs
new file mode 100644
index 00000000..c8569754
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/Controllers/MetricEmitter.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using OpenTelemetry;
+using System.Diagnostics.Metrics;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Instrumentation;
+
+namespace dotnet_sample_app.Controllers
+{
+ public class MetricEmitter
+ {
+ const string DIMENSION_API_NAME = "apiName";
+ const string DIMENSION_STATUS_CODE = "statusCode";
+
+ static string API_COUNTER_METRIC = "total_api_requests";
+ static string API_LATENCY_METRIC = "latency_time";
+ static string API_SUM_METRIC = "total_bytes_sent";
+ static string API_TOTAL_TIME_METRIC = "time_alive";
+ static string API_TOTAL_HEAP_SIZE = "total_heap_size";
+ static string API_TOTAL_THREAD_SIZE = "threads_active";
+ static string API_CPU_USAGE = "cpu_usage";
+
+ public Histogram apiLatencyRecorder;
+ public Counter totalTimeSentObserver;
+ public ObservableUpDownCounter totalHeapSizeObserver;
+ public UpDownCounter totalThreadsObserver;
+
+ private long apiRequestSent = 0;
+ private long totalBytesSent = 0;
+ private long totalHeapSize = 0;
+ private int cpuUsage = 0;
+ private int totalTime = 1;
+ private int totalThreads = 0;
+ private bool threadsBool = true;
+ private int returnTime = 100;
+
+ private static Random rand = new Random(DateTime.Now.Millisecond);
+
+ private KeyValuePair[] requestAttributes = new KeyValuePair[] {
+ new KeyValuePair("signal", "metric"),
+ new KeyValuePair("language", "dotnet"),
+ new KeyValuePair("metricType", "request")};
+
+ private KeyValuePair[] randomAttributes = new KeyValuePair[] {
+ new KeyValuePair("signal", "metric"),
+ new KeyValuePair("language", "dotnet"),
+ new KeyValuePair("metricType", "random")};
+
+ public MetricEmitter()
+ {
+ Meter meter = new Meter("adot", "1.0");
+
+ string latencyMetricName = API_LATENCY_METRIC;
+ string totalApiRequestSent = API_COUNTER_METRIC;
+ string totalApiBytesSentMetricName = API_SUM_METRIC;
+ string totaltimealiveMetricName = API_TOTAL_TIME_METRIC;
+ string totalHeapSizeMetricName = API_TOTAL_HEAP_SIZE;
+ string totalThreadsMetricName = API_TOTAL_THREAD_SIZE;
+ string cpuUsageMetricName = API_CPU_USAGE;
+
+ string instanceId = Environment.GetEnvironmentVariable("INSTANCE_ID");
+ if (instanceId != null && !instanceId.Trim().Equals(""))
+ {
+ latencyMetricName = API_LATENCY_METRIC + "_" + instanceId;
+ totalApiRequestSent = API_COUNTER_METRIC + "_" + instanceId;
+ totalApiBytesSentMetricName = API_SUM_METRIC + "_" + instanceId;
+ totaltimealiveMetricName = API_TOTAL_TIME_METRIC + "_" + instanceId;
+ totalHeapSizeMetricName = API_TOTAL_HEAP_SIZE + "_" + instanceId;
+ totalThreadsMetricName = API_TOTAL_THREAD_SIZE + "_" + instanceId;
+ cpuUsageMetricName = API_CPU_USAGE + "_" + instanceId;
+
+ }
+
+
+ meter.CreateObservableCounter(totalApiRequestSent,() => {
+ return new Measurement(apiRequestSent, requestAttributes);
+ },
+ "1",
+ "Increments by one every time a sampleapp endpoint is used");
+
+ meter.CreateObservableCounter(totalApiBytesSentMetricName, () => {
+ return new Measurement(totalBytesSent, requestAttributes);
+ },
+ "By",
+ "Keeps a sum of the total amount of bytes sent while the application is alive");
+
+ meter.CreateObservableGauge(cpuUsageMetricName, () => {
+ return new Measurement(cpuUsage, randomAttributes);
+ },
+ "1",
+ "Cpu usage percent");
+
+ meter.CreateObservableUpDownCounter(totalHeapSizeMetricName, () => {
+ return new Measurement(totalHeapSize, randomAttributes);
+ },
+ "1",
+ "The current total heap size”");
+
+ apiLatencyRecorder = meter.CreateHistogram(latencyMetricName,
+ "ms",
+ "Measures latency time in buckets of 100 300 and 500");
+
+ totalThreadsObserver = meter.CreateUpDownCounter(totalThreadsMetricName,
+ "1",
+ "The total number of threads active”");
+
+ totalTimeSentObserver = meter.CreateCounter(totaltimealiveMetricName,
+ "ms",
+ "Measures the total time the application has been alive");
+
+
+ totalTimeSentObserver.Add(totalTime, randomAttributes);
+ totalThreadsObserver.Add(totalThreads++, randomAttributes);
+ apiLatencyRecorder.Record(returnTime, requestAttributes);
+ }
+
+ public void emitReturnTimeMetric(int returnTime) {
+ apiLatencyRecorder.Record(
+ returnTime, requestAttributes);
+ }
+
+ public void apiRequestSentMetric() {
+ this.apiRequestSent += 1;
+ Console.WriteLine("apiBs: "+ this.apiRequestSent);
+ }
+
+ public void updateTotalBytesSentMetric(int bytes) {
+ totalBytesSent += bytes;
+ Console.WriteLine("Total amount of bytes sent while the application is alive:"+ totalBytesSent);
+ }
+
+ public void updateTotalHeapSizeMetric() {
+ this.totalHeapSize += rand.Next(0,1) * Program.cfg.RandomTotalHeapSizeUpperBound;
+ }
+
+ public void updateTotalThreadSizeMetric() {
+ if (threadsBool) {
+ if (totalThreads < Program.cfg.RandomThreadsActiveUpperBound) {
+ totalThreadsObserver.Add(1, randomAttributes);
+ totalThreads += 1;
+ }
+ else {
+ threadsBool = false;
+ totalThreads -= 1;
+ }
+ }
+ else {
+ if (totalThreads > 0) {
+ totalThreadsObserver.Add(-1, randomAttributes);
+ totalThreads -= 1;
+ }
+ else {
+ threadsBool = true;
+ totalThreads += 1;
+ }
+ }
+ }
+
+ public void updateCpuUsageMetric() {
+ this.cpuUsage = rand.Next(0,1) * Program.cfg.RandomCpuUsageUpperBound;
+ }
+
+ public void updateTotalTimeMetric() {
+ totalTimeSentObserver.Add(Program.cfg.RandomTimeAliveIncrementer, randomAttributes);
+ }
+
+ public async Task UpdateRandomMetrics(CancellationToken cancellationToken = default) {
+ void update() {
+ updateTotalTimeMetric();
+ updateTotalHeapSizeMetric();
+ updateTotalThreadSizeMetric();
+ updateCpuUsageMetric();
+ }
+
+ while (true) {
+ var delayTask = Task.Delay(Program.cfg.TimeInterval * 1000, cancellationToken);
+ await Task.Run(() => update());
+ await delayTask;
+ }
+ }
+
+ }
+}
diff --git a/sample-applications/udp-exporter-test-app/Dockerfile b/sample-applications/udp-exporter-test-app/Dockerfile
new file mode 100644
index 00000000..3627e713
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/Dockerfile
@@ -0,0 +1,13 @@
+FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env
+WORKDIR /app
+COPY . ./
+RUN dotnet publish *.csproj -c Release -o out
+
+FROM mcr.microsoft.com/dotnet/aspnet:7.0
+WORKDIR /app
+COPY . ./
+ENV AWS_REGION=us-west-2
+ENV OTEL_EXPORTER_OTLP_ENDPOINT=http://otel:4317
+COPY --from=build-env /app/out .
+ENTRYPOINT ["dotnet", "dotnet-sample-app.dll"]
+
diff --git a/sample-applications/udp-exporter-test-app/Program.cs b/sample-applications/udp-exporter-test-app/Program.cs
new file mode 100644
index 00000000..162f9ceb
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/Program.cs
@@ -0,0 +1,26 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Hosting;
+using dotnet_sample_app.Controllers;
+using System;
+
+namespace dotnet_sample_app
+{
+ public class Program
+ {
+ public static Config cfg = Config.ReadInFile("config.yaml");
+
+ public static void Main(string[] args)
+ {
+ CreateHostBuilder(args).Build().Run();
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder.UseStartup();
+ string listenAddress = "http://"+cfg.Host+":"+cfg.Port;
+ webBuilder.UseUrls(listenAddress);
+ });
+ }
+}
diff --git a/sample-applications/udp-exporter-test-app/Properties/launchSettings.json b/sample-applications/udp-exporter-test-app/Properties/launchSettings.json
new file mode 100644
index 00000000..119da20d
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "dotnet-sample-app": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317",
+ "OTEL_EXPORTER_OTLP_INSECURE": "True"
+ }
+ }
+ }
+}
diff --git a/sample-applications/udp-exporter-test-app/README.md b/sample-applications/udp-exporter-test-app/README.md
new file mode 100644
index 00000000..201a2c76
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/README.md
@@ -0,0 +1,50 @@
+## .NET Opentelemetry Sample App
+
+### Description
+
+This .NET Sample App will emit Traces and Metrics. There are two types of metrics emitted;
+Request Based and Random Based.
+Metrics are generated as soon as the application is ran or deployed without any additional effort. These are considered the random based metrics which track a mock of TimeAlive, TotalHeapSize, ThreadsActive and CpuUsage. The boundaries for these metrics are standard and can be found in the configuration file (YAML) called config.yaml.
+
+Additionally, you can generate Traces and request based Metrics by making requests to the following exposed endpoints:
+
+1. /
+ 1. Ensures the application is running
+2. /outgoing-http-call
+ 1. Makes a HTTP request to aws.amazon.com (http://aws.amazon.com/)
+3. /aws-sdk-call
+ 1. Makes a call to AWS S3 to list buckets for the account corresponding to the provided AWS credentials
+4. /outgoing-sampleapp
+ 1. Makes a call to all other sample app ports configured at `:/outgoing-sampleapp`. If none available, makes a HTTP request to www.amazon.com (http://www.amazon.com/)
+
+[Sample App Spec](../SampleAppSpec.md)
+
+* Non-conformance: This SDK language is not missing any features or extensions required other than Resource Detectors
+* Workarounds: No workarounds are being used in this application
+
+### Getting Started:
+
+#### Running the application (local)
+
+In order to run the application
+
+- Clone the repository
+`git clone https://github.com/aws-observability/aws-otel-community.git`
+- Switch into the directory
+`cd sample-apps/dotnet-sample-app`
+- Install dependencies
+`dotnet build`
+- Run the .NET server
+`dotnet run`
+Now the application is ran and the endpoints can be called at `0.0.0.0:8080/`.
+
+#### Docker
+
+In order to build the Docker image and run it in a container
+
+- Build the image
+`docker build -t dotnet-sample-app .`
+- Run the image in a container
+`docker run -p 8080:8080 dotnet-sample-app`
+
+
diff --git a/sample-applications/udp-exporter-test-app/Startup.cs b/sample-applications/udp-exporter-test-app/Startup.cs
new file mode 100644
index 00000000..fbbd0b5c
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/Startup.cs
@@ -0,0 +1,115 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using OpenTelemetry;
+using OpenTelemetry.Contrib.Extensions.AWSXRay.Trace;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+using OpenTelemetry.Metrics;
+using System;
+using System.Diagnostics;
+using dotnet_sample_app.Controllers;
+using AWS.OpenTelemetry.Exporter.Otlp.Udp;
+
+
+namespace dotnet_sample_app
+{
+ public class Startup
+ {
+ public static MetricEmitter metricEmitter = new MetricEmitter();
+
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddControllers();
+
+ AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
+
+ if(!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("OTEL_RESOURCE_ATTRIBUTES"))) {
+ var resourceBuilder = ResourceBuilder.CreateDefault().AddTelemetrySdk();
+ Sdk.CreateTracerProviderBuilder()
+ .AddSource("dotnet-sample-app")
+ .SetResourceBuilder(resourceBuilder)
+ .AddAWSInstrumentation()
+ .AddAspNetCoreInstrumentation()
+ .AddHttpClientInstrumentation()
+ .AddOtlpExporter(options =>
+ {
+ options.Endpoint = new Uri(Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT"));
+ })
+ .AddOtlpUdpExporter(resourceBuilder.Build(), "localhost:2000")
+ .Build();
+ }
+ else {
+ var resourceBuilder = ResourceBuilder.CreateDefault()
+ .AddService(serviceName: "dotnet-sample-app") // Added first
+ .AddTelemetrySdk();
+ Sdk.CreateTracerProviderBuilder()
+ .AddSource("dotnet-sample-app")
+ .SetResourceBuilder(resourceBuilder)
+ .AddAWSInstrumentation()
+ .AddAspNetCoreInstrumentation()
+ .AddHttpClientInstrumentation()
+ .AddOtlpExporter(options =>
+ {
+ options.Endpoint = new Uri(Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT"));
+ })
+ .AddOtlpUdpExporter(resourceBuilder.Build(), "localhost:2000")
+ .Build();
+ }
+
+ Sdk.CreateMeterProviderBuilder()
+ .AddMeter("adot")
+ .AddOtlpExporter()
+ .Build();
+
+ Sdk.SetDefaultTextMapPropagator(new AWSXRayPropagator());
+
+
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ app.UseRouting();
+
+ app.UseAuthorization();
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ });
+
+ metricEmitter.UpdateRandomMetrics();
+ }
+ }
+
+ public static class TracerProviderBuilderExtensions
+ {
+ public static TracerProviderBuilder AddOtlpUdpExporter(
+ this TracerProviderBuilder builder,
+ Resource resource,
+ string endpoint)
+ {
+ return builder.AddProcessor(
+ new BatchActivityExportProcessor(
+ new OtlpUdpExporter(resource, endpoint)
+ )
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/sample-applications/udp-exporter-test-app/appsettings.Development.json b/sample-applications/udp-exporter-test-app/appsettings.Development.json
new file mode 100644
index 00000000..8983e0fc
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
diff --git a/sample-applications/udp-exporter-test-app/appsettings.json b/sample-applications/udp-exporter-test-app/appsettings.json
new file mode 100644
index 00000000..d9d9a9bf
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/appsettings.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/sample-applications/udp-exporter-test-app/collector-config-local.yml b/sample-applications/udp-exporter-test-app/collector-config-local.yml
new file mode 100644
index 00000000..0dbbeae0
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/collector-config-local.yml
@@ -0,0 +1,28 @@
+receivers:
+ otlp:
+ protocols:
+ grpc:
+ endpoint: 0.0.0.0:4317
+
+exporters:
+ debug:
+ verbosity: detailed
+ awsxray:
+ region: us-west-2
+ awsemf:
+ region: us-west-2
+
+service:
+ pipelines:
+ traces:
+ receivers:
+ - otlp
+ exporters:
+ - debug
+ - awsxray
+ metrics:
+ receivers:
+ - otlp
+ exporters:
+ - debug
+ - awsemf
diff --git a/sample-applications/udp-exporter-test-app/config.yaml b/sample-applications/udp-exporter-test-app/config.yaml
new file mode 100644
index 00000000..674baf1a
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/config.yaml
@@ -0,0 +1,9 @@
+---
+Host: "0.0.0.0" # Host - String Address
+Port: "8080" # Port - String Port
+TimeInterval: 1 # Interval - Time in seconds to generate new metrics
+RandomTimeAliveIncrementer: 1 # Metric - Amount to incremement metric by every TimeInterval
+RandomTotalHeapSizeUpperBound: 100 # Metric - UpperBound for TotalHeapSize for random metric value every TimeInterval
+RandomThreadsActiveUpperBound: 10 # Metric - UpperBound for ThreadsActive for random metric value every TimeInterval
+RandomCpuUsageUpperBound: 100 # Metric - UppperBound for CpuUsage for random metric value every TimeInterval
+SampleAppPorts: [] # Sampleapp ports to make calls to
diff --git a/sample-applications/udp-exporter-test-app/docker-compose.yml b/sample-applications/udp-exporter-test-app/docker-compose.yml
new file mode 100644
index 00000000..e566dbd3
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/docker-compose.yml
@@ -0,0 +1,28 @@
+version: "3.7"
+services:
+ otel:
+ image: amazon/aws-otel-collector:latest
+ command: --config /config/collector-config-local.yml
+ volumes:
+ - ~/.aws:/home/aoc/.aws:ro
+ - .:/config
+ environment:
+ - AWS_REGION=us-west-2
+ ports:
+ - '4317:4317'
+
+ app:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ environment:
+ - AWS_REGION=us-west-2
+ - INSTANCE_ID
+ - LISTEN_ADDRESS=0.0.0.0:8080
+ - OTEL_RESOURCE_ATTRIBUTES=service.name=adot-integ-test
+ - OTEL_EXPORTER_OTLP_ENDPOINT=http://otel:4317
+ - ASPNETCORE_URLS=http://+:8080
+ ports:
+ - '8080:8080'
+ volumes:
+ - ~/.aws:/root/.aws:ro
diff --git a/sample-applications/udp-exporter-test-app/dotnet-sample-app.csproj b/sample-applications/udp-exporter-test-app/dotnet-sample-app.csproj
new file mode 100644
index 00000000..4101fa30
--- /dev/null
+++ b/sample-applications/udp-exporter-test-app/dotnet-sample-app.csproj
@@ -0,0 +1,20 @@
+
+
+ net8.0
+ dotnet_sample_app
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+