107 lines
3.6 KiB
C#
107 lines
3.6 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using Serilog;
|
|
using Serilog.Core;
|
|
using Serilog.Events;
|
|
using Serilog.Formatting;
|
|
using Serilog.Formatting.Display;
|
|
|
|
namespace NexusReader.Maui.Infrastructure.Logging;
|
|
|
|
public static class SerilogConfiguration
|
|
{
|
|
private const string OutputTemplate =
|
|
"[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [ThreadId: {ThreadId}] [{SourceContext}] {Message:lj}{NewLine}{Exception}";
|
|
|
|
public static MauiAppBuilder RegisterLogging(this MauiAppBuilder builder)
|
|
{
|
|
// 1. Ensure logs directory exists in secure sandbox
|
|
var logDir = Path.Combine(Microsoft.Maui.Storage.FileSystem.AppDataDirectory, "logs");
|
|
if (!Directory.Exists(logDir))
|
|
{
|
|
Directory.CreateDirectory(logDir);
|
|
}
|
|
var logPath = Path.Combine(logDir, "log-.txt");
|
|
|
|
// 2. Inject sandboxed log path dynamically into configuration provider
|
|
builder.Configuration["Serilog:WriteTo:0:Args:configure:0:Args:path"] = logPath;
|
|
|
|
// 3. Configure Serilog Logger Configuration using App Configuration settings
|
|
var loggerConfig = new LoggerConfiguration()
|
|
.ReadFrom.Configuration(builder.Configuration)
|
|
.Enrich.With(new ThreadIdEnricher());
|
|
|
|
// 4. Platform-specific and environment-specific sinks
|
|
#if ANDROID
|
|
// Direct Native Android Logcat Sink (JNI bindings for native diagnostics)
|
|
loggerConfig.WriteTo.Sink(
|
|
new AndroidLogcatSink(new MessageTemplateTextFormatter(OutputTemplate, null)),
|
|
restrictedToMinimumLevel: LogEventLevel.Debug);
|
|
#endif
|
|
|
|
// 5. Initialize the static Serilog Log
|
|
Log.Logger = loggerConfig.CreateLogger();
|
|
|
|
// 6. Connect Serilog to Microsoft.Extensions.Logging
|
|
builder.Logging.ClearProviders();
|
|
builder.Logging.AddSerilog(dispose: true);
|
|
|
|
return builder;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A custom self-contained thread enricher to avoid unnecessary NuGet packages.
|
|
/// </summary>
|
|
internal sealed class ThreadIdEnricher : ILogEventEnricher
|
|
{
|
|
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
|
|
{
|
|
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ThreadId", Environment.CurrentManagedThreadId));
|
|
}
|
|
}
|
|
|
|
#if ANDROID
|
|
/// <summary>
|
|
/// A high-performance, direct Android Logcat Sink utilizing native Android APIs.
|
|
/// </summary>
|
|
internal sealed class AndroidLogcatSink : ILogEventSink
|
|
{
|
|
private readonly ITextFormatter _formatter;
|
|
private const string Tag = "NexusReader";
|
|
|
|
public AndroidLogcatSink(ITextFormatter formatter)
|
|
{
|
|
_formatter = formatter ?? throw new ArgumentNullException(nameof(formatter));
|
|
}
|
|
|
|
public void Emit(LogEvent logEvent)
|
|
{
|
|
using var writer = new StringWriter();
|
|
_formatter.Format(logEvent, writer);
|
|
var message = writer.ToString().Trim();
|
|
|
|
switch (logEvent.Level)
|
|
{
|
|
case LogEventLevel.Verbose:
|
|
Android.Util.Log.Verbose(Tag, message);
|
|
break;
|
|
case LogEventLevel.Debug:
|
|
Android.Util.Log.Debug(Tag, message);
|
|
break;
|
|
case LogEventLevel.Information:
|
|
Android.Util.Log.Info(Tag, message);
|
|
break;
|
|
case LogEventLevel.Warning:
|
|
Android.Util.Log.Warn(Tag, message);
|
|
break;
|
|
case LogEventLevel.Error:
|
|
Android.Util.Log.Error(Tag, message);
|
|
break;
|
|
case LogEventLevel.Fatal:
|
|
Android.Util.Log.Wtf(Tag, message);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|