diff --git a/Metrics.cs b/Metrics.cs new file mode 100644 index 0000000..e130c00 --- /dev/null +++ b/Metrics.cs @@ -0,0 +1,67 @@ +using System.Diagnostics.Metrics; + +namespace telemetry; + +public class Metrics +{ + private readonly Counter indexRequestsCount; + private readonly Histogram indexRequestsTime; + private readonly Counter errorCount; + private readonly Counter slowRequestsCount; + + private int totalRequests; + private int errorRequests; + + public Metrics(IMeterFactory meterFactory) + { + var meter = meterFactory.Create(nameof(Metrics)); + + indexRequestsCount = meter.CreateCounter("requests.index.count", "pcs", "Количество запросов"); + indexRequestsTime = meter.CreateHistogram("requests.index.time", "ms", "Время запроса к index"); + + errorCount = meter.CreateCounter( + "requests.index.errors", + "errors", + "Количество ошибок при обработке запроса" + ); + + slowRequestsCount = meter.CreateCounter( + "requests.index.slow", + "requests", + "Количество медленных запросов (более 50ms)" + ); + + meter.CreateObservableGauge( + "requests.index.error_rate", + CalculateErrorRate, + "ratio", + "Доля ошибок среди всех запросов (0-1)" + ); + } + + public void RequestToIndex(TimeSpan elapsed) + { + totalRequests++; + indexRequestsCount.Add(1); + indexRequestsTime.Record(elapsed.TotalMilliseconds); + + if (elapsed.TotalMilliseconds > 50) + { + slowRequestsCount.Add(1); + } + } + + public void RecordError() + { + errorRequests++; + errorCount.Add(1); + } + + private double CalculateErrorRate() + { + if (totalRequests == 0) + return 0; + + return (double)errorRequests / totalRequests; + } +} \ No newline at end of file diff --git a/Pages/Index.cshtml.cs b/Pages/Index.cshtml.cs index 34a599f..5ba2b5f 100644 --- a/Pages/Index.cshtml.cs +++ b/Pages/Index.cshtml.cs @@ -1,19 +1,40 @@ +using System.Diagnostics; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace telemetry.Pages; -public class IndexModel : PageModel +public class IndexModel(ILogger logger, Metrics metrics) : PageModel { - private readonly ILogger _logger; - - public IndexModel(ILogger logger) - { - _logger = logger; - } + private readonly ILogger _logger = logger; public void OnGet() { - + var sw = Stopwatch.StartNew(); + var random = new Random(); + + try + { + for (var i = 0; i < random.Next(0, 100); i++) + { + if (random.Next(100) < 5) + { + throw new Exception("Simulated processing error"); + } + + Console.Write("1"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Ошибка при обработке запроса"); + metrics.RecordError(); + } + finally + { + sw.Stop(); + + metrics.RequestToIndex(sw.Elapsed); + } } -} +} \ No newline at end of file diff --git a/Program.cs b/Program.cs index bc275e4..a56ca19 100644 --- a/Program.cs +++ b/Program.cs @@ -1,7 +1,32 @@ +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using telemetry; + + var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorPages(); +builder.Services.AddSingleton(); +builder.Logging.AddOpenTelemetry(options => +{ + options + .SetResourceBuilder( + ResourceBuilder.CreateDefault() + .AddService("TelemetryExample")) + .AddConsoleExporter(); +}); +builder.Services.AddOpenTelemetry() + .ConfigureResource(resource => resource.AddService("TelemetryExample")) + .WithTracing(tracing => tracing + .AddAspNetCoreInstrumentation() + .AddConsoleExporter()) + .WithMetrics(metrics => metrics + .AddPrometheusExporter() + .AddAspNetCoreInstrumentation() + .AddConsoleExporter().AddMeter(nameof(Metrics))); var app = builder.Build(); @@ -20,6 +45,8 @@ app.UseAuthorization(); +app.UseOpenTelemetryPrometheusScrapingEndpoint(); + app.MapRazorPages(); app.Run(); diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..34b44cc --- /dev/null +++ b/compose.yaml @@ -0,0 +1,12 @@ +services: + prometheus: + image: "prom/prometheus" + ports: + - "9090:9090" + volumes: + - "./prometheus.yml:/etc/prometheus/prometheus.yml" + + grafana: + image: "grafana/grafana-oss" + ports: + - "3000:3000" \ No newline at end of file diff --git a/prometheus.yml b/prometheus.yml new file mode 100644 index 0000000..c6b44a0 --- /dev/null +++ b/prometheus.yml @@ -0,0 +1,6 @@ +scrape_configs: + - job_name: "prometheus" + scrape_interval: 5s + static_configs: + - targets: ["host.docker.internal:5149"] + \ No newline at end of file diff --git a/telemetry.csproj b/telemetry.csproj index 1b28a01..27e0ff0 100644 --- a/telemetry.csproj +++ b/telemetry.csproj @@ -4,6 +4,17 @@ net8.0 enable enable + Linux + + + + + + + + + +