Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/release-udp-exporter.yml
Original file line number Diff line number Diff line change
@@ -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
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="local-udp-exporter" value="$GITHUB_WORKSPACE/exporters/AWS.OpenTelemetry.Exporter.Otlp.Udp/bin/Release" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>
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
Original file line number Diff line number Diff line change
@@ -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);
}
}

}
45 changes: 45 additions & 0 deletions sample-applications/udp-exporter-test-app/Controllers/Config.cs
Original file line number Diff line number Diff line change
@@ -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<Config>(File.ReadAllText(file));
}
catch {
returnConfig = new Config();
}
return returnConfig;

}
}
}
Loading
Loading