File: Program.cs
Web Access
Project: src\playground\Stress\Stress.ApiService\Stress.ApiService.csproj (Stress.ApiService)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Text;
using System.Text.Json.Nodes;
using System.Threading.Channels;
using System.Xml.Linq;
using Microsoft.AspNetCore.Mvc;
using Stress.ApiService;
 
var builder = WebApplication.CreateBuilder(args);
 
builder.AddServiceDefaults();
 
builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing.AddSource(TraceCreator.ActivitySourceName, ProducerConsumer.ActivitySourceName))
    .WithMetrics(metrics => metrics.AddMeter(TestMetrics.MeterName));
builder.Services.AddSingleton<TestMetrics>();
 
var app = builder.Build();
 
app.Lifetime.ApplicationStarted.Register(ConsoleStresser.Stress);
 
app.Lifetime.ApplicationStarted.Register(() =>
{
    _ = app.Services.GetRequiredService<TestMetrics>();
});
 
app.MapGet("/", () => "Hello world");
 
app.MapGet("/write-console", () =>
{
    for (var i = 0; i < 5000; i++)
    {
        if (i % 500 == 0)
        {
            Console.Error.WriteLine($"{i} Error");
        }
        else
        {
            Console.Out.WriteLine($"{i} Out");
        }
    }
 
    return "Console written";
});
 
app.MapGet("/increment-counter", (TestMetrics metrics) =>
{
    metrics.IncrementCounter(1, new TagList([new KeyValuePair<string, object?>("add-tag", "1")]));
    metrics.IncrementCounter(2, new TagList([new KeyValuePair<string, object?>("add-tag", "")]));
    metrics.IncrementCounter(3, default);
 
    return "Counter incremented";
});
 
app.MapGet("/big-trace", async () =>
{
    var traceCreator = new TraceCreator
    {
        IncludeBrokenLinks = true
    };
 
    await traceCreator.CreateTraceAsync(count: 10, createChildren: true);
 
    return "Big trace created";
});
 
app.MapGet("/trace-limit", async () =>
{
    const int TraceCount = 20_000;
 
    var current = Activity.Current;
    Activity.Current = null;
    var traceCreator = new TraceCreator();
 
    for (var i = 0; i < TraceCount; i++)
    {
        await traceCreator.CreateTraceAsync(count: 1, createChildren: false);
    }
 
    Activity.Current = current;
 
    return $"Created {TraceCount} traces.";
});
 
app.MapGet("/http-client-requests", async (HttpClient client) =>
{
    var urls = Environment.GetEnvironmentVariable("ASPNETCORE_URLS")!.Split(';');
 
    foreach (var url in urls)
    {
        var response = await client.GetAsync(url);
        await response.Content.ReadAsStringAsync();
    }
 
    return $"Sent requests to {string.Join(';', urls)}";
});
 
app.MapGet("/log-message-limit", async ([FromServices] ILogger<Program> logger) =>
{
    const int LogCount = 10_000;
    const int BatchSize = 10;
 
    for (var i = 0; i < LogCount / BatchSize; i++)
    {
        for (var j = 0; j < BatchSize; j++)
        {
            logger.LogInformation("Log entry {BatchIndex}-{LogEntryIndex}", i, j);
        }
 
        await Task.Delay(100);
    }
 
    return $"Created {LogCount} logs.";
});
 
app.MapGet("/log-message", ([FromServices] ILogger<Program> logger) =>
{
    const string message = "Hello World";
    IReadOnlyList<KeyValuePair<string, object?>> eventData = new List<KeyValuePair<string, object?>>()
    {
        new KeyValuePair<string, object?>("Message", message),
        new KeyValuePair<string, object?>("ActivityId", 123),
        new KeyValuePair<string, object?>("Level", (int) 10),
        new KeyValuePair<string, object?>("Tid", Environment.CurrentManagedThreadId),
        new KeyValuePair<string, object?>("Pid", Environment.ProcessId),
    };
 
    logger.Log(LogLevel.Information, 0, eventData, null, formatter: (_, _) => null!);
 
    return message;
});
 
app.MapGet("/many-logs", (ILoggerFactory loggerFactory, CancellationToken cancellationToken) =>
{
    var channel = Channel.CreateUnbounded<string>();
    var logger = loggerFactory.CreateLogger("ManyLogs");
 
    cancellationToken.Register(() =>
    {
        logger.LogInformation("Writing logs canceled.");
    });
 
    // Write logs for 1 minute.
    _ = Task.Run(async () =>
    {
        var stopwatch = Stopwatch.StartNew();
        var logCount = 0;
        while (stopwatch.Elapsed < TimeSpan.FromMinutes(1))
        {
            cancellationToken.ThrowIfCancellationRequested();
 
            logCount++;
            logger.LogInformation("This is log message {LogCount}.", logCount);
 
            if (logCount % 100 == 0)
            {
                channel.Writer.TryWrite($"Logged {logCount} messages.");
            }
 
            await Task.Delay(5, cancellationToken);
        }
 
        channel.Writer.Complete();
    }, cancellationToken);
 
    return WriteOutput();
 
    async IAsyncEnumerable<string> WriteOutput()
    {
        await foreach (var message in channel.Reader.ReadAllAsync(cancellationToken))
        {
            yield return message;
        }
    }
});
 
app.MapGet("/producer-consumer", async () =>
{
    var producerConsumer = new ProducerConsumer();
 
    await producerConsumer.ProduceAndConsumeAsync(count: 5);
 
    return "Produced and consumed";
});
 
app.MapGet("/log-formatting", (ILoggerFactory loggerFactory) =>
{
    var logger = loggerFactory.CreateLogger("LogAttributes");
 
    // From https://learn.microsoft.com/previous-versions/windows/desktop/ms762271(v=vs.85)
    var xmlLarge = File.ReadAllText(Path.Combine("content", "books.xml"));
 
    var xmlWithComments = @"<hello><!-- world --></hello>";
 
    var xmlWithUrl = new XElement(new XElement("url", "http://localhost:8080")).ToString();
 
    // From https://microsoftedge.github.io/Demos/json-dummy-data/
    var jsonLarge = File.ReadAllText(Path.Combine("content", "example.json"));
 
    var jsonWithComments = @"
// line comment
[
    /* block comment */
    1
]";
 
    var jsonWithUrl = new JsonObject
    {
        ["url"] = "http://localhost:8080"
    }.ToString();
 
    var sb = new StringBuilder();
    for (int i = 0; i < 26; i++)
    {
        var line = new string((char)('a' + i), 256);
        sb.AppendLine(line);
    }
 
    logger.LogInformation(@"XML large content: {XmlLarge}
XML comment content: {XmlComment}
XML URL content: {XmlUrl}
JSON large content: {JsonLarge}
JSON comment content: {JsonComment}
JSON URL content: {JsonUrl}
Long line content: {LongLines}
URL content: {UrlContent}
Empty content: {EmptyContent}
Whitespace content: {WhitespaceContent}
Null content: {NullContent}", xmlLarge, xmlWithComments, xmlWithUrl, jsonLarge, jsonWithComments, jsonWithUrl, sb.ToString(), "http://localhost:8080", "", "   ", null);
 
    return "Log with formatted data";
});
 
app.MapGet("/duplicate-spanid", async () =>
{
    var traceCreator = new TraceCreator();
    var span1 = traceCreator.CreateActivity("Test 1", "0485b1947fe788bb");
    await Task.Delay(1000);
    span1?.Stop();
    var span2 = traceCreator.CreateActivity("Test 2", "0485b1947fe788bb");
    await Task.Delay(1000);
    span2?.Stop();
    return $"Created duplicate span IDs.";
});
 
app.MapGet("/multiple-traces-linked", async () =>
{
    const int TraceCount = 2;
 
    var current = Activity.Current;
    Activity.Current = null;
    var traceCreator = new TraceCreator();
 
    await traceCreator.CreateTraceAsync(count: 1, createChildren: true, rootName: "LinkedTrace1");
    await traceCreator.CreateTraceAsync(count: 1, createChildren: true, rootName: "LinkedTrace2");
    await traceCreator.CreateTraceAsync(count: 1, createChildren: true, rootName: "LinkedTrace3");
 
    Activity.Current = current;
 
    return $"Created {TraceCount} traces.";
});
 
app.Run();