using System.Collections.Concurrent;
using System.Threading.Tasks;
using Serilog.Sinks.SystemConsole;
static readonly ILogger logger = new LoggerConfiguration()
.WriteTo.Async(a => a.Console(theme: AnsiConsoleTheme.Code))
static async Task Main(string[] args)
int numberOfRequests = 100;
var results = new ConcurrentBag<string>();
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
await Parallel.ForEachAsync(Enumerable.Range(0, numberOfRequests), parallelOptions, async (i, token) =>
await Task.Delay(100, token);
results.Add($"Request {i} completed");
logger.Information($"Request {i} completed");
catch (OperationCanceledException ex)
results.Add($"Request {i} cancelled");
logger.Warning(ex, $"Request {i} cancelled");
catch (Exception ex) when (IsTransientException(ex))
results.Add($"Request {i} failed with transient error: {ex.Message}");
logger.Warning(ex, $"Request {i} failed with transient error");
results.Add($"Request {i} failed with critical error: {ex.Message}");
logger.Error(ex, $"Request {i} failed with critical error");
foreach (var result in results)
Console.WriteLine(result);
static bool IsTransientException(Exception ex)
return ex is TimeoutException ||
ex is TaskCanceledException;