This commit is contained in:
2025-08-21 14:56:35 +05:00
commit ecde1f0eeb
10 changed files with 923 additions and 0 deletions

8
Services/ILokiService.cs Normal file
View File

@ -0,0 +1,8 @@
using GetFromLoki.Models;
namespace GetFromLoki.Services;
public interface ILokiService
{
Task<List<LogEntry>> GetLogsAsync(LogQueryRequest request);
}

7
Services/LokiOptions.cs Normal file
View File

@ -0,0 +1,7 @@
namespace GetFromLoki.Services;
public class LokiOptions
{
public string BaseUrl { get; set; } = "http://localhost:3100";
public int QueryTimeout { get; set; } = 30;
}

113
Services/LokiService.cs Normal file
View File

@ -0,0 +1,113 @@
using System.Text.Json;
using GetFromLoki.Models;
using Microsoft.Extensions.Options;
namespace GetFromLoki.Services;
public class LokiService : ILokiService
{
private readonly HttpClient _httpClient;
private readonly LokiOptions _options;
public LokiService(HttpClient httpClient, IOptions<LokiOptions> options)
{
_httpClient = httpClient;
_options = options.Value;
_httpClient.BaseAddress = new Uri(_options.BaseUrl);
_httpClient.Timeout = TimeSpan.FromSeconds(_options.QueryTimeout);
}
public async Task<List<LogEntry>> GetLogsAsync(LogQueryRequest request)
{
var logs = new List<LogEntry>();
// Формируем LogQL запрос
var query = BuildLogQLQuery(request);
// Формируем URL с параметрами
var queryParams = new List<string>
{
$"query={Uri.EscapeDataString(query)}",
$"limit={request.Limit ?? 100}"
};
if (request.StartTime.HasValue)
{
var startTimeNs = ((DateTimeOffset)request.StartTime.Value).ToUnixTimeMilliseconds() * 1_000_000;
queryParams.Add($"start={startTimeNs}");
}
if (request.EndTime.HasValue)
{
var endTimeNs = ((DateTimeOffset)request.EndTime.Value).ToUnixTimeMilliseconds() * 1_000_000;
queryParams.Add($"end={endTimeNs}");
}
var url = $"/loki/api/v1/query_range?{string.Join("&", queryParams)}";
try
{
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var jsonResponse = await response.Content.ReadAsStringAsync();
var lokiResponse = JsonSerializer.Deserialize<LokiQueryResponse>(
jsonResponse,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
);
if (lokiResponse?.Data?.Result != null)
{
foreach (var result in lokiResponse.Data.Result)
{
foreach (var value in result.Values)
{
if (value.Count >= 2)
{
// Loki возвращает timestamp в наносекундах
if (long.TryParse(value[0], out var timestampNs))
{
var timestamp = DateTimeOffset.FromUnixTimeMilliseconds(timestampNs / 1000000).DateTime;
logs.Add(new LogEntry
{
Timestamp = timestamp,
Message = value[1]
});
}
}
}
}
}
}
catch (Exception ex)
{
throw new InvalidOperationException($"Ошибка при получении логов из Loki: {ex.Message}", ex);
}
return logs.OrderBy(l => l.Timestamp).ToList();
}
private static string BuildLogQLQuery(LogQueryRequest request)
{
var query = "{";
var conditions = new List<string>();
// Добавляем все лейблы
foreach (var label in request.Labels)
{
if (!string.IsNullOrEmpty(label.Value))
{
conditions.Add($"{label.Key}=\"{label.Value}\"");
}
else
{
conditions.Add($"{label.Key}!=\"\"");
}
}
query += string.Join(",", conditions);
query += "}";
return query;
}
}