diff --git a/ATI.Services.Common/ATI.Services.Common.csproj b/ATI.Services.Common/ATI.Services.Common.csproj index 12a642c..aeb5a12 100644 --- a/ATI.Services.Common/ATI.Services.Common.csproj +++ b/ATI.Services.Common/ATI.Services.Common.csproj @@ -4,7 +4,6 @@ linux-x64 Team Services ATI - 10 true true atisu.services.common @@ -18,7 +17,7 @@ - + @@ -26,7 +25,7 @@ - + diff --git a/ATI.Services.Common/Initializers/MetricsInitializer.cs b/ATI.Services.Common/Initializers/MetricsInitializer.cs index e37e990..e553676 100644 --- a/ATI.Services.Common/Initializers/MetricsInitializer.cs +++ b/ATI.Services.Common/Initializers/MetricsInitializer.cs @@ -25,10 +25,7 @@ public Task InitializeAsync() return Task.CompletedTask; } - if (_metricsOptions.MetricsServiceName != null ) - { - MetricsFactory.Init(_metricsOptions.MetricsServiceName, _metricsOptions.DefaultLongRequestTime); - } + MetricsFactory.Init(_metricsOptions.DefaultLongRequestTime); _initialized = true; return Task.CompletedTask; diff --git a/ATI.Services.Common/Logging/LogSource.cs b/ATI.Services.Common/Logging/LogSource.cs index 5b3c800..0941e86 100644 --- a/ATI.Services.Common/Logging/LogSource.cs +++ b/ATI.Services.Common/Logging/LogSource.cs @@ -13,6 +13,7 @@ public enum LogSource Sql = 4, Controller = 5, Repository = 6, - ExternalHttpClient = 8 + RabbitMq = 9, + Custom = 10 } } diff --git a/ATI.Services.Common/Metrics/ExceptionsMetricsCollector.cs b/ATI.Services.Common/Metrics/ExceptionsMetricsCollector.cs index 0dd4f9e..eb63171 100644 --- a/ATI.Services.Common/Metrics/ExceptionsMetricsCollector.cs +++ b/ATI.Services.Common/Metrics/ExceptionsMetricsCollector.cs @@ -1,59 +1,49 @@ using System; using System.Collections.Concurrent; using System.Diagnostics.Tracing; -using Microsoft.Extensions.Configuration; using Prometheus; -using ConfigurationManager = ATI.Services.Common.Behaviors.ConfigurationManager; -namespace ATI.Services.Common.Metrics -{ - public class ExceptionsMetricsCollector : EventListener - { - // Да, даже под linux - Microsoft-Windows - private const string ExceptionSourceName = "Microsoft-Windows-DotNETRuntime"; - private const int ExceptionBit = 0x8000; - private const int ExceptionNameIndex = 0; - private readonly ConcurrentDictionary _exceptionCounters = new(); - private MetricFactory _metricFactory; - private const string ExceptionsMetricName = "Exceptions"; - private Gauge _gauge; +namespace ATI.Services.Common.Metrics; - private string ServiceName { get; } - public ExceptionsMetricsCollector() - { - ServiceName = ConfigurationManager.GetSection(nameof(MetricsOptions)).Get().MetricsServiceName; - } +public class ExceptionsMetricsCollector : EventListener +{ + // Да, даже под linux - Microsoft-Windows + private const string ExceptionSourceName = "Microsoft-Windows-DotNETRuntime"; + private const int ExceptionBit = 0x8000; + private const int ExceptionNameIndex = 0; + private readonly ConcurrentDictionary _exceptionCounters = new(); + private MetricFactory _metricFactory; + private Gauge _gauge; - protected override void OnEventSourceCreated(EventSource eventSource) - { - if (eventSource.Name != ExceptionSourceName) - return; + protected override void OnEventSourceCreated(EventSource eventSource) + { + if (eventSource.Name != ExceptionSourceName) + return; - EnableEvents( - eventSource, - EventLevel.Error, - (EventKeywords) ExceptionBit); - } + EnableEvents( + eventSource, + EventLevel.Error, + (EventKeywords) ExceptionBit); + } - protected override void OnEventWritten(EventWrittenEventArgs eventData) - { - var exceptionType = eventData.Payload[ExceptionNameIndex].ToString(); - _exceptionCounters.AddOrUpdate(exceptionType, _ => 1, (_, oldValue) => oldValue + 1); - } + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + var exceptionType = eventData.Payload[ExceptionNameIndex].ToString(); + _exceptionCounters.AddOrUpdate(exceptionType, _ => 1, (_, oldValue) => oldValue + 1); + } - public void RegisterMetrics(CollectorRegistry registry) - { - _metricFactory = Prometheus.Metrics.WithCustomRegistry(registry); - _gauge = _metricFactory.CreateGauge(ServiceName + ExceptionsMetricName, "", "machine_name", "exception_type"); - } + public void RegisterMetrics(CollectorRegistry registry) + { + _metricFactory = Prometheus.Metrics.WithCustomRegistry(registry); + _gauge = _metricFactory.CreateGauge($"{MetricsFactory.Prefix}_Exceptions", "", "machine_name", "exception_type"); + } - public void UpdateMetrics() + public void UpdateMetrics() + { + foreach (var exceptionType in _exceptionCounters.Keys) { - foreach (var exceptionType in _exceptionCounters.Keys) - { - _exceptionCounters.TryRemove(exceptionType, out var count); - _gauge.WithLabels(Environment.MachineName, exceptionType).Set(count); - } + _exceptionCounters.TryRemove(exceptionType, out var count); + _gauge.WithLabels(Environment.MachineName, exceptionType).Set(count); } } } \ No newline at end of file diff --git a/ATI.Services.Common/Metrics/HttpWrapper/MetricsHttpClientWrapper.cs b/ATI.Services.Common/Metrics/HttpWrapper/MetricsHttpClientWrapper.cs index 07eb715..13bfab7 100644 --- a/ATI.Services.Common/Metrics/HttpWrapper/MetricsHttpClientWrapper.cs +++ b/ATI.Services.Common/Metrics/HttpWrapper/MetricsHttpClientWrapper.cs @@ -15,537 +15,525 @@ using JetBrains.Annotations; using NLog; -namespace ATI.Services.Common.Metrics.HttpWrapper +namespace ATI.Services.Common.Metrics.HttpWrapper; + +/// +/// Для удобства лучше использовать ConsulMetricsHttpClientWrapper из ATI.Services.Consul +/// Он внутри себя инкапсулирует ConsulServiceAddress и MetricsFactory +/// +[PublicAPI] +public class MetricsHttpClientWrapper { - /// - /// Для удобства лучше использовать ConsulMetricsHttpClientWrapper из ATI.Services.Consul - /// Он внутри себя инкапсулирует ConsulServiceAddress и MetricsFactory - /// - [PublicAPI] - public class MetricsHttpClientWrapper + private readonly ILogger _logger; + private readonly HttpClient _httpClient; + private readonly Func _logLevelOverride; + + private const string LogMessageTemplate = + "Сервис:{0} в ответ на запрос [HTTP {1} {2}] вернул ответ с статус кодом {3}."; + + public MetricsHttpClientWrapper(MetricsHttpClientConfig config) { - private readonly ILogger _logger; - private readonly HttpClient _httpClient; - private readonly MetricsFactory _metricsFactory; - private readonly Func _logLevelOverride; + Config = config; + _logger = LogManager.GetLogger(Config.ServiceName); + _httpClient = CreateHttpClient(config.Headers, config.PropagateActivity); + _logLevelOverride = Config.LogLevelOverride; + } - private const string LogMessageTemplate = - "Сервис:{0} в ответ на запрос [HTTP {1} {2}] вернул ответ с статус кодом {3}."; + public MetricsHttpClientConfig Config { get; } - public MetricsHttpClientWrapper(MetricsHttpClientConfig config) + private HttpClient CreateHttpClient(Dictionary additionalHeaders, bool propagateActivity = true) + { + HttpClient httpClient; + if (propagateActivity) { - Config = config; - _logger = LogManager.GetLogger(Config.ServiceName); - _httpClient = CreateHttpClient(config.Headers, config.PropagateActivity); - _metricsFactory = MetricsFactory.CreateExternalHttpMetricsFactory(); - _logLevelOverride = Config.LogLevelOverride; + httpClient = new HttpClient(); } - - public MetricsHttpClientConfig Config { get; } - - private HttpClient CreateHttpClient(Dictionary additionalHeaders, bool propagateActivity = true) + else { - HttpClient httpClient; - if (propagateActivity) - { - httpClient = new HttpClient(); - } - else + httpClient = new HttpClient(new SocketsHttpHandler { - httpClient = new HttpClient(new SocketsHttpHandler - { - ActivityHeadersPropagator = DistributedContextPropagator.CreateNoOutputPropagator() - }); - } + ActivityHeadersPropagator = DistributedContextPropagator.CreateNoOutputPropagator() + }); + } - httpClient.Timeout = Config.Timeout; - httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - if (!ServiceVariables.ServiceAsClientName.IsNullOrEmpty() && - !ServiceVariables.ServiceAsClientHeaderName.IsNullOrEmpty()) - { - httpClient.DefaultRequestHeaders.Add( - ServiceVariables.ServiceAsClientHeaderName, - ServiceVariables.ServiceAsClientName); - } + httpClient.Timeout = Config.Timeout; + httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + if (!ServiceVariables.ServiceAsClientName.IsNullOrEmpty() && + !ServiceVariables.ServiceAsClientHeaderName.IsNullOrEmpty()) + { + httpClient.DefaultRequestHeaders.Add( + ServiceVariables.ServiceAsClientHeaderName, + ServiceVariables.ServiceAsClientName); + } - if (additionalHeaders != null && additionalHeaders.Count > 0) + if (additionalHeaders != null && additionalHeaders.Count > 0) + { + foreach (var header in additionalHeaders) { - foreach (var header in additionalHeaders) - { - httpClient.DefaultRequestHeaders.Add(header.Key, header.Value); - } + httpClient.DefaultRequestHeaders.Add(header.Key, header.Value); } - - return httpClient; } + return httpClient; + } - public async Task> GetAsync(Uri fullUrl, string metricName, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Get, fullUrl, headers)); + public async Task> GetAsync(Uri fullUrl, string metricName, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Get, fullUrl, headers)); - public async Task> GetAsync(string serviceAddress, string metricName, - string url, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Get, FullUri(serviceAddress, url), headers)); + public async Task> GetAsync(string serviceAddress, string metricName, + string url, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Get, FullUri(serviceAddress, url), headers)); - public async Task> GetAsync(string serviceAddress, string metricName, string url, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Get, FullUri(serviceAddress, url), headers)); + public async Task> GetAsync(string serviceAddress, string metricName, string url, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Get, FullUri(serviceAddress, url), headers)); - public async Task> GetAsync(Uri fullUrl, string metricName, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Get, fullUrl, headers)); + public async Task> GetAsync(Uri fullUrl, string metricName, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Get, fullUrl, headers)); - public async Task> PostAsync(string serviceAddress, string metricName, - string url, TModel model, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers) - { - Content = Config.Serializer.Serialize(model) - }); + public async Task> PostAsync(string serviceAddress, string metricName, + string url, TModel model, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers) + { + Content = Config.Serializer.Serialize(model) + }); - public async Task> PostAsync(Uri fullUrl, string metricName, - TModel model, Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUrl, headers) - { - Content = Config.Serializer.Serialize(model) - }); + public async Task> PostAsync(Uri fullUrl, string metricName, + TModel model, Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUrl, headers) + { + Content = Config.Serializer.Serialize(model) + }); - public async Task> PostAsync(string serviceAddress, string metricName, - string url, TModel model, Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers) - { - Content = Config.Serializer.Serialize(model) - }); + public async Task> PostAsync(string serviceAddress, string metricName, + string url, TModel model, Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers) + { + Content = Config.Serializer.Serialize(model) + }); - public async Task> PostAsync(Uri fullUrl, string metricName, TModel model, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUrl, headers) - { - Content = Config.Serializer.Serialize(model) - }); + public async Task> PostAsync(Uri fullUrl, string metricName, TModel model, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUrl, headers) + { + Content = Config.Serializer.Serialize(model) + }); - public async Task> PostAsync(string serviceAddress, string metricName, - string url, string rawContent, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers) - { - Content = rawContent - }); + public async Task> PostAsync(string serviceAddress, string metricName, + string url, string rawContent, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers) + { + Content = rawContent + }); - public async Task> PostAsync(Uri fullUrl, string metricName, - string rawContent, Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUrl, headers) - { - Content = rawContent - }); + public async Task> PostAsync(Uri fullUrl, string metricName, + string rawContent, Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUrl, headers) + { + Content = rawContent + }); - public async Task> PostAsync(string serviceAddress, string metricName, - string url, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers)); + public async Task> PostAsync(string serviceAddress, string metricName, + string url, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers)); - public async Task> PostAsync(Uri fullUrl, string metricName, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUrl, headers)); + public async Task> PostAsync(Uri fullUrl, string metricName, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUrl, headers)); - public async Task> PostAsync(string serviceAddress, string metricName, string url, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers)); + public async Task> PostAsync(string serviceAddress, string metricName, string url, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers)); - public async Task> PostAsync(Uri fullUri, string metricName, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUri, headers)); + public async Task> PostAsync(Uri fullUri, string metricName, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUri, headers)); - public async Task> PostAsync(string serviceAddress, string metricName, string url, - string rawContent, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers) { Content = rawContent }); + public async Task> PostAsync(string serviceAddress, string metricName, string url, + string rawContent, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Post, FullUri(serviceAddress, url), headers) { Content = rawContent }); - public async Task> PostAsync(Uri fullUri, string metricName, string rawContent, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUri, headers) { Content = rawContent }); + public async Task> PostAsync(Uri fullUri, string metricName, string rawContent, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Post, fullUri, headers) { Content = rawContent }); - public async Task> PutAsync(string serviceAddress, string metricName, - string url, TModel model, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Put, FullUri(serviceAddress, url), headers) - { - Content = Config.Serializer.Serialize(model) - }); + public async Task> PutAsync(string serviceAddress, string metricName, + string url, TModel model, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Put, FullUri(serviceAddress, url), headers) + { + Content = Config.Serializer.Serialize(model) + }); - public async Task> PutAsync(Uri fullUri, string metricName, - TModel model, Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Put, fullUri, headers) - { - Content = Config.Serializer.Serialize(model) - }); + public async Task> PutAsync(Uri fullUri, string metricName, + TModel model, Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Put, fullUri, headers) + { + Content = Config.Serializer.Serialize(model) + }); - public async Task> PutAsync(string serviceAddress, string metricName, - string url, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Put, FullUri(serviceAddress, url), headers)); + public async Task> PutAsync(string serviceAddress, string metricName, + string url, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Put, FullUri(serviceAddress, url), headers)); - public async Task> PutAsync(Uri fullUri, string metricName, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Put, fullUri, headers)); + public async Task> PutAsync(Uri fullUri, string metricName, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Put, fullUri, headers)); - public async Task> PutAsync(string serviceAddress, string metricName, string url, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Put, FullUri(serviceAddress, url), headers)); + public async Task> PutAsync(string serviceAddress, string metricName, string url, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Put, FullUri(serviceAddress, url), headers)); - public async Task> PutAsync(Uri fullUri, string metricName, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Put, fullUri, headers)); + public async Task> PutAsync(Uri fullUri, string metricName, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Put, fullUri, headers)); - public async Task> DeleteAsync(string serviceAddress, - string metricName, string url, TModel model, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Delete, FullUri(serviceAddress, url), headers) - { - Content = Config.Serializer.Serialize(model) - }); + public async Task> DeleteAsync(string serviceAddress, + string metricName, string url, TModel model, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Delete, FullUri(serviceAddress, url), headers) + { + Content = Config.Serializer.Serialize(model) + }); - public async Task> DeleteAsync(string serviceAddress, string metricName, - string url, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Delete, FullUri(serviceAddress, url), headers)); - public async Task> DeleteAsync(Uri fullUri, string metricName, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Delete, fullUri, headers)); + public async Task> DeleteAsync(string serviceAddress, string metricName, + string url, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Delete, FullUri(serviceAddress, url), headers)); + public async Task> DeleteAsync(Uri fullUri, string metricName, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Delete, fullUri, headers)); - public async Task> DeleteAsync(string serviceAddress, string metricName, string url, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Delete, FullUri(serviceAddress, url), headers)); + public async Task> DeleteAsync(string serviceAddress, string metricName, string url, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Delete, FullUri(serviceAddress, url), headers)); - public async Task> DeleteAsync(Uri fullUri, string metricName, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Delete, fullUri, headers)); + public async Task> DeleteAsync(Uri fullUri, string metricName, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Delete, fullUri, headers)); - public async Task> PatchAsync(string serviceAddress, - string metricName, - string url, TModel model, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Patch, FullUri(serviceAddress, url), headers) - { - Content = Config.Serializer.Serialize(model) - }); + public async Task> PatchAsync(string serviceAddress, + string metricName, + string url, TModel model, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Patch, FullUri(serviceAddress, url), headers) + { + Content = Config.Serializer.Serialize(model) + }); - public async Task> PatchAsync(Uri fullUri, string metricName, - TModel model, Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Patch, fullUri, headers) - { - Content = Config.Serializer.Serialize(model) - }); + public async Task> PatchAsync(Uri fullUri, string metricName, + TModel model, Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Patch, fullUri, headers) + { + Content = Config.Serializer.Serialize(model) + }); - public async Task> PatchAsync(string serviceAddress, string metricName, - string url, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Patch, FullUri(serviceAddress, url), headers)); + public async Task> PatchAsync(string serviceAddress, string metricName, + string url, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Patch, FullUri(serviceAddress, url), headers)); - public async Task> PatchAsync(Uri fullUri, string metricName, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Patch, fullUri, headers)); + public async Task> PatchAsync(Uri fullUri, string metricName, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Patch, fullUri, headers)); - public async Task> PatchAsync(string serviceAddress, string metricName, - string url, TModel model, Dictionary headers = null) - => await SendAsync(metricName, - new HttpMessage(HttpMethod.Patch, FullUri(serviceAddress, url), headers) - { - Content = Config.Serializer.Serialize(model) - }); + public async Task> PatchAsync(string serviceAddress, string metricName, + string url, TModel model, Dictionary headers = null) + => await SendAsync(metricName, + new HttpMessage(HttpMethod.Patch, FullUri(serviceAddress, url), headers) + { + Content = Config.Serializer.Serialize(model) + }); - public async Task> PatchAsync(Uri fullUri, string metricName, - TModel model, Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Patch, fullUri, headers) - { - Content = Config.Serializer.Serialize(model) - }); + public async Task> PatchAsync(Uri fullUri, string metricName, + TModel model, Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Patch, fullUri, headers) + { + Content = Config.Serializer.Serialize(model) + }); - public async Task> PatchAsync(string serviceAddress, string metricName, string url, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Patch, FullUri(serviceAddress, url), headers)); + public async Task> PatchAsync(string serviceAddress, string metricName, string url, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Patch, FullUri(serviceAddress, url), headers)); - public async Task> PatchAsync(Uri fullUri, string metricName, - Dictionary headers = null) - => await SendAsync(metricName, new HttpMessage(HttpMethod.Patch, fullUri, headers)); + public async Task> PatchAsync(Uri fullUri, string metricName, + Dictionary headers = null) + => await SendAsync(metricName, new HttpMessage(HttpMethod.Patch, fullUri, headers)); - public async Task>> SendAsync(Uri fullUri, - string metricName, TModel model, - Dictionary headers = null, - HttpMethod method = null) + + public async Task>> SendAsync(Uri fullUri, + string metricName, TModel model, + Dictionary headers = null, + HttpMethod method = null) + { + try { - try + if (fullUri == null) + return new OperationResult>(ActionStatus.InternalServerError, + "Адрес сообщения не указан (fullUri==null)"); + + var message = new HttpMessage(method ?? HttpMethod.Put, fullUri, headers) { - if (fullUri == null) - return new OperationResult>(ActionStatus.InternalServerError, - "Адрес сообщения не указан (fullUri==null)"); + Content = model != null ? Config.Serializer.Serialize(model) : "" + }; - var message = new HttpMessage(method ?? HttpMethod.Put, fullUri, headers) - { - Content = model != null ? Config.Serializer.Serialize(model) : "" - }; + using var requestMessage = message.ToRequestMessage(Config); - using (_metricsFactory.CreateMetricsTimer(metricName, message.Content)) - { - using var requestMessage = message.ToRequestMessage(Config); - - using var responseMessage = await _httpClient.SendAsync(requestMessage); - var responseContent = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); - - if (!responseMessage.IsSuccessStatusCode) - { - var logMessage = string.Format(LogMessageTemplate, Config.ServiceName, message.Method, - message.FullUri, responseMessage.StatusCode); - - var logLevel = responseMessage.StatusCode == HttpStatusCode.InternalServerError - ? _logLevelOverride(LogLevel.Error) - : _logLevelOverride(LogLevel.Warn); - _logger.LogWithObject(logLevel, ex: null, logMessage, logObjects: responseContent); - } - - var result = new HttpResponseMessage - { - StatusCode = responseMessage.StatusCode, - Headers = responseMessage.Headers, - TrailingHeaders = responseMessage.TrailingHeaders, - ReasonPhrase = responseMessage.ReasonPhrase, - Version = responseMessage.Version, - RawContent = responseContent - }; - - try - { - result.Content = !string.IsNullOrEmpty(result.RawContent) - ? Config.Serializer.Deserialize(result.RawContent) - : default; - } - catch (TaskCanceledException e) when (e.InnerException is TimeoutException) - { - _logger.LogWithObject(_logLevelOverride(LogLevel.Warn), - e, - logObjects: new - { - MetricName = metricName, FullUri = fullUri, Model = model, - Headers = headers - }); - return new OperationResult>(ActionStatus.Timeout); - } - catch (Exception e) - { - _logger.LogWithObject(_logLevelOverride(LogLevel.Error), - e, - logObjects: - new - { - MetricName = metricName, FullUri = fullUri, Model = model, - Headers = headers, - ResponseBody = result.RawContent - }); - } - - return new(result, OperationResult.GetActionStatusByHttpStatusCode(result.StatusCode)); - } + using var responseMessage = await _httpClient.SendAsync(requestMessage); + var responseContent = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); + + if (!responseMessage.IsSuccessStatusCode) + { + var logMessage = string.Format(LogMessageTemplate, Config.ServiceName, message.Method, + message.FullUri, responseMessage.StatusCode); + + var logLevel = responseMessage.StatusCode == HttpStatusCode.InternalServerError + ? _logLevelOverride(LogLevel.Error) + : _logLevelOverride(LogLevel.Warn); + _logger.LogWithObject(logLevel, ex: null, logMessage, logObjects: responseContent); + } + + var result = new HttpResponseMessage + { + StatusCode = responseMessage.StatusCode, + Headers = responseMessage.Headers, + TrailingHeaders = responseMessage.TrailingHeaders, + ReasonPhrase = responseMessage.ReasonPhrase, + Version = responseMessage.Version, + RawContent = responseContent + }; + + try + { + result.Content = !string.IsNullOrEmpty(result.RawContent) + ? Config.Serializer.Deserialize(result.RawContent) + : default; + } + catch (TaskCanceledException e) when (e.InnerException is TimeoutException) + { + _logger.LogWithObject(_logLevelOverride(LogLevel.Warn), + e, + logObjects: new + { + MetricName = metricName, FullUri = fullUri, Model = model, + Headers = headers + }); + return new OperationResult>(ActionStatus.Timeout); } catch (Exception e) { _logger.LogWithObject(_logLevelOverride(LogLevel.Error), - e, - logObjects: new { MetricName = metricName, FullUri = fullUri, Model = model, Headers = headers }); - return new OperationResult>(e); + e, + logObjects: + new + { + MetricName = metricName, FullUri = fullUri, Model = model, + Headers = headers, + ResponseBody = result.RawContent + }); } + + return new(result, OperationResult.GetActionStatusByHttpStatusCode(result.StatusCode)); + } + catch (Exception e) + { + _logger.LogWithObject(_logLevelOverride(LogLevel.Error), + e, + logObjects: new { MetricName = metricName, FullUri = fullUri, Model = model, Headers = headers }); + return new OperationResult>(e); } + } - private async Task> SendAsync(string methodName, HttpMessage message) + private async Task> SendAsync(string methodName, HttpMessage message) + { + try { - using (_metricsFactory.CreateMetricsTimer(methodName, message.Content)) - { - try - { - if (message.FullUri == null) - return new OperationResult(ActionStatus.InternalServerError, - "Адрес сообщения не указан (message.FullUri==null)"); + if (message.FullUri == null) + return new OperationResult(ActionStatus.InternalServerError, + "Адрес сообщения не указан (message.FullUri==null)"); - using var requestMessage = message.ToRequestMessage(Config); + using var requestMessage = message.ToRequestMessage(Config); - using var responseMessage = await _httpClient.SendAsync(requestMessage); + using var responseMessage = await _httpClient.SendAsync(requestMessage); - if (responseMessage.IsSuccessStatusCode) - { - var stream = await responseMessage.Content.ReadAsStreamAsync(); - var result = await Config.Serializer.DeserializeAsync(stream); - return new OperationResult(result, OperationResult.GetActionStatusByHttpStatusCode(responseMessage.StatusCode)); - } + if (responseMessage.IsSuccessStatusCode) + { + var stream = await responseMessage.Content.ReadAsStreamAsync(); + var result = await Config.Serializer.DeserializeAsync(stream); + return new OperationResult(result, OperationResult.GetActionStatusByHttpStatusCode(responseMessage.StatusCode)); + } - var logMessage = string.Format(LogMessageTemplate, Config.ServiceName, message.Method, - message.FullUri, responseMessage.StatusCode); - var responseContent = await responseMessage.Content.ReadAsStringAsync(); + var logMessage = string.Format(LogMessageTemplate, Config.ServiceName, message.Method, + message.FullUri, responseMessage.StatusCode); + var responseContent = await responseMessage.Content.ReadAsStringAsync(); - var logLevel = responseMessage.StatusCode == HttpStatusCode.InternalServerError - ? _logLevelOverride(LogLevel.Error) - : _logLevelOverride(LogLevel.Warn); - _logger.LogWithObject(logLevel, ex: null, logMessage, logObjects: responseContent); + var logLevel = responseMessage.StatusCode == HttpStatusCode.InternalServerError + ? _logLevelOverride(LogLevel.Error) + : _logLevelOverride(LogLevel.Warn); + _logger.LogWithObject(logLevel, ex: null, logMessage, logObjects: responseContent); - return new OperationResult(OperationResult.GetActionStatusByHttpStatusCode(responseMessage.StatusCode)); - } - catch (TaskCanceledException e) when (e.InnerException is TimeoutException) - { - _logger.LogWithObject(_logLevelOverride(LogLevel.Error), - e, - logObjects: new { Method = methodName, Message = message }); - return new OperationResult(ActionStatus.Timeout); - } - catch (Exception e) - { - _logger.LogWithObject(_logLevelOverride(LogLevel.Error), - e, - logObjects: new { Method = methodName, Message = message }); - return new OperationResult(e); - } - } + return new OperationResult(OperationResult.GetActionStatusByHttpStatusCode(responseMessage.StatusCode)); + } + catch (TaskCanceledException e) when (e.InnerException is TimeoutException) + { + _logger.LogWithObject(_logLevelOverride(LogLevel.Error), + e, + logObjects: new { Method = methodName, Message = message }); + return new OperationResult(ActionStatus.Timeout); } + catch (Exception e) + { + _logger.LogWithObject(_logLevelOverride(LogLevel.Error), + e, + logObjects: new { Method = methodName, Message = message }); + return new OperationResult(e); + } + } - private async Task> SendAsync(string methodName, HttpMessage message) + private async Task> SendAsync(string methodName, HttpMessage message) + { + try { - using (_metricsFactory.CreateMetricsTimer(methodName, message.Content)) - { - try - { - if (message.FullUri == null) - return new OperationResult(ActionStatus.InternalServerError); + if (message.FullUri == null) + return new OperationResult(ActionStatus.InternalServerError); - using var requestMessage = message.ToRequestMessage(Config); + using var requestMessage = message.ToRequestMessage(Config); - using var responseMessage = await _httpClient.SendAsync(requestMessage); - var responseContent = await responseMessage.Content.ReadAsStringAsync(); + using var responseMessage = await _httpClient.SendAsync(requestMessage); + var responseContent = await responseMessage.Content.ReadAsStringAsync(); - if (responseMessage.IsSuccessStatusCode) - return new OperationResult(responseContent, OperationResult.GetActionStatusByHttpStatusCode(responseMessage.StatusCode)); + if (responseMessage.IsSuccessStatusCode) + return new OperationResult(responseContent, OperationResult.GetActionStatusByHttpStatusCode(responseMessage.StatusCode)); - var logMessage = string.Format(LogMessageTemplate, Config.ServiceName, message.Method, - message.FullUri, responseMessage.StatusCode); + var logMessage = string.Format(LogMessageTemplate, Config.ServiceName, message.Method, + message.FullUri, responseMessage.StatusCode); - var logLevel = responseMessage.StatusCode == HttpStatusCode.InternalServerError - ? _logLevelOverride(LogLevel.Error) - : _logLevelOverride(LogLevel.Warn); - _logger.LogWithObject(logLevel, ex: null, logMessage, logObjects: responseContent); + var logLevel = responseMessage.StatusCode == HttpStatusCode.InternalServerError + ? _logLevelOverride(LogLevel.Error) + : _logLevelOverride(LogLevel.Warn); + _logger.LogWithObject(logLevel, ex: null, logMessage, logObjects: responseContent); - return new OperationResult(responseContent, - OperationResult.GetActionStatusByHttpStatusCode(responseMessage.StatusCode)); - } - catch (TaskCanceledException e) when (e.InnerException is TimeoutException) - { - _logger.LogWithObject(_logLevelOverride(LogLevel.Error), - e, - logObjects: new { Method = methodName, Message = message }); - return new OperationResult(ActionStatus.Timeout); - } - catch (Exception e) - { - _logger.LogWithObject(_logLevelOverride(LogLevel.Error), - e, - logObjects: new { Method = methodName, Message = message }); - return new OperationResult(e); - } - } + return new OperationResult(responseContent, + OperationResult.GetActionStatusByHttpStatusCode(responseMessage.StatusCode)); + } + catch (TaskCanceledException e) when (e.InnerException is TimeoutException) + { + _logger.LogWithObject(_logLevelOverride(LogLevel.Error), + e, + logObjects: new { Method = methodName, Message = message }); + return new OperationResult(ActionStatus.Timeout); } + catch (Exception e) + { + _logger.LogWithObject(_logLevelOverride(LogLevel.Error), + e, + logObjects: new { Method = methodName, Message = message }); + return new OperationResult(e); + } + } - private Uri FullUri(string serviceAddress, string url) => - serviceAddress != null ? new Uri(new Uri(serviceAddress), url ?? "") : null; + private Uri FullUri(string serviceAddress, string url) => + serviceAddress != null ? new Uri(new Uri(serviceAddress), url ?? "") : null; - private class HttpMessage - { - private readonly string ContentTypeHeaderName = "Content-Type"; + private class HttpMessage + { + private readonly string ContentTypeHeaderName = "Content-Type"; - public HttpMessage(HttpMethod method, Uri fullUri, Dictionary headers) - { - Method = method; - FullUri = fullUri; - Headers = new Dictionary(); - ContentType = "application/json"; + public HttpMessage(HttpMethod method, Uri fullUri, Dictionary headers) + { + Method = method; + FullUri = fullUri; + Headers = new Dictionary(); + ContentType = "application/json"; - if (headers == null) - return; + if (headers == null) + return; - foreach (var header in headers) + foreach (var header in headers) + { + if (string.Equals(header.Key, ContentTypeHeaderName, + StringComparison.InvariantCultureIgnoreCase)) { - if (string.Equals(header.Key, ContentTypeHeaderName, - StringComparison.InvariantCultureIgnoreCase)) - { - ContentType = header.Value; - } - else - { - Headers.Add(header.Key, header.Value); - } + ContentType = header.Value; + } + else + { + Headers.Add(header.Key, header.Value); } } + } - public HttpMethod Method { get; } - public string Content { get; init; } - public Uri FullUri { get; } - public Dictionary Headers { get; } - private string ContentType { get; } - - internal HttpRequestMessage ToRequestMessage(MetricsHttpClientConfig config) - { - var msg = new HttpRequestMessage(Method, FullUri); + public HttpMethod Method { get; } + public string Content { get; init; } + public Uri FullUri { get; } + public Dictionary Headers { get; } + private string ContentType { get; } - if (config.HeadersToProxy.Count != 0) - Headers.AddRange(AppHttpContext.HeadersAndValuesToProxy(config.HeadersToProxy)); + internal HttpRequestMessage ToRequestMessage(MetricsHttpClientConfig config) + { + var msg = new HttpRequestMessage(Method, FullUri); - foreach (var header in Headers) - msg.Headers.TryAddWithoutValidation(header.Key, header.Value); + if (config.HeadersToProxy.Count != 0) + Headers.AddRange(AppHttpContext.HeadersAndValuesToProxy(config.HeadersToProxy)); - string acceptLanguage; - if (config.AddCultureToRequest - && (acceptLanguage = FlowContext.Current.AcceptLanguage) != null) - msg.Headers.Add("Accept-Language", acceptLanguage); + foreach (var header in Headers) + msg.Headers.TryAddWithoutValidation(header.Key, header.Value); + string acceptLanguage; + if (config.AddCultureToRequest + && (acceptLanguage = FlowContext.Current.AcceptLanguage) != null) + msg.Headers.Add("Accept-Language", acceptLanguage); - if (string.IsNullOrEmpty(Content) == false) - { - msg.Content = new StringContent(Content, Encoding.UTF8, ContentType); - } - return msg; + if (string.IsNullOrEmpty(Content) == false) + { + msg.Content = new StringContent(Content, Encoding.UTF8, ContentType); } + + return msg; } } } \ No newline at end of file diff --git a/ATI.Services.Common/Metrics/MeasureAttribute.cs b/ATI.Services.Common/Metrics/MeasureAttribute.cs index 84b8976..0dec142 100644 --- a/ATI.Services.Common/Metrics/MeasureAttribute.cs +++ b/ATI.Services.Common/Metrics/MeasureAttribute.cs @@ -1,91 +1,69 @@ using System; using System.Collections.Concurrent; using System.Threading.Tasks; -using ATI.Services.Common.Behaviors; using JetBrains.Annotations; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; -namespace ATI.Services.Common.Metrics -{ - [PublicAPI] - public class MeasureAttribute : ActionFilterAttribute - { - private string _metricEntity; - private readonly TimeSpan? _longRequestTime; - private readonly bool _longRequestLoggingEnabled = true; - - private static readonly ConcurrentDictionary ControllerNameMetricsFactories - = new(); +namespace ATI.Services.Common.Metrics; - public MeasureAttribute() - { - } - - public MeasureAttribute(string metricEntity) - { - _metricEntity = metricEntity; - } +[PublicAPI] +public class MeasureAttribute : ActionFilterAttribute +{ + private string _metricEntity; + private readonly TimeSpan? _longRequestTime; + private readonly bool _longRequestLoggingEnabled = true; - /// Имя метрики - /// Время ответа после которого запрос считается достаточно долгим для логирования. В секундах - public MeasureAttribute(string metricEntity, double longRequestTimeSeconds) : this(metricEntity) - { - _longRequestTime = TimeSpan.FromSeconds(longRequestTimeSeconds); - } + private static readonly ConcurrentDictionary ControllerNameMetricsFactories + = new(); - public MeasureAttribute(string metricEntity, bool longRequestLoggingEnabled) : this(metricEntity) - { - _longRequestLoggingEnabled = longRequestLoggingEnabled; - } + public MeasureAttribute() { } - public MeasureAttribute(double longRequestTimeSeconds) - { - _longRequestTime = TimeSpan.FromSeconds(longRequestTimeSeconds); - } + public MeasureAttribute(string metricEntity) => _metricEntity = metricEntity; - public MeasureAttribute(bool longRequestLoggingEnabled) - { - _longRequestLoggingEnabled = longRequestLoggingEnabled; - } + /// Имя метрики + /// Время ответа после которого запрос считается достаточно долгим для логирования. В секундах + public MeasureAttribute(string metricEntity, double longRequestTimeSeconds) : this(metricEntity) => + _longRequestTime = TimeSpan.FromSeconds(longRequestTimeSeconds); - public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) - { - var controllerActionDescriptor = (ControllerActionDescriptor)context.ActionDescriptor; - var actionName = - $"{context.HttpContext.Request.Method}:{controllerActionDescriptor.AttributeRouteInfo.Template}"; - var controllerName = controllerActionDescriptor.ControllerName + "Controller"; + public MeasureAttribute(string metricEntity, bool longRequestLoggingEnabled) : this(metricEntity) => + _longRequestLoggingEnabled = longRequestLoggingEnabled; - if (string.IsNullOrEmpty(_metricEntity)) - { - _metricEntity = controllerActionDescriptor.ControllerName; - } + public MeasureAttribute(double longRequestTimeSeconds) => + _longRequestTime = TimeSpan.FromSeconds(longRequestTimeSeconds); - var metricsFactory = - ControllerNameMetricsFactories.GetOrAdd( - controllerName, - MetricsFactory.CreateControllerMetricsFactory(controllerName, "client_header_name")); + public MeasureAttribute(bool longRequestLoggingEnabled) + { + _longRequestLoggingEnabled = longRequestLoggingEnabled; + } - var serviceName = "Unknown"; - - if (context.HttpContext.Items.TryGetValue(CommonBehavior.ServiceNameItemKey, out var serviceNameValue)) - { - serviceName = serviceNameValue as string; - } + public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + var controllerActionDescriptor = (ControllerActionDescriptor)context.ActionDescriptor; + var actionName = + $"{context.HttpContext.Request.Method}:{controllerActionDescriptor.AttributeRouteInfo.Template}"; + var controllerName = controllerActionDescriptor.ControllerName + "Controller"; - using (CreateTimer(context, metricsFactory, actionName, serviceName)) - { - await next.Invoke(); - } + if (string.IsNullOrEmpty(_metricEntity)) + { + _metricEntity = controllerActionDescriptor.ControllerName; } - private IDisposable CreateTimer(ActionExecutingContext context, MetricsFactory metricsFactory, - string actionName, string serviceName) + var metricsFactory = + ControllerNameMetricsFactories.GetOrAdd( + controllerName, + MetricsFactory.CreateControllerMetricsFactory(controllerName)); + + using (CreateTimer(context, metricsFactory, actionName)) { - return _longRequestLoggingEnabled - ? metricsFactory.CreateLoggingMetricsTimer(_metricEntity, actionName, context.ActionArguments, - _longRequestTime, serviceName) - : metricsFactory.CreateMetricsTimer(_metricEntity, actionName, serviceName); + await next.Invoke(); } } + + private IDisposable CreateTimer(ActionExecutingContext context, MetricsFactory metricsFactory, string actionName) + { + return _longRequestLoggingEnabled + ? metricsFactory.CreateLoggingMetricsTimer(_metricEntity, actionName, context.ActionArguments, _longRequestTime) + : metricsFactory.CreateMetricsTimer(_metricEntity, actionName); + } } \ No newline at end of file diff --git a/ATI.Services.Common/Metrics/MetricsExtensions.cs b/ATI.Services.Common/Metrics/MetricsExtensions.cs index 42288da..2c6acb2 100644 --- a/ATI.Services.Common/Metrics/MetricsExtensions.cs +++ b/ATI.Services.Common/Metrics/MetricsExtensions.cs @@ -8,35 +8,39 @@ using Microsoft.Extensions.DependencyInjection; using ConfigurationManager = ATI.Services.Common.Behaviors.ConfigurationManager; -namespace ATI.Services.Common.Metrics +namespace ATI.Services.Common.Metrics; + +[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] +public static class MetricsExtensions { - [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] - public static class MetricsExtensions + /// + /// Alias to distinguish from Microsoft.Extensions.DependencyInjection.MetricsServiceExtensions.AddMetrics(IServiceCollection) + /// + public static void AddCommonMetrics(this IServiceCollection services) => services.AddMetrics(); + + public static void AddMetrics(this IServiceCollection services) { - public static void AddMetrics(this IServiceCollection services) - { - services.ConfigureByName(); - services.AddTransient(); + services.ConfigureByName(); + services.AddTransient(); - InitializeExceptionsMetrics(); + InitializeExceptionsMetrics(); - MetricsLabelsAndHeaders.LabelsStatic = ConfigurationManager.GetSection(nameof(MetricsOptions))?.Get()?.LabelsAndHeaders ?? new Dictionary(); - MetricsLabelsAndHeaders.UserLabels = MetricsLabelsAndHeaders.LabelsStatic.Keys.ToArray(); - MetricsLabelsAndHeaders.UserHeaders = MetricsLabelsAndHeaders.LabelsStatic.Values.ToArray(); - } + MetricsLabelsAndHeaders.LabelsStatic = ConfigurationManager.GetSection(nameof(MetricsOptions))?.Get()?.LabelsAndHeaders ?? new Dictionary(); + MetricsLabelsAndHeaders.UserLabels = MetricsLabelsAndHeaders.LabelsStatic.Keys.ToArray(); + MetricsLabelsAndHeaders.UserHeaders = MetricsLabelsAndHeaders.LabelsStatic.Values.ToArray(); + } - private static void InitializeExceptionsMetrics() - { - var exceptionCollector = new ExceptionsMetricsCollector(); - var registry = Prometheus.Metrics.DefaultRegistry; + private static void InitializeExceptionsMetrics() + { + var exceptionCollector = new ExceptionsMetricsCollector(); + var registry = Prometheus.Metrics.DefaultRegistry; - exceptionCollector.RegisterMetrics(registry); - registry.AddBeforeCollectCallback(exceptionCollector.UpdateMetrics); - } + exceptionCollector.RegisterMetrics(registry); + registry.AddBeforeCollectCallback(exceptionCollector.UpdateMetrics); + } - public static void UseMetrics(this IApplicationBuilder app) - { - app.UseMiddleware(); - } + public static void UseMetrics(this IApplicationBuilder app) + { + app.UseMiddleware(); } } \ No newline at end of file diff --git a/ATI.Services.Common/Metrics/MetricsFactory.cs b/ATI.Services.Common/Metrics/MetricsFactory.cs index 9c51316..8f8c1fa 100644 --- a/ATI.Services.Common/Metrics/MetricsFactory.cs +++ b/ATI.Services.Common/Metrics/MetricsFactory.cs @@ -6,380 +6,438 @@ using JetBrains.Annotations; using Prometheus; -namespace ATI.Services.Common.Metrics +namespace ATI.Services.Common.Metrics; + +[PublicAPI] +public class MetricsFactory { - [PublicAPI] - public class MetricsFactory + private Summary Summary { get; } + public const string Prefix = "common_metric"; + private static readonly string MachineName = Environment.MachineName; + private readonly string _className; + private readonly string _externalHttpServiceName; + private readonly LogSource _logSource; + private static TimeSpan _defaultLongRequestTime = TimeSpan.FromSeconds(1); + + private static readonly QuantileEpsilonPair[] SummaryQuantileEpsilonPairs = { + new(0.5, 0.05), + new(0.9, 0.05), + new(0.95, 0.01), + new(0.99, 0.005), + }; + + //Время запроса считающегося достаточно долгим, что бы об этом доложить в кибану + private readonly TimeSpan _longRequestTime; + + public static void Init(TimeSpan? defaultLongRequestTime = null) { - private Summary Summary { get; } - private static string _serviceName = "default_name"; - private static readonly string MachineName = Environment.MachineName; - private readonly string _externalHttpServiceName; - private readonly LogSource _logSource; - private static TimeSpan _defaultLongRequestTime = TimeSpan.FromSeconds(1); - - private static readonly QuantileEpsilonPair[] _summaryQuantileEpsilonPairs = { - new(0.5, 0.05), - new(0.9, 0.05), - new(0.95, 0.01), - new(0.99, 0.005), - }; - - //Время запроса считающегося достаточно долгим, что бы об этом доложить в кибану - private readonly TimeSpan _longRequestTime; - - public static void Init(string serviceName, TimeSpan? defaultLongRequestTime = null) + if (defaultLongRequestTime != null) { - _serviceName = serviceName; - if (defaultLongRequestTime != null) - { - _defaultLongRequestTime = defaultLongRequestTime.Value; - } + _defaultLongRequestTime = defaultLongRequestTime.Value; } + } - public static MetricsFactory CreateHttpClientMetricsFactory( - [NotNull] string summaryName, - string externalHttpServiceName, - TimeSpan? longRequestTime = null, - params string[] additionalSummaryLabels) - { - var labels = ConcatLabelNames( - "route_template", - "entity_name", - "external_http_service_name", - MetricsLabelsAndHeaders.UserLabels, - additionalSummaryLabels); - - return new MetricsFactory( - LogSource.HttpClient, - _serviceName + summaryName, - externalHttpServiceName, - longRequestTime, - labels); - } + public static MetricsFactory CreateHttpClientMetricsFactory( + [NotNull] string className, + string externalHttpServiceName, + TimeSpan? longRequestTime = null, + params string[] additionalSummaryLabels) + { + var labels = ConcatLabelNames( + "route_template", + "entity_name", + "external_http_service_name", + MetricsLabelsAndHeaders.UserLabels, + additionalSummaryLabels); + + return new MetricsFactory( + className, + LogSource.HttpClient, + $"{Prefix}_http_client", + externalHttpServiceName, + longRequestTime, + labels); + } - public static MetricsFactory CreateRedisMetricsFactory( - [NotNull] string summaryName, - TimeSpan? longRequestTime = null, - params string[] additionalSummaryLabels) - { - var labels = ConcatLabelNames( - "method_name", - "entity_name", - null, - MetricsLabelsAndHeaders.UserLabels, - additionalSummaryLabels); - - return new MetricsFactory( - LogSource.Redis, - _serviceName + summaryName, - longRequestTime ?? _defaultLongRequestTime, - labels); - } + public static MetricsFactory CreateRedisMetricsFactory( + [NotNull] string className, + TimeSpan? longRequestTime = null, + params string[] additionalSummaryLabels) + { + var labels = ConcatLabelNames( + "method_name", + "entity_name", + null, + MetricsLabelsAndHeaders.UserLabels, + additionalSummaryLabels); + + return new MetricsFactory( + className, + LogSource.Redis, + $"{Prefix}_redis", + longRequestTime ?? _defaultLongRequestTime, + labels); + } - public static MetricsFactory CreateMongoMetricsFactory( - [NotNull] string summaryName, - params string[] additionalSummaryLabels) - { - var labels = ConcatLabelNames("method_name", "entity_name", null, MetricsLabelsAndHeaders.UserLabels, - additionalSummaryLabels); - - return new MetricsFactory( - LogSource.Mongo, - _serviceName + summaryName, - _defaultLongRequestTime, - labels); - } + public static MetricsFactory CreateMongoMetricsFactory( + [NotNull] string className, + params string[] additionalSummaryLabels) + { + var labels = ConcatLabelNames( + "method_name", + "entity_name", + null, + MetricsLabelsAndHeaders.UserLabels, + additionalSummaryLabels); + + return new MetricsFactory( + className, + LogSource.Mongo, + $"{Prefix}_mongo", + _defaultLongRequestTime, + labels); + } - public static MetricsFactory CreateSqlMetricsFactory( - [NotNull] string summaryName, - TimeSpan? longTimeRequest = null, - params string[] additionalSummaryLabels) - { - var labels = ConcatLabelNames( - "procedure_name", - "entity_name", - null, - MetricsLabelsAndHeaders.UserLabels, - additionalSummaryLabels); - - return new MetricsFactory( - LogSource.Sql, - _serviceName + summaryName, - _defaultLongRequestTime, - labels); - } + public static MetricsFactory CreateSqlMetricsFactory( + [NotNull] string className, + TimeSpan? longTimeRequest = null, + params string[] additionalSummaryLabels) + { + var labels = ConcatLabelNames( + "procedure_name", + "entity_name", + null, + MetricsLabelsAndHeaders.UserLabels, + additionalSummaryLabels); + + return new MetricsFactory( + className, + LogSource.Sql, + $"{Prefix}_sql", + _defaultLongRequestTime, + labels); + } - public static MetricsFactory CreateControllerMetricsFactory( - [NotNull] string summaryName, - params string[] additionalSummaryLabels) - { - var labels = ConcatLabelNames( - "route_template", - "entity_name", - null, - MetricsLabelsAndHeaders.UserLabels, - additionalSummaryLabels); - - return new MetricsFactory( - LogSource.Controller, - _serviceName + summaryName, - _defaultLongRequestTime, - labels); - } + public static MetricsFactory CreateControllerMetricsFactory( + [NotNull] string className, + params string[] additionalSummaryLabels) + { + var labels = ConcatLabelNames( + "route_template", + "entity_name", + null, + MetricsLabelsAndHeaders.UserLabels, + additionalSummaryLabels); + + return new MetricsFactory( + className, + LogSource.Controller, + $"{Prefix}_controller", + _defaultLongRequestTime, + labels); + } - public static MetricsFactory CreateRepositoryMetricsFactory( - [NotNull] string summaryName, - TimeSpan? requestLongTime = null, - params string[] additionalSummaryLabels) - { - var labels = ConcatLabelNames( - "method_name", - "entity_name", - null, - MetricsLabelsAndHeaders.UserLabels, - additionalSummaryLabels); - - return new MetricsFactory( - LogSource.Repository, - _serviceName + summaryName, - requestLongTime ?? _defaultLongRequestTime, - labels); - } + public static MetricsFactory CreateRepositoryMetricsFactory( + [NotNull] string className, + TimeSpan? requestLongTime = null, + params string[] additionalSummaryLabels) + { + var labels = ConcatLabelNames( + "method_name", + "entity_name", + null, + MetricsLabelsAndHeaders.UserLabels, + additionalSummaryLabels); + + return new MetricsFactory( + className, + LogSource.Repository, + $"{Prefix}_repository", + requestLongTime ?? _defaultLongRequestTime, + labels); + } + + public static MetricsFactory CreateRabbitMqMetricsFactory( + RabbitMetricsType type, + [NotNull] string className, + TimeSpan? requestLongTime = null, + params string[] additionalSummaryLabels) + { + var labels = ConcatLabelNames( + "exchange_routing_key_name", + "entity_name", + null, + MetricsLabelsAndHeaders.UserLabels, + additionalSummaryLabels); + + return new MetricsFactory( + className, + LogSource.RabbitMq, + $"{Prefix}_rabbitmq_{type.ToString().ToLower()}", + requestLongTime ?? _defaultLongRequestTime, + labels); + } + + public static MetricsFactory CreateCustomMetricsFactory( + [NotNull] string className, + string customMetricName, + TimeSpan? requestLongTime = null, + params string[] additionalSummaryLabels) + { + if(customMetricName is null) throw new ArgumentNullException(nameof(customMetricName)); + + var labels = ConcatLabelNames( + "method_name", + "entity_name", + null, + MetricsLabelsAndHeaders.UserLabels, + additionalSummaryLabels); + + return new MetricsFactory( + className, + LogSource.Custom, + $"{Prefix}_{customMetricName}", + requestLongTime ?? _defaultLongRequestTime, + labels); + } - public static MetricsFactory CreateExternalHttpMetricsFactory(TimeSpan? longRequestTime = null) - { - return new MetricsFactory( - LogSource.ExternalHttpClient, - null, - longRequestTime ?? _defaultLongRequestTime); - } + private MetricsFactory( + string className, + LogSource logSource, + string summaryServiceName, + string externalHttpServiceName, + TimeSpan? longRequestTime = null, + params string[] summaryLabelNames) + { + _className = className; + _externalHttpServiceName = externalHttpServiceName; + _longRequestTime = longRequestTime ?? _defaultLongRequestTime; + _logSource = logSource; - private MetricsFactory( - LogSource logSource, - string summaryServiceName, - string externalHttpServiceName, - TimeSpan? longRequestTime = null, - params string[] summaryLabelNames) + if (summaryServiceName != null) { - _externalHttpServiceName = externalHttpServiceName; - _longRequestTime = longRequestTime ?? _defaultLongRequestTime; - _logSource = logSource; - - if (summaryServiceName != null) + var options = new SummaryConfiguration { - var options = new SummaryConfiguration - { - MaxAge = TimeSpan.FromMinutes(1), - Objectives = _summaryQuantileEpsilonPairs - }; - - Summary = Prometheus.Metrics.CreateSummary( - summaryServiceName, - string.Empty, - summaryLabelNames, - options); - } - } - - private MetricsFactory( - LogSource logSource, - [CanBeNull] string summaryServiceName, - TimeSpan longRequestTime, - params string[] summaryLabelNames) - { - _longRequestTime = longRequestTime; - _logSource = logSource; + MaxAge = TimeSpan.FromMinutes(1), + Objectives = SummaryQuantileEpsilonPairs + }; - if (summaryServiceName != null) - { - var options = new SummaryConfiguration - { - MaxAge = TimeSpan.FromMinutes(1), - Objectives = _summaryQuantileEpsilonPairs - }; - - Summary = Prometheus.Metrics.CreateSummary( - summaryServiceName, - string.Empty, - summaryLabelNames, - options); - } + Summary = Prometheus.Metrics.CreateSummary( + summaryServiceName, + string.Empty, + summaryLabelNames, + options); } + } - public IDisposable CreateMetricsTimerWithLogging( - string entityName, - [CallerMemberName] string actionName = null, - object requestParams = null, - TimeSpan? longRequestTime = null, - params string[] additionalLabels) - { - if (Summary == null) - { - throw new NullReferenceException($"{nameof(Summary)} is not initialized"); - } - - var labels = ConcatLabelValues( - actionName, - entityName, - _externalHttpServiceName, - AppHttpContext.MetricsHeadersValues, - additionalLabels); - - return new MetricsTimer( - Summary, - labels, - longRequestTime ?? _longRequestTime, - requestParams, - _logSource); - } + private MetricsFactory( + string className, + LogSource logSource, + [CanBeNull] string summaryServiceName, + TimeSpan longRequestTime, + params string[] summaryLabelNames) + { + _className = className; + _longRequestTime = longRequestTime; + _logSource = logSource; - /// - /// В случае создания данного экземпляра таймер для метрик стартует не сразу, а только после вызова метода Restart() - /// - /// - /// - /// - /// - /// - /// - /// - public MetricsTimer CreateMetricsTimerWithDelayedLogging( - string entityName, - [CallerMemberName] string actionName = null, - object requestParams = null, - TimeSpan? longRequestTime = null, - params string[] additionalLabels) + if (summaryServiceName != null) { - if (Summary == null) + var options = new SummaryConfiguration { - throw new NullReferenceException($"{nameof(Summary)} is not initialized"); - } - - var labels = ConcatLabelValues( - actionName, - entityName, - _externalHttpServiceName, - AppHttpContext.MetricsHeadersValues, - additionalLabels); - - return new MetricsTimer( - Summary, - labels, - longRequestTime ?? _longRequestTime, - requestParams, - _logSource, - false); - } + MaxAge = TimeSpan.FromMinutes(1), + Objectives = SummaryQuantileEpsilonPairs + }; - public IDisposable CreateLoggingMetricsTimer( - string entityName, - [CallerMemberName] string actionName = null, - object requestParams = null, - TimeSpan? longRequestTime = null, - params string[] additionalLabels) - { - var labels = ConcatLabelValues( - actionName, - entityName, - _externalHttpServiceName, - AppHttpContext.MetricsHeadersValues, - additionalLabels); - - return new MetricsTimer( - Summary, - labels, - longRequestTime ?? _longRequestTime, - requestParams, - _logSource); + Summary = Prometheus.Metrics.CreateSummary( + summaryServiceName, + string.Empty, + summaryLabelNames, + options); } + } - public IDisposable CreateMetricsTimer( - string entityName, - [CallerMemberName] string actionName = null, - params string[] additionalLabels) + public IDisposable CreateMetricsTimerWithLogging( + string entityName, + [CallerMemberName] string actionName = null, + object requestParams = null, + TimeSpan? longRequestTime = null, + params string[] additionalLabels) + { + if (Summary == null) { - var labels = ConcatLabelValues( - actionName, - entityName, - _externalHttpServiceName, - AppHttpContext.MetricsHeadersValues, - additionalLabels); - - return new MetricsTimer(Summary, labels); + throw new NullReferenceException($"{nameof(Summary)} is not initialized"); } - /// - /// Метод управляющий порядком значений лэйблов - /// - private static string[] ConcatLabelValues( - string actionName, - string entityName = null, - string externHttpService = null, - string[] userLabels = null, - params string[] additionalLabels) - { - return ConcatLabels( - MachineName, - actionName, - entityName, - externHttpService, - userLabels, - additionalLabels); - } + var labels = ConcatLabelValues( + _className, + actionName, + entityName, + _externalHttpServiceName, + AppHttpContext.MetricsHeadersValues, + additionalLabels); + + return new MetricsTimer( + Summary, + labels, + longRequestTime ?? _longRequestTime, + requestParams, + _logSource); + } - /// - /// Метод управляющий порядком названий лэйблов - /// - private static string[] ConcatLabelNames( - string actionName, - string entityName = null, - string externHttpService = null, - string[] userLabels = null, - params string[] additionalLabels) + /// + /// В случае создания данного экземпляра таймер для метрик стартует не сразу, а только после вызова метода Restart() + /// + /// + /// + /// + /// + /// + /// + /// + public MetricsTimer CreateMetricsTimerWithDelayedLogging( + string entityName, + [CallerMemberName] string actionName = null, + object requestParams = null, + TimeSpan? longRequestTime = null, + params string[] additionalLabels) + { + if (Summary == null) { - return ConcatLabels( - "machine_name", - actionName, - entityName, - externHttpService, - userLabels, - additionalLabels); + throw new NullReferenceException($"{nameof(Summary)} is not initialized"); } - /// - /// Метод управляющий порядком лэйблов и их значений - /// Указывать только при объявлении лейблов. Записывается он в таймере, так как нужен для трейсинга - /// - private static string[] ConcatLabels( - string machineName, - string actionName, - string entityName, - string externHttpService, - string[] userLabels, - params string[] additionalLabels) - { - var labels = new List - { - actionName - }; + var labels = ConcatLabelValues( + _className, + actionName, + entityName, + _externalHttpServiceName, + AppHttpContext.MetricsHeadersValues, + additionalLabels); + + return new MetricsTimer( + Summary, + labels, + longRequestTime ?? _longRequestTime, + requestParams, + _logSource, + false); + } - if (machineName != null) - labels.Add(machineName); + public IDisposable CreateLoggingMetricsTimer( + string entityName, + [CallerMemberName] string actionName = null, + object requestParams = null, + TimeSpan? longRequestTime = null, + params string[] additionalLabels) + { + var labels = ConcatLabelValues( + _className, + actionName, + entityName, + _externalHttpServiceName, + AppHttpContext.MetricsHeadersValues, + additionalLabels); + + return new MetricsTimer( + Summary, + labels, + longRequestTime ?? _longRequestTime, + requestParams, + _logSource); + } - if (entityName != null) - labels.Add(entityName); + public IDisposable CreateMetricsTimer( + string entityName, + [CallerMemberName] string actionName = null, + params string[] additionalLabels) + { + var labels = ConcatLabelValues( + _className, + actionName, + entityName, + _externalHttpServiceName, + AppHttpContext.MetricsHeadersValues, + additionalLabels); + + return new MetricsTimer(Summary, labels); + } - if (externHttpService != null) - labels.Add(externHttpService); + /// + /// Метод управляющий порядком значений лэйблов + /// + private static string[] ConcatLabelValues( + string className, + string actionName, + string entityName = null, + string externHttpService = null, + string[] userLabels = null, + params string[] additionalLabels) + { + return ConcatLabels( + className, + MachineName, + actionName, + entityName, + externHttpService, + userLabels, + additionalLabels); + } - if (userLabels != null && userLabels.Length != 0) - labels.AddRange(userLabels); + /// + /// Метод управляющий порядком названий лэйблов + /// + private static string[] ConcatLabelNames( + string actionName, + string entityName = null, + string externHttpService = null, + string[] userLabels = null, + params string[] additionalLabels) + { + return ConcatLabels( + "class_name", + "machine_name", + actionName, + entityName, + externHttpService, + userLabels, + additionalLabels); + } - if (additionalLabels.Length != 0) - labels.AddRange(additionalLabels); + /// + /// Метод управляющий порядком лэйблов и их значений + /// Указывать только при объявлении лейблов. Записывается он в таймере, так как нужен для трейсинга + /// + private static string[] ConcatLabels( + string className, + string machineName, + string actionName, + string entityName, + string externHttpService, + string[] userLabels, + params string[] additionalLabels) + { + var labels = new List + { + className, + actionName + }; + + if (machineName != null) + labels.Add(machineName); - return labels.ToArray(); - } + if (entityName != null) + labels.Add(entityName); + + if (externHttpService != null) + labels.Add(externHttpService); + + if (userLabels != null && userLabels.Length != 0) + labels.AddRange(userLabels); + + if (additionalLabels.Length != 0) + labels.AddRange(additionalLabels); + + return labels.ToArray(); } } \ No newline at end of file diff --git a/ATI.Services.Common/Metrics/MetricsOptions.cs b/ATI.Services.Common/Metrics/MetricsOptions.cs index 4b2b162..66cdce9 100644 --- a/ATI.Services.Common/Metrics/MetricsOptions.cs +++ b/ATI.Services.Common/Metrics/MetricsOptions.cs @@ -1,22 +1,16 @@ using System; using System.Collections.Generic; -namespace ATI.Services.Common.Metrics +namespace ATI.Services.Common.Metrics; + +public class MetricsOptions { - public class MetricsOptions - { - /// - /// User's additional labels and headers - /// Keys are labels' names - /// Values are headers' names - /// - public Dictionary LabelsAndHeaders { get; set; } - - public TimeSpan? DefaultLongRequestTime { get; set; } + /// + /// User's additional labels and headers + /// Keys are labels' names + /// Values are headers' names + /// + public Dictionary LabelsAndHeaders { get; set; } - /// - /// Название сервиса для метрик, без точек и прочих символов - /// - public string MetricsServiceName { get; set; } - } + public TimeSpan? DefaultLongRequestTime { get; set; } } \ No newline at end of file diff --git a/ATI.Services.Common/Metrics/MetricsStatusCodeCounterMiddleware.cs b/ATI.Services.Common/Metrics/MetricsStatusCodeCounterMiddleware.cs index f0bd43a..eabbb31 100644 --- a/ATI.Services.Common/Metrics/MetricsStatusCodeCounterMiddleware.cs +++ b/ATI.Services.Common/Metrics/MetricsStatusCodeCounterMiddleware.cs @@ -4,29 +4,31 @@ using Microsoft.AspNetCore.Http; using Prometheus; -namespace ATI.Services.Common.Metrics -{ - public class MetricsStatusCodeCounterMiddleware - { - private static Counter counter = Prometheus.Metrics.CreateCounter("HttpStatusCodeCounter", "", - new CounterConfiguration - { - LabelNames = new[] {"http_status_code"}.Concat(MetricsLabelsAndHeaders.UserLabels).ToArray() - }); +namespace ATI.Services.Common.Metrics; - private readonly RequestDelegate _next; +public class MetricsStatusCodeCounterMiddleware +{ + private readonly Counter _counter; + private readonly RequestDelegate _next; - public MetricsStatusCodeCounterMiddleware(RequestDelegate next) - { - _next = next; - } + public MetricsStatusCodeCounterMiddleware(RequestDelegate next) + { + _counter = Prometheus.Metrics.CreateCounter($"{MetricsFactory.Prefix}_HttpStatusCodeCounter", + "", + new CounterConfiguration + { + LabelNames = new[] { "http_status_code" } + .Concat(MetricsLabelsAndHeaders.UserLabels) + .ToArray() + }); + _next = next; + } - public async Task InvokeAsync(HttpContext context) - { - await _next(context); - var param = new[] {context.Response.StatusCode.ToString()}.Concat(AppHttpContext.MetricsHeadersValues) - .ToArray(); - counter.WithLabels(param).Inc(); - } + public async Task InvokeAsync(HttpContext context) + { + await _next(context); + var param = new[] {context.Response.StatusCode.ToString()}.Concat(AppHttpContext.MetricsHeadersValues) + .ToArray(); + _counter.WithLabels(param).Inc(); } } \ No newline at end of file diff --git a/ATI.Services.Common/Metrics/RabbitMetricsType.cs b/ATI.Services.Common/Metrics/RabbitMetricsType.cs new file mode 100644 index 0000000..7f60838 --- /dev/null +++ b/ATI.Services.Common/Metrics/RabbitMetricsType.cs @@ -0,0 +1,7 @@ +namespace ATI.Services.Common.Metrics; + +public enum RabbitMetricsType +{ + Subscribe, + Publish +} \ No newline at end of file diff --git a/ATI.Services.Common/Sql/DapperDb.cs b/ATI.Services.Common/Sql/DapperDb.cs index 100a97c..c265a80 100644 --- a/ATI.Services.Common/Sql/DapperDb.cs +++ b/ATI.Services.Common/Sql/DapperDb.cs @@ -1019,6 +1019,8 @@ private string BuildConnectionString() builder.ConnectRetryInterval = _options.ConnectRetryInterval.Value; } + builder.TrustServerCertificate = _options.TrustServerCertificate ?? true; + return builder.ToString(); } diff --git a/ATI.Services.Common/Sql/DataBaseOptions.cs b/ATI.Services.Common/Sql/DataBaseOptions.cs index c5ba327..43914ca 100644 --- a/ATI.Services.Common/Sql/DataBaseOptions.cs +++ b/ATI.Services.Common/Sql/DataBaseOptions.cs @@ -1,23 +1,23 @@ using System; using System.Collections.Generic; -namespace ATI.Services.Common.Sql +namespace ATI.Services.Common.Sql; + +public class DataBaseOptions { - public class DataBaseOptions - { - public string ConnectionString { get; set; } - public TimeSpan Timeout { get; set; } - public IDictionary TimeoutDictionary { get; set; } = new Dictionary(); - public TimeSpan? LongTimeRequest { get; set; } + public string ConnectionString { get; set; } + public TimeSpan Timeout { get; set; } + public IDictionary TimeoutDictionary { get; set; } = new Dictionary(); + public TimeSpan? LongTimeRequest { get; set; } - public string Server { get; set; } - public string Database { get; set; } - public string UserName { get; set; } - public string Password { get; set; } - public int? MinPoolSize { get; set; } - public int? MaxPoolSize { get; set; } - public int? ConnectTimeout { get; set; } - public int? ConnectRetryCount { get; set; } - public int? ConnectRetryInterval { get; set; } - } -} + public string Server { get; set; } + public string Database { get; set; } + public string UserName { get; set; } + public string Password { get; set; } + public int? MinPoolSize { get; set; } + public int? MaxPoolSize { get; set; } + public int? ConnectTimeout { get; set; } + public int? ConnectRetryCount { get; set; } + public int? ConnectRetryInterval { get; set; } + public bool? TrustServerCertificate { get; set; } +} \ No newline at end of file diff --git a/README.md b/README.md index 929ad46..aec8b12 100644 --- a/README.md +++ b/README.md @@ -109,15 +109,36 @@ --- ### Метрики -Добавляем метрики в `Startup.cs` : `services.AddMetrics();` -Он автоматически добавит: `MetricsOptions` + +#### Виды метрик +``` +common_metric_sql - collected by DapperDb and PostgressDapper +common_metric_http_client - for outgoing http requests, ConsulMetricsHttpClientWrapper uses it +common_metric_rabbitmq_in - incoming messages from rmq, used by ATI.Services.RabbitMQ and ChangeTracking +common_metric_rabbitmq_out - outgoing messages to rmq, used by ATI.Services.RabbitMQ and ChangeTracking +common_metric_repository - should be collected manually +common_metric_controller - incoming http requests, added by MeasureAttribute in controllers +common_metric_Exceptions - application exceptions +common_metric_HttpStatusCodeCounter - aspnet response codes +common_metric_redis - collected by RedisCache +common_metric_mongo - should be collected manually +common_metric_{something} - this one reserved for custom metric, if you really need it, try to keep number of unique metrics as low as possible +``` +#### Добавление в проект Так как Prometheus собирает метрики через консул, добавляем тег в конфиг консула `metrics-port-*портприложения*`. -Добавляем [endpoint](http://stash.ri.domain:7990/projects/AS/repos/ati.firmservicecore/browse/ATI.FirmService.Core.Web.Api/Controllers/MetricsController.cs) для сбора метрик. -Добавляем мидлвару ```csharp - app.UseMetrics(); +services.AddMetrics(); //или services.AddCommonMetrics(); +//... +app.UseEndpoints(endpoints => + { + //... + endpoints.MapMetricsCollection(); //Добавляем эндпоинт для сбора метрик + //... + }); + +app.UseMetrics(); //Добавляем мидлвару ``` Для использования кастомных метрик в `appsettings.json` нужно определить следующую модель: @@ -126,15 +147,10 @@ "LabelsAndHeaders": { "Лейбл метрики" : "Header HTTP-запроса" }, - "MetricsServiceName": "notifications" //переопределяем название сервиса для метрик }, ``` Ключ словаря - лейбл метрики, значение - Header HTTP-запроса. - - -Просим девопсов добавить сервис в Prometheus. - Собственно сбор: На метод котроллера вешаем `MeasureAttribute`, в который передаем название сущности, с которой работает метод. В остальных файлах создаем нужный экземпляр `MetricsFactory` оборачиваем методы в using c `CreateMetricsTimer`: