namespace bsmd.util { using log4net.Appender; using log4net.Core; using Newtonsoft.Json; using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; public class LokiAppender : AppenderSkeleton { public string LokiUrl { get; set; } public string ApplicationName { get; set; } = "log4net-app"; private static readonly HttpClient httpClient = new HttpClient(); protected override void Append(LoggingEvent loggingEvent) { try { var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString() + "000000"; // nanoseconds var message = RenderLoggingEvent(loggingEvent); var payload = new { streams = new[] { new { stream = new { app = ApplicationName, level = loggingEvent.Level.Name.ToLower() }, values = new[] { new[] { timestamp, message } } } } }; var json = JsonConvert.SerializeObject(payload); var content = new StringContent(json, Encoding.UTF8, "application/json"); // Fire and forget Task.Run(() => httpClient.PostAsync(LokiUrl, content)); } catch (Exception ex) { ErrorHandler.Error("Error sending log to Loki", ex); } } } }