From 9c99fb0dbdb1f883ce92f82e5957a2c5f3df8856 Mon Sep 17 00:00:00 2001 From: Chris Blake Date: Fri, 24 Nov 2023 10:31:21 +0000 Subject: [PATCH 1/2] .NET 8 Upgrade --- .appveyor.yml | 1 + README.md | 2 +- ...nTracing.Contrib.NetCore.Benchmarks.csproj | 6 +++++- global.json | 2 +- launch-sample.ps1 | 2 +- .../OpenTracing.Contrib.NetCore.csproj | 21 ++++++++++++++++++- .../AspNetCore/HostingTest.cs | 1 + .../OpenTracing.Contrib.NetCore.Tests.csproj | 7 ++++++- 8 files changed, 36 insertions(+), 6 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index e414ff0..9f46984 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -16,6 +16,7 @@ build_script: - ps: .\dotnet-install.ps1 -Runtime dotnet -Version 3.1.10 - ps: .\dotnet-install.ps1 -Version 6.0.100 - ps: .\dotnet-install.ps1 -Version 7.0.100 + - ps: .\dotnet-install.ps1 -Version 8.0.100 - ps: .\build.ps1 test: off diff --git a/README.md b/README.md index 3b3b2b5..9070546 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ _**IMPORTANT:** OpenTracing and OpenCensus have merget to form **[OpenTelemetry] ## Supported .NET versions -This project currently only supports apps targeting .NET Core 3.1, .NET 6.0, or .NET 7.0! +This project currently only supports apps targeting .NET Core 3.1, .NET 6.0, .NET 7.0, or .NET 8.0! This project DOES NOT support the full .NET framework as that uses different instrumentation code. diff --git a/benchmarks/OpenTracing.Contrib.NetCore.Benchmarks/OpenTracing.Contrib.NetCore.Benchmarks.csproj b/benchmarks/OpenTracing.Contrib.NetCore.Benchmarks/OpenTracing.Contrib.NetCore.Benchmarks.csproj index 8b0f114..b1959bd 100644 --- a/benchmarks/OpenTracing.Contrib.NetCore.Benchmarks/OpenTracing.Contrib.NetCore.Benchmarks.csproj +++ b/benchmarks/OpenTracing.Contrib.NetCore.Benchmarks/OpenTracing.Contrib.NetCore.Benchmarks.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1;net6.0;net7.0 + netcoreapp3.1;net6.0;net7.0;net8.0 @@ -22,6 +22,10 @@ + + + + diff --git a/global.json b/global.json index 001eb58..60e45fc 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.100", + "version": "8.0.100", "rollForward": "feature" } } diff --git a/launch-sample.ps1 b/launch-sample.ps1 index 316d829..2d73f98 100644 --- a/launch-sample.ps1 +++ b/launch-sample.ps1 @@ -1,6 +1,6 @@ [CmdletBinding(PositionalBinding = $false)] param( - [ValidateSet("net7.0", "net6.0", "netcoreapp3.1")] + [ValidateSet("net8.0", "net7.0", "net6.0", "netcoreapp3.1")] [string] $Framework = "net6.0" ) diff --git a/src/OpenTracing.Contrib.NetCore/OpenTracing.Contrib.NetCore.csproj b/src/OpenTracing.Contrib.NetCore/OpenTracing.Contrib.NetCore.csproj index acc2f63..d02c5b4 100644 --- a/src/OpenTracing.Contrib.NetCore/OpenTracing.Contrib.NetCore.csproj +++ b/src/OpenTracing.Contrib.NetCore/OpenTracing.Contrib.NetCore.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0;net7.0 + netcoreapp3.1;net6.0;net7.0;net8.0 Adds OpenTracing instrumentation for .NET Core apps that use the `Microsoft.Extensions.*` stack. Instrumented components: HttpClient calls, ASP.NET Core, Entity Framework Core and any other library that uses DiagnosticSource events. opentracing;distributed-tracing;tracing;netcore @@ -65,4 +65,23 @@ Instrumented components: HttpClient calls, ASP.NET Core, Entity Framework Core a + + + + + + + + + + + + + + + + + + + diff --git a/test/OpenTracing.Contrib.NetCore.Tests/AspNetCore/HostingTest.cs b/test/OpenTracing.Contrib.NetCore.Tests/AspNetCore/HostingTest.cs index bb50aea..7f0e5b0 100644 --- a/test/OpenTracing.Contrib.NetCore.Tests/AspNetCore/HostingTest.cs +++ b/test/OpenTracing.Contrib.NetCore.Tests/AspNetCore/HostingTest.cs @@ -142,6 +142,7 @@ public async Task Span_has_status_404() var client = CreateClient(); await client.GetAsync("/not-found"); + await Task.Delay(50); var finishedSpans = _tracer.FinishedSpans(); Assert.Single(finishedSpans); diff --git a/test/OpenTracing.Contrib.NetCore.Tests/OpenTracing.Contrib.NetCore.Tests.csproj b/test/OpenTracing.Contrib.NetCore.Tests/OpenTracing.Contrib.NetCore.Tests.csproj index d72589e..e3abeb5 100644 --- a/test/OpenTracing.Contrib.NetCore.Tests/OpenTracing.Contrib.NetCore.Tests.csproj +++ b/test/OpenTracing.Contrib.NetCore.Tests/OpenTracing.Contrib.NetCore.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net6.0;net7.0 + netcoreapp3.1;net6.0;net7.0;net8.0 @@ -27,6 +27,11 @@ + + + + + From 12464719f792270dfbf9a434891448b86d7bf849 Mon Sep 17 00:00:00 2001 From: Chris Blake Date: Fri, 24 Nov 2023 10:31:33 +0000 Subject: [PATCH 2/2] .NET 8 sample projects --- OpenTracing.Contrib.sln | 38 ++++++++++ .../net8.0/CustomersApi/CustomersApi.csproj | 19 +++++ .../DataStore/CustomerDbContext.cs | 31 ++++++++ samples/net8.0/CustomersApi/Program.cs | 74 ++++++++++++++++++ .../Properties/launchSettings.json | 12 +++ samples/net8.0/CustomersApi/appsettings.json | 10 +++ .../FrontendWeb/Controllers/HomeController.cs | 71 +++++++++++++++++ samples/net8.0/FrontendWeb/FrontendWeb.csproj | 14 ++++ samples/net8.0/FrontendWeb/Program.cs | 28 +++++++ .../Properties/launchSettings.json | 12 +++ .../FrontendWeb/Views/Home/Index.cshtml | 8 ++ .../FrontendWeb/Views/Home/PlaceOrder.cshtml | 13 ++++ .../FrontendWeb/Views/_ViewImports.cshtml | 2 + samples/net8.0/FrontendWeb/appsettings.json | 11 +++ .../OrdersApi/Controllers/OrdersController.cs | 76 +++++++++++++++++++ samples/net8.0/OrdersApi/DataStore/Order.cs | 17 +++++ .../OrdersApi/DataStore/OrdersDbContext.cs | 23 ++++++ samples/net8.0/OrdersApi/OrdersApi.csproj | 19 +++++ samples/net8.0/OrdersApi/Program.cs | 57 ++++++++++++++ .../OrdersApi/Properties/launchSettings.json | 12 +++ samples/net8.0/OrdersApi/appsettings.json | 10 +++ samples/net8.0/Shared/Constants.cs | 10 +++ samples/net8.0/Shared/Customer.cs | 19 +++++ .../JaegerServiceCollectionExtensions.cs | 53 +++++++++++++ samples/net8.0/Shared/PlaceOrderCommand.cs | 15 ++++ samples/net8.0/Shared/Shared.csproj | 20 +++++ samples/net8.0/TrafficGenerator/Program.cs | 15 ++++ .../TrafficGenerator/TrafficGenerator.csproj | 14 ++++ samples/net8.0/TrafficGenerator/Worker.cs | 51 +++++++++++++ .../net8.0/TrafficGenerator/appsettings.json | 10 +++ 30 files changed, 764 insertions(+) create mode 100644 samples/net8.0/CustomersApi/CustomersApi.csproj create mode 100644 samples/net8.0/CustomersApi/DataStore/CustomerDbContext.cs create mode 100644 samples/net8.0/CustomersApi/Program.cs create mode 100644 samples/net8.0/CustomersApi/Properties/launchSettings.json create mode 100644 samples/net8.0/CustomersApi/appsettings.json create mode 100644 samples/net8.0/FrontendWeb/Controllers/HomeController.cs create mode 100644 samples/net8.0/FrontendWeb/FrontendWeb.csproj create mode 100644 samples/net8.0/FrontendWeb/Program.cs create mode 100644 samples/net8.0/FrontendWeb/Properties/launchSettings.json create mode 100644 samples/net8.0/FrontendWeb/Views/Home/Index.cshtml create mode 100644 samples/net8.0/FrontendWeb/Views/Home/PlaceOrder.cshtml create mode 100644 samples/net8.0/FrontendWeb/Views/_ViewImports.cshtml create mode 100644 samples/net8.0/FrontendWeb/appsettings.json create mode 100644 samples/net8.0/OrdersApi/Controllers/OrdersController.cs create mode 100644 samples/net8.0/OrdersApi/DataStore/Order.cs create mode 100644 samples/net8.0/OrdersApi/DataStore/OrdersDbContext.cs create mode 100644 samples/net8.0/OrdersApi/OrdersApi.csproj create mode 100644 samples/net8.0/OrdersApi/Program.cs create mode 100644 samples/net8.0/OrdersApi/Properties/launchSettings.json create mode 100644 samples/net8.0/OrdersApi/appsettings.json create mode 100644 samples/net8.0/Shared/Constants.cs create mode 100644 samples/net8.0/Shared/Customer.cs create mode 100644 samples/net8.0/Shared/JaegerServiceCollectionExtensions.cs create mode 100644 samples/net8.0/Shared/PlaceOrderCommand.cs create mode 100644 samples/net8.0/Shared/Shared.csproj create mode 100644 samples/net8.0/TrafficGenerator/Program.cs create mode 100644 samples/net8.0/TrafficGenerator/TrafficGenerator.csproj create mode 100644 samples/net8.0/TrafficGenerator/Worker.cs create mode 100644 samples/net8.0/TrafficGenerator/appsettings.json diff --git a/OpenTracing.Contrib.sln b/OpenTracing.Contrib.sln index 1e4b893..c83c7e6 100644 --- a/OpenTracing.Contrib.sln +++ b/OpenTracing.Contrib.sln @@ -67,6 +67,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "samples\net7.0\Sh EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrafficGenerator", "samples\net7.0\TrafficGenerator\TrafficGenerator.csproj", "{69E6E77E-646D-475A-9B6B-C5511C21B11C}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "net8.0", "net8.0", "{71609BAA-55BF-4A0A-AE05-83D87644E44C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomersApi", "samples\net8.0\CustomersApi\CustomersApi.csproj", "{0DF66128-3C35-4301-98E2-8822FB56B146}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrontendWeb", "samples\net8.0\FrontendWeb\FrontendWeb.csproj", "{744E71C6-79A5-4566-BCBA-221AF084A7EF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrdersApi", "samples\net8.0\OrdersApi\OrdersApi.csproj", "{63E80769-4B3E-4897-ABD1-237226ED0629}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "samples\net8.0\Shared\Shared.csproj", "{05A112D5-65C4-4F7C-A974-94E5EFBE32DC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrafficGenerator", "samples\net8.0\TrafficGenerator\TrafficGenerator.csproj", "{67A203A9-9EAC-4375-9A32-E42E91C8AA22}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -145,6 +157,26 @@ Global {69E6E77E-646D-475A-9B6B-C5511C21B11C}.Debug|Any CPU.Build.0 = Debug|Any CPU {69E6E77E-646D-475A-9B6B-C5511C21B11C}.Release|Any CPU.ActiveCfg = Release|Any CPU {69E6E77E-646D-475A-9B6B-C5511C21B11C}.Release|Any CPU.Build.0 = Release|Any CPU + {0DF66128-3C35-4301-98E2-8822FB56B146}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0DF66128-3C35-4301-98E2-8822FB56B146}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DF66128-3C35-4301-98E2-8822FB56B146}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0DF66128-3C35-4301-98E2-8822FB56B146}.Release|Any CPU.Build.0 = Release|Any CPU + {744E71C6-79A5-4566-BCBA-221AF084A7EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {744E71C6-79A5-4566-BCBA-221AF084A7EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {744E71C6-79A5-4566-BCBA-221AF084A7EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {744E71C6-79A5-4566-BCBA-221AF084A7EF}.Release|Any CPU.Build.0 = Release|Any CPU + {63E80769-4B3E-4897-ABD1-237226ED0629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63E80769-4B3E-4897-ABD1-237226ED0629}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63E80769-4B3E-4897-ABD1-237226ED0629}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63E80769-4B3E-4897-ABD1-237226ED0629}.Release|Any CPU.Build.0 = Release|Any CPU + {05A112D5-65C4-4F7C-A974-94E5EFBE32DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05A112D5-65C4-4F7C-A974-94E5EFBE32DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05A112D5-65C4-4F7C-A974-94E5EFBE32DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05A112D5-65C4-4F7C-A974-94E5EFBE32DC}.Release|Any CPU.Build.0 = Release|Any CPU + {67A203A9-9EAC-4375-9A32-E42E91C8AA22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67A203A9-9EAC-4375-9A32-E42E91C8AA22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67A203A9-9EAC-4375-9A32-E42E91C8AA22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67A203A9-9EAC-4375-9A32-E42E91C8AA22}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -171,6 +203,12 @@ Global {48CD3687-6EB7-47CD-A611-8D579FBCBB3B} = {E46F4333-859A-4CC5-BD2A-7FE8892861BE} {B4A8B209-AA79-4C34-9A12-F03E3423B330} = {E46F4333-859A-4CC5-BD2A-7FE8892861BE} {69E6E77E-646D-475A-9B6B-C5511C21B11C} = {E46F4333-859A-4CC5-BD2A-7FE8892861BE} + {71609BAA-55BF-4A0A-AE05-83D87644E44C} = {36333C22-54F7-403C-ABC4-BECE4EE3F52D} + {0DF66128-3C35-4301-98E2-8822FB56B146} = {71609BAA-55BF-4A0A-AE05-83D87644E44C} + {744E71C6-79A5-4566-BCBA-221AF084A7EF} = {71609BAA-55BF-4A0A-AE05-83D87644E44C} + {63E80769-4B3E-4897-ABD1-237226ED0629} = {71609BAA-55BF-4A0A-AE05-83D87644E44C} + {05A112D5-65C4-4F7C-A974-94E5EFBE32DC} = {71609BAA-55BF-4A0A-AE05-83D87644E44C} + {67A203A9-9EAC-4375-9A32-E42E91C8AA22} = {71609BAA-55BF-4A0A-AE05-83D87644E44C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {832F86C2-4B74-4259-8073-04EE0C37FBCD} diff --git a/samples/net8.0/CustomersApi/CustomersApi.csproj b/samples/net8.0/CustomersApi/CustomersApi.csproj new file mode 100644 index 0000000..99ecb9b --- /dev/null +++ b/samples/net8.0/CustomersApi/CustomersApi.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/samples/net8.0/CustomersApi/DataStore/CustomerDbContext.cs b/samples/net8.0/CustomersApi/DataStore/CustomerDbContext.cs new file mode 100644 index 0000000..55c61d5 --- /dev/null +++ b/samples/net8.0/CustomersApi/DataStore/CustomerDbContext.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore; +using Shared; + +namespace CustomersApi.DataStore; + +public class CustomerDbContext : DbContext +{ + public CustomerDbContext(DbContextOptions options) + : base(options) + { + } + + public DbSet Customers => Set(); + + public void Seed() + { + if (Database.EnsureCreated()) + { + Database.Migrate(); + + Customers.Add(new Customer(1, "Marcel Belding")); + Customers.Add(new Customer(2, "Phyllis Schriver")); + Customers.Add(new Customer(3, "Estefana Balderrama")); + Customers.Add(new Customer(4, "Kenyetta Lone")); + Customers.Add(new Customer(5, "Vernita Fernald")); + Customers.Add(new Customer(6, "Tessie Storrs")); + + SaveChanges(); + } + } +} diff --git a/samples/net8.0/CustomersApi/Program.cs b/samples/net8.0/CustomersApi/Program.cs new file mode 100644 index 0000000..57048ec --- /dev/null +++ b/samples/net8.0/CustomersApi/Program.cs @@ -0,0 +1,74 @@ +using CustomersApi.DataStore; +using Microsoft.EntityFrameworkCore; +using Shared; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.WebHost.UseUrls(Constants.CustomersUrl); + +// Registers and starts Jaeger (see Shared.JaegerServiceCollectionExtensions) +builder.Services.AddJaeger(); + +// Enables OpenTracing instrumentation for ASP.NET Core, CoreFx, EF Core +builder.Services.AddOpenTracing(ot => +{ + ot.ConfigureAspNetCore(options => + { + // We don't need any tracing data for our health endpoint. + options.Hosting.IgnorePatterns.Add(ctx => ctx.Request.Path == "/health"); + }); + + ot.ConfigureEntityFrameworkCore(options => + { + // This is an example for how certain EF Core commands can be ignored. + // As en example, we're ignoring the "PRAGMA foreign_keys=ON;" commands that are executed by Sqlite. + // Remove this code to see those statements. + options.IgnorePatterns.Add(cmd => cmd.Command.CommandText.StartsWith("PRAGMA")); + }); +}); + +// Adds a Sqlite DB to show EFCore traces. +builder.Services.AddDbContext(options => +{ + options.UseSqlite("Data Source=DataStore/customers.db"); +}); + +builder.Services.AddHealthChecks() + .AddDbContextCheck(); + + +var app = builder.Build(); + + +// Load some dummy data into the db. +using (var scope = app.Services.CreateScope()) +{ + var dbContext = scope.ServiceProvider.GetRequiredService(); + dbContext.Seed(); +} + + +// Configure the HTTP request pipeline. + +app.MapGet("/", () => "Customers API"); + +app.MapHealthChecks("/health"); + +app.MapGet("/customers", async (CustomerDbContext dbContext) => await dbContext.Customers.ToListAsync()); + +app.MapGet("/customers/{id}", async (int id, CustomerDbContext dbContext, ILogger logger) => +{ + var customer = await dbContext.Customers.FirstOrDefaultAsync(x => x.CustomerId == id); + + if (customer == null) + return Results.NotFound(); + + // ILogger events are sent to OpenTracing as well! + logger.LogInformation("Returning data for customer {CustomerId}", id); + + return Results.Ok(customer); +}); + +app.Run(); diff --git a/samples/net8.0/CustomersApi/Properties/launchSettings.json b/samples/net8.0/CustomersApi/Properties/launchSettings.json new file mode 100644 index 0000000..e40eb1b --- /dev/null +++ b/samples/net8.0/CustomersApi/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "CustomersApi": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": "http://localhost:5001", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/net8.0/CustomersApi/appsettings.json b/samples/net8.0/CustomersApi/appsettings.json new file mode 100644 index 0000000..723c096 --- /dev/null +++ b/samples/net8.0/CustomersApi/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/samples/net8.0/FrontendWeb/Controllers/HomeController.cs b/samples/net8.0/FrontendWeb/Controllers/HomeController.cs new file mode 100644 index 0000000..17ad7ae --- /dev/null +++ b/samples/net8.0/FrontendWeb/Controllers/HomeController.cs @@ -0,0 +1,71 @@ +using System.Text; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using Newtonsoft.Json; +using Shared; + +namespace FrontendWeb.Controllers; + +public class HomeController : Controller +{ + private readonly HttpClient _httpClient; + + public HomeController(HttpClient httpClient) + { + _httpClient = httpClient; + } + + [HttpGet] + public IActionResult Index() + { + return View(); + } + + [HttpGet] + public async Task PlaceOrder() + { + ViewBag.Customers = await GetCustomers(); + return View(new PlaceOrderCommand { ItemNumber = "ABC11", Quantity = 1 }); + } + + [HttpPost, ValidateAntiForgeryToken] + public async Task PlaceOrder(PlaceOrderCommand cmd) + { + if (!ModelState.IsValid) + { + ViewBag.Customers = await GetCustomers(); + return View(cmd); + } + + string body = JsonConvert.SerializeObject(cmd); + + var request = new HttpRequestMessage + { + Method = HttpMethod.Post, + RequestUri = new Uri(Constants.OrdersUrl + "orders"), + Content = new StringContent(body, Encoding.UTF8, "application/json") + }; + + await _httpClient.SendAsync(request); + + return RedirectToAction("Index"); + } + + private async Task> GetCustomers() + { + var request = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = new Uri(Constants.CustomersUrl + "customers") + }; + + var response = await _httpClient.SendAsync(request); + + response.EnsureSuccessStatusCode(); + + var body = await response.Content.ReadAsStringAsync(); + + return JsonConvert.DeserializeObject>(body) + .Select(x => new SelectListItem { Value = x.CustomerId.ToString(), Text = x.Name }); + } +} diff --git a/samples/net8.0/FrontendWeb/FrontendWeb.csproj b/samples/net8.0/FrontendWeb/FrontendWeb.csproj new file mode 100644 index 0000000..002f5c1 --- /dev/null +++ b/samples/net8.0/FrontendWeb/FrontendWeb.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/samples/net8.0/FrontendWeb/Program.cs b/samples/net8.0/FrontendWeb/Program.cs new file mode 100644 index 0000000..f32cb02 --- /dev/null +++ b/samples/net8.0/FrontendWeb/Program.cs @@ -0,0 +1,28 @@ +using Shared; + + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.WebHost.UseUrls(Constants.FrontendUrl); + +// Registers and starts Jaeger (see Shared.JaegerServiceCollectionExtensions) +builder.Services.AddJaeger(); + +// Enables OpenTracing instrumentation for ASP.NET Core, CoreFx, EF Core +builder.Services.AddOpenTracing(); + +builder.Services.AddSingleton(); + +builder.Services.AddMvc(); + + +var app = builder.Build(); + + +// Configure the HTTP request pipeline. + +app.MapDefaultControllerRoute(); + +app.Run(); diff --git a/samples/net8.0/FrontendWeb/Properties/launchSettings.json b/samples/net8.0/FrontendWeb/Properties/launchSettings.json new file mode 100644 index 0000000..8870b99 --- /dev/null +++ b/samples/net8.0/FrontendWeb/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "FrontendWeb": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/net8.0/FrontendWeb/Views/Home/Index.cshtml b/samples/net8.0/FrontendWeb/Views/Home/Index.cshtml new file mode 100644 index 0000000..8948537 --- /dev/null +++ b/samples/net8.0/FrontendWeb/Views/Home/Index.cshtml @@ -0,0 +1,8 @@ + + +FrontendWeb +

FrontendWeb

+

This is the beautiful web frontend.

+

Refresh this page a few times and check the Jaeger UI - you should already see traces!

+ +

Place an order

diff --git a/samples/net8.0/FrontendWeb/Views/Home/PlaceOrder.cshtml b/samples/net8.0/FrontendWeb/Views/Home/PlaceOrder.cshtml new file mode 100644 index 0000000..70a6ebc --- /dev/null +++ b/samples/net8.0/FrontendWeb/Views/Home/PlaceOrder.cshtml @@ -0,0 +1,13 @@ +@model Shared.PlaceOrderCommand + + +FrontendWeb +

FrontendWeb

+

Place an order

+ +
+
Customer: @Html.DropDownListFor(x => x.CustomerId, (IEnumerable)ViewBag.Customers)
+
ItemNumber: @Html.TextBoxFor(x => x.ItemNumber)
+
Quantity: @Html.TextBoxFor(x => x.Quantity)
+ +
diff --git a/samples/net8.0/FrontendWeb/Views/_ViewImports.cshtml b/samples/net8.0/FrontendWeb/Views/_ViewImports.cshtml new file mode 100644 index 0000000..1113d34 --- /dev/null +++ b/samples/net8.0/FrontendWeb/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@using FrontendWeb +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/samples/net8.0/FrontendWeb/appsettings.json b/samples/net8.0/FrontendWeb/appsettings.json new file mode 100644 index 0000000..36ea555 --- /dev/null +++ b/samples/net8.0/FrontendWeb/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information", + "OpenTracing": "Debug" + } + } +} diff --git a/samples/net8.0/OrdersApi/Controllers/OrdersController.cs b/samples/net8.0/OrdersApi/Controllers/OrdersController.cs new file mode 100644 index 0000000..aa0095c --- /dev/null +++ b/samples/net8.0/OrdersApi/Controllers/OrdersController.cs @@ -0,0 +1,76 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; +using OpenTracing; +using OrdersApi.DataStore; +using Shared; + +namespace OrdersApi.Controllers; + +[Route("orders")] +public class OrdersController : Controller +{ + private readonly OrdersDbContext _dbContext; + private readonly HttpClient _httpClient; + private readonly ITracer _tracer; + + public OrdersController(OrdersDbContext dbContext, HttpClient httpClient, ITracer tracer) + { + _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + _tracer = tracer ?? throw new ArgumentNullException(nameof(tracer)); + } + + [HttpGet] + public async Task Index() + { + var orders = await _dbContext.Orders.ToListAsync(); + + return Ok(orders.Select(x => new { x.OrderId }).ToList()); + } + + [HttpPost] + public async Task Index([FromBody] PlaceOrderCommand cmd) + { + var customer = await GetCustomer(cmd.CustomerId); + + var order = new Order + { + CustomerId = cmd.CustomerId, + ItemNumber = cmd.ItemNumber, + Quantity = cmd.Quantity + }; + + _dbContext.Orders.Add(order); + + await _dbContext.SaveChangesAsync(); + + _tracer.ActiveSpan?.Log(new Dictionary { + { "event", "OrderPlaced" }, + { "orderId", order.OrderId }, + { "customer", order.CustomerId }, + { "customer_name", customer.Name }, + { "item_number", order.ItemNumber }, + { "quantity", order.Quantity } + }); + + return Ok(); + } + + private async Task GetCustomer(int customerId) + { + var request = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = new Uri(Constants.CustomersUrl + "customers/" + customerId) + }; + + var response = await _httpClient.SendAsync(request); + + response.EnsureSuccessStatusCode(); + + var body = await response.Content.ReadAsStringAsync(); + + return JsonConvert.DeserializeObject(body); + } +} diff --git a/samples/net8.0/OrdersApi/DataStore/Order.cs b/samples/net8.0/OrdersApi/DataStore/Order.cs new file mode 100644 index 0000000..b2572fd --- /dev/null +++ b/samples/net8.0/OrdersApi/DataStore/Order.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace OrdersApi.DataStore; + +public class Order +{ + [Key] + public int OrderId { get; set; } + + public int CustomerId { get; set; } + + [Required, StringLength(10)] + public string ItemNumber { get; set; } = string.Empty; + + [Required, Range(1, 100)] + public int Quantity { get; set; } +} diff --git a/samples/net8.0/OrdersApi/DataStore/OrdersDbContext.cs b/samples/net8.0/OrdersApi/DataStore/OrdersDbContext.cs new file mode 100644 index 0000000..730384a --- /dev/null +++ b/samples/net8.0/OrdersApi/DataStore/OrdersDbContext.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; + +namespace OrdersApi.DataStore; + +public class OrdersDbContext : DbContext +{ + public OrdersDbContext(DbContextOptions options) + : base(options) + { + } + + public DbSet Orders => Set(); + + public void Seed() + { + if (Database.EnsureCreated()) + { + Database.Migrate(); + + SaveChanges(); + } + } +} diff --git a/samples/net8.0/OrdersApi/OrdersApi.csproj b/samples/net8.0/OrdersApi/OrdersApi.csproj new file mode 100644 index 0000000..965d17c --- /dev/null +++ b/samples/net8.0/OrdersApi/OrdersApi.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/samples/net8.0/OrdersApi/Program.cs b/samples/net8.0/OrdersApi/Program.cs new file mode 100644 index 0000000..01e56d1 --- /dev/null +++ b/samples/net8.0/OrdersApi/Program.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore; +using OrdersApi.DataStore; +using Shared; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.WebHost.UseUrls(Constants.OrdersUrl); + +// Registers and starts Jaeger (see Shared.JaegerServiceCollectionExtensions) +builder.Services.AddJaeger(); + +// Enables OpenTracing instrumentation for ASP.NET Core, CoreFx, EF Core +builder.Services.AddOpenTracing(builder => +{ + builder.ConfigureAspNetCore(options => + { + // We don't need any tracing data for our health endpoint. + options.Hosting.IgnorePatterns.Add(ctx => ctx.Request.Path == "/health"); + }); +}); + +// Adds a SqlServer DB to show EFCore traces. +builder.Services.AddDbContext(options => +{ + options.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=Orders-net5;Trusted_Connection=True;MultipleActiveResultSets=true"); +}); + +builder.Services.AddSingleton(); + +builder.Services.AddMvc(); + +builder.Services.AddHealthChecks() + .AddDbContextCheck(); + + +var app = builder.Build(); + + +// Load some dummy data into the db. +using (var scope = app.Services.CreateScope()) +{ + var dbContext = scope.ServiceProvider.GetRequiredService(); + dbContext.Seed(); +} + + +// Configure the HTTP request pipeline. + +app.MapGet("/", () => "Orders API"); + +app.MapHealthChecks("/health"); + +app.MapDefaultControllerRoute(); + +app.Run(); diff --git a/samples/net8.0/OrdersApi/Properties/launchSettings.json b/samples/net8.0/OrdersApi/Properties/launchSettings.json new file mode 100644 index 0000000..6b818d0 --- /dev/null +++ b/samples/net8.0/OrdersApi/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "OrdersApi": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": "http://localhost:5002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/net8.0/OrdersApi/appsettings.json b/samples/net8.0/OrdersApi/appsettings.json new file mode 100644 index 0000000..723c096 --- /dev/null +++ b/samples/net8.0/OrdersApi/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/samples/net8.0/Shared/Constants.cs b/samples/net8.0/Shared/Constants.cs new file mode 100644 index 0000000..9979429 --- /dev/null +++ b/samples/net8.0/Shared/Constants.cs @@ -0,0 +1,10 @@ +namespace Shared; + +public class Constants +{ + public const string FrontendUrl = "http://localhost:5000/"; + + public const string CustomersUrl = "http://localhost:5001/"; + + public const string OrdersUrl = "http://localhost:5002/"; +} diff --git a/samples/net8.0/Shared/Customer.cs b/samples/net8.0/Shared/Customer.cs new file mode 100644 index 0000000..48248f5 --- /dev/null +++ b/samples/net8.0/Shared/Customer.cs @@ -0,0 +1,19 @@ +namespace Shared; + +public class Customer +{ + public int CustomerId { get; set; } + public string Name { get; set; } + + public Customer() + { + CustomerId = 0; + Name = string.Empty; + } + + public Customer(int customerId, string name) + { + CustomerId = customerId; + Name = name; + } +} diff --git a/samples/net8.0/Shared/JaegerServiceCollectionExtensions.cs b/samples/net8.0/Shared/JaegerServiceCollectionExtensions.cs new file mode 100644 index 0000000..b2f8f91 --- /dev/null +++ b/samples/net8.0/Shared/JaegerServiceCollectionExtensions.cs @@ -0,0 +1,53 @@ +using System.Reflection; +using Jaeger; +using Jaeger.Reporters; +using Jaeger.Samplers; +using Jaeger.Senders.Thrift; +using Microsoft.Extensions.Logging; +using OpenTracing; +using OpenTracing.Contrib.NetCore.Configuration; +using OpenTracing.Util; + +namespace Microsoft.Extensions.DependencyInjection; + +public static class JaegerServiceCollectionExtensions +{ + private static readonly Uri _jaegerUri = new Uri("http://localhost:14268/api/traces"); + + public static IServiceCollection AddJaeger(this IServiceCollection services) + { + if (services == null) + throw new ArgumentNullException(nameof(services)); + + services.AddSingleton(serviceProvider => + { + string serviceName = Assembly.GetEntryAssembly()?.GetName().Name ?? "unknown-service"; + + ILoggerFactory loggerFactory = serviceProvider.GetRequiredService(); + + ISampler sampler = new ConstSampler(sample: true); + + IReporter reporter = new RemoteReporter.Builder() + .WithSender(new HttpSender.Builder(_jaegerUri.ToString()).Build()) + .Build(); + + ITracer tracer = new Tracer.Builder(serviceName) + .WithLoggerFactory(loggerFactory) + .WithSampler(sampler) + .WithReporter(reporter) + .Build(); + + GlobalTracer.Register(tracer); + + return tracer; + }); + + // Prevent endless loops when OpenTracing is tracking HTTP requests to Jaeger. + services.Configure(options => + { + options.IgnorePatterns.Add(request => request.RequestUri != null && _jaegerUri.IsBaseOf(request.RequestUri)); + }); + + return services; + } +} diff --git a/samples/net8.0/Shared/PlaceOrderCommand.cs b/samples/net8.0/Shared/PlaceOrderCommand.cs new file mode 100644 index 0000000..a538e2c --- /dev/null +++ b/samples/net8.0/Shared/PlaceOrderCommand.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace Shared; + +public class PlaceOrderCommand +{ + [Required, Range(1, int.MaxValue)] + public int CustomerId { get; set; } + + [Required, StringLength(10)] + public string ItemNumber { get; set; } = string.Empty; + + [Required, Range(1, 100)] + public int Quantity { get; set; } +} diff --git a/samples/net8.0/Shared/Shared.csproj b/samples/net8.0/Shared/Shared.csproj new file mode 100644 index 0000000..ac57a2c --- /dev/null +++ b/samples/net8.0/Shared/Shared.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + diff --git a/samples/net8.0/TrafficGenerator/Program.cs b/samples/net8.0/TrafficGenerator/Program.cs new file mode 100644 index 0000000..23f4934 --- /dev/null +++ b/samples/net8.0/TrafficGenerator/Program.cs @@ -0,0 +1,15 @@ +using TrafficGenerator; + +using var host = Host.CreateDefaultBuilder(args) + .ConfigureServices((hostContext, services) => + { + // Registers and starts Jaeger (see Shared.JaegerServiceCollectionExtensions) + services.AddJaeger(); + + services.AddOpenTracing(); + + services.AddHostedService(); + }) + .Build(); + +await host.RunAsync(); diff --git a/samples/net8.0/TrafficGenerator/TrafficGenerator.csproj b/samples/net8.0/TrafficGenerator/TrafficGenerator.csproj new file mode 100644 index 0000000..78c7ee3 --- /dev/null +++ b/samples/net8.0/TrafficGenerator/TrafficGenerator.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/samples/net8.0/TrafficGenerator/Worker.cs b/samples/net8.0/TrafficGenerator/Worker.cs new file mode 100644 index 0000000..0717557 --- /dev/null +++ b/samples/net8.0/TrafficGenerator/Worker.cs @@ -0,0 +1,51 @@ +using Shared; + +namespace TrafficGenerator; + +public class Worker : BackgroundService +{ + private readonly ILogger _logger; + + public Worker(ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + try + { + HttpClient customersHttpClient = new HttpClient(); + customersHttpClient.BaseAddress = new Uri(Constants.CustomersUrl); + + HttpClient ordersHttpClient = new HttpClient(); + ordersHttpClient.BaseAddress = new Uri(Constants.OrdersUrl); + + + while (!stoppingToken.IsCancellationRequested) + { + HttpResponseMessage ordersHealthResponse = await ordersHttpClient.GetAsync("health"); + _logger.LogInformation($"Health of 'orders'-endpoint: '{ordersHealthResponse.StatusCode}'"); + + HttpResponseMessage customersHealthResponse = await customersHttpClient.GetAsync("health"); + _logger.LogInformation($"Health of 'customers'-endpoint: '{customersHealthResponse.StatusCode}'"); + + _logger.LogInformation("Requesting customers"); + + HttpResponseMessage response = await customersHttpClient.GetAsync("customers"); + + _logger.LogInformation($"Response was '{response.StatusCode}'"); + + await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); + } + } + catch (TaskCanceledException) + { + /* Application should be stopped -> no-op */ + } + catch (Exception ex) + { + _logger.LogError(ex, "Unhandled exception"); + } + } +} diff --git a/samples/net8.0/TrafficGenerator/appsettings.json b/samples/net8.0/TrafficGenerator/appsettings.json new file mode 100644 index 0000000..789de74 --- /dev/null +++ b/samples/net8.0/TrafficGenerator/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Warning", + "Microsoft.AspNetCore.Hosting": "Information" + } + } +}