133 lines
6.2 KiB
C#
133 lines
6.2 KiB
C#
using EonaCat.Json;
|
|
using Microsoft.Extensions.Logging;
|
|
using System;
|
|
using System.Net.Http;
|
|
using System.Text;
|
|
using System.Collections.Generic;
|
|
|
|
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
|
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
|
|
|
namespace EonaCat.Logger.EonaCatCoreLogger
|
|
{
|
|
public class HttpLogger : ILogger
|
|
{
|
|
private readonly string _categoryName;
|
|
private readonly HttpLoggerOptions _options;
|
|
private readonly LoggerScopedContext _context = new();
|
|
|
|
public bool IncludeCorrelationId { get; set; }
|
|
|
|
private static readonly HttpClient _client = new();
|
|
public event EventHandler<Exception> OnException;
|
|
public event EventHandler<string> OnInvalidStatusCode;
|
|
|
|
public HttpLogger(string categoryName, HttpLoggerOptions options)
|
|
{
|
|
_categoryName = categoryName;
|
|
_options = options;
|
|
IncludeCorrelationId = options.IncludeCorrelationId;
|
|
}
|
|
|
|
public void SetContext(string key, string value) => _context.Set(key, value);
|
|
public void ClearContext() => _context.Clear();
|
|
public string GetContext(string key) => _context.Get(key);
|
|
|
|
public IDisposable BeginScope<TState>(TState state) => null;
|
|
public bool IsEnabled(LogLevel logLevel) => _options.IsEnabled;
|
|
|
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
|
|
Exception exception, Func<TState, Exception, string> formatter)
|
|
{
|
|
if (!IsEnabled(logLevel))
|
|
{
|
|
return;
|
|
}
|
|
|
|
string message = formatter(state, exception);
|
|
|
|
if (IncludeCorrelationId)
|
|
{
|
|
var correlationId = _context.Get("CorrelationId") ?? Guid.NewGuid().ToString();
|
|
_context.Set("CorrelationId", correlationId);
|
|
}
|
|
|
|
var payload = new Dictionary<string, object>
|
|
{
|
|
{ "timestamp", DateTime.UtcNow },
|
|
{ "level", logLevel.ToString() },
|
|
{ "category", _categoryName },
|
|
{ "message", message },
|
|
{ "eventId", eventId.Id }
|
|
};
|
|
|
|
// Add full log context as a nested dictionary
|
|
var contextData = _context.GetAll();
|
|
if (contextData.Count > 0)
|
|
{
|
|
payload["context"] = contextData;
|
|
}
|
|
|
|
try
|
|
{
|
|
var content = _options.SendAsJson
|
|
? new StringContent(JsonHelper.ToJson(payload), Encoding.UTF8, "application/json")
|
|
: new StringContent(message);
|
|
|
|
var result = _client.PostAsync(_options.Endpoint, content);
|
|
if (result.Result.IsSuccessStatusCode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Handle non-success status codes
|
|
var statusCode = result.Result.StatusCode;
|
|
var statusCodeMessage = statusCode switch
|
|
{
|
|
System.Net.HttpStatusCode.BadRequest => "400 Bad Request",
|
|
System.Net.HttpStatusCode.Unauthorized => "401 Unauthorized",
|
|
System.Net.HttpStatusCode.Forbidden => "403 Forbidden",
|
|
System.Net.HttpStatusCode.NotFound => "404 Not Found",
|
|
System.Net.HttpStatusCode.MethodNotAllowed => "405 Method Not Allowed",
|
|
System.Net.HttpStatusCode.NotAcceptable => "406 Not Acceptable",
|
|
System.Net.HttpStatusCode.ProxyAuthenticationRequired => "407 Proxy Authentication Required",
|
|
System.Net.HttpStatusCode.RequestTimeout => "408 Request Timeout",
|
|
System.Net.HttpStatusCode.Conflict => "409 Conflict",
|
|
System.Net.HttpStatusCode.Gone => "410 Gone",
|
|
System.Net.HttpStatusCode.LengthRequired => "411 Length Required",
|
|
System.Net.HttpStatusCode.PreconditionFailed => "412 Precondition Failed",
|
|
System.Net.HttpStatusCode.RequestEntityTooLarge => "413 Request Entity Too Large",
|
|
System.Net.HttpStatusCode.RequestUriTooLong => "414 Request URI Too Long",
|
|
System.Net.HttpStatusCode.UnsupportedMediaType => "415 Unsupported Media Type",
|
|
System.Net.HttpStatusCode.RequestedRangeNotSatisfiable => "416 Requested Range Not Satisfiable",
|
|
System.Net.HttpStatusCode.ExpectationFailed => "417 Expectation Failed",
|
|
(System.Net.HttpStatusCode)418 => "418 I'm a teapot",
|
|
(System.Net.HttpStatusCode)421 => "421 Misdirected Request",
|
|
(System.Net.HttpStatusCode)422 => "422 Unprocessable Entity",
|
|
(System.Net.HttpStatusCode)423 => "423 Locked",
|
|
(System.Net.HttpStatusCode)424 => "424 Failed Dependency",
|
|
(System.Net.HttpStatusCode)425 => "425 Too Early",
|
|
(System.Net.HttpStatusCode)426 => "426 Upgrade Required",
|
|
(System.Net.HttpStatusCode)428 => "428 Precondition Required",
|
|
(System.Net.HttpStatusCode)429 => "429 Too Many Requests",
|
|
(System.Net.HttpStatusCode)431 => "431 Request Header Fields Too Large",
|
|
(System.Net.HttpStatusCode)451 => "451 Unavailable For Legal Reasons",
|
|
System.Net.HttpStatusCode.InternalServerError => "500 Internal Server Error",
|
|
System.Net.HttpStatusCode.NotImplemented => "501 Not Implemented",
|
|
System.Net.HttpStatusCode.BadGateway => "502 Bad Gateway",
|
|
System.Net.HttpStatusCode.ServiceUnavailable => "503 Service Unavailable",
|
|
System.Net.HttpStatusCode.GatewayTimeout => "504 Gateway Timeout",
|
|
System.Net.HttpStatusCode.HttpVersionNotSupported => "505 HTTP Version Not Supported",
|
|
_ => statusCode.ToString()
|
|
};
|
|
|
|
OnInvalidStatusCode?.Invoke(this, statusCodeMessage);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
OnException?.Invoke(this, e);
|
|
}
|
|
}
|
|
}
|
|
}
|