mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-10-16 23:49:19 +02:00
log avalonia warnings and write bledy.txt as it goes
This commit is contained in:
@@ -15,6 +15,9 @@ public static class Diagnostics
|
|||||||
private static readonly object Gate = new();
|
private static readonly object Gate = new();
|
||||||
private static readonly List<string> Lines = new();
|
private static readonly List<string> Lines = new();
|
||||||
|
|
||||||
|
private static StreamWriter? _writer;
|
||||||
|
private static bool _writerFailed;
|
||||||
|
|
||||||
public static string LogPath =>
|
public static string LogPath =>
|
||||||
Path.Combine(Directory.GetCurrentDirectory(), "starter", "bledy.txt");
|
Path.Combine(Directory.GetCurrentDirectory(), "starter", "bledy.txt");
|
||||||
|
|
||||||
@@ -25,13 +28,29 @@ public static class Diagnostics
|
|||||||
|
|
||||||
public static void Clear()
|
public static void Clear()
|
||||||
{
|
{
|
||||||
lock (Gate) Lines.Clear();
|
lock (Gate)
|
||||||
|
{
|
||||||
|
Lines.Clear();
|
||||||
|
CloseWriter();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(LogPath))
|
||||||
|
File.Delete(LogPath);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Log(string message)
|
public static void Log(string message)
|
||||||
{
|
{
|
||||||
|
string line = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)} {message}";
|
||||||
lock (Gate)
|
lock (Gate)
|
||||||
Lines.Add($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)} {message}");
|
{
|
||||||
|
Lines.Add(line);
|
||||||
|
Append(line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Log(IEnumerable<string> messages)
|
public static void Log(IEnumerable<string> messages)
|
||||||
@@ -45,22 +64,66 @@ public static class Diagnostics
|
|||||||
|
|
||||||
public static void Flush()
|
public static void Flush()
|
||||||
{
|
{
|
||||||
string[] snapshot;
|
|
||||||
lock (Gate)
|
lock (Gate)
|
||||||
{
|
{
|
||||||
if (Lines.Count == 0) return;
|
if (Lines.Count == 0)
|
||||||
snapshot = Lines.ToArray();
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_writer?.Flush();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_writerFailed = true;
|
||||||
|
Lines.Add($"log flush failed: {ex.GetType().Name}: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_writerFailed && _writer is not null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(LogPath)!);
|
||||||
|
File.WriteAllLines(LogPath, Lines);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Append(string line)
|
||||||
|
{
|
||||||
|
if (_writerFailed)
|
||||||
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string dir = Path.GetDirectoryName(LogPath)!;
|
if (_writer is null)
|
||||||
Directory.CreateDirectory(dir);
|
{
|
||||||
File.WriteAllLines(LogPath, snapshot);
|
Directory.CreateDirectory(Path.GetDirectoryName(LogPath)!);
|
||||||
|
_writer = new StreamWriter(LogPath, append: false) { AutoFlush = true };
|
||||||
|
}
|
||||||
|
_writer.WriteLine(line);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_writerFailed = true;
|
||||||
|
CloseWriter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CloseWriter()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_writer?.Dispose();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
_writer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Window? Owner =>
|
private static Window? Owner =>
|
||||||
|
|||||||
85
StarterNG/Infrastructure/DiagnosticsLogSink.cs
Normal file
85
StarterNG/Infrastructure/DiagnosticsLogSink.cs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using Avalonia.Logging;
|
||||||
|
|
||||||
|
namespace StarterNG.Infrastructure;
|
||||||
|
|
||||||
|
public sealed class DiagnosticsLogSink : ILogSink
|
||||||
|
{
|
||||||
|
private const int MaxDistinctMessages = 200;
|
||||||
|
|
||||||
|
private readonly LogEventLevel _minimumLevel;
|
||||||
|
private readonly HashSet<string> _seen = new(StringComparer.Ordinal);
|
||||||
|
private readonly object _gate = new();
|
||||||
|
private bool _capped;
|
||||||
|
|
||||||
|
private DiagnosticsLogSink(LogEventLevel minimumLevel) => _minimumLevel = minimumLevel;
|
||||||
|
|
||||||
|
public static void Install(LogEventLevel minimumLevel = LogEventLevel.Warning) =>
|
||||||
|
Logger.Sink = new DiagnosticsLogSink(minimumLevel);
|
||||||
|
|
||||||
|
public bool IsEnabled(LogEventLevel level, string area) => level >= _minimumLevel;
|
||||||
|
|
||||||
|
public void Log(LogEventLevel level, string area, object? source, string messageTemplate) =>
|
||||||
|
Write(level, area, source, messageTemplate, null);
|
||||||
|
|
||||||
|
public void Log(LogEventLevel level, string area, object? source, string messageTemplate,
|
||||||
|
params object?[] propertyValues) =>
|
||||||
|
Write(level, area, source, messageTemplate, propertyValues);
|
||||||
|
|
||||||
|
private void Write(LogEventLevel level, string area, object? source, string messageTemplate,
|
||||||
|
object?[]? propertyValues)
|
||||||
|
{
|
||||||
|
if (!IsEnabled(level, area))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string line = $"[Avalonia/{area}/{level}] {Format(messageTemplate, propertyValues)}";
|
||||||
|
if (source is not null)
|
||||||
|
line += $" (source: {source.GetType().Name})";
|
||||||
|
|
||||||
|
lock (_gate)
|
||||||
|
{
|
||||||
|
if (_capped || !_seen.Add(line))
|
||||||
|
return;
|
||||||
|
if (_seen.Count >= MaxDistinctMessages)
|
||||||
|
{
|
||||||
|
_capped = true;
|
||||||
|
Diagnostics.Log(line);
|
||||||
|
Diagnostics.Log($"[Avalonia] {MaxDistinctMessages} distinct messages logged, muting the rest");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Diagnostics.Log(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Format(string template, object?[]? values)
|
||||||
|
{
|
||||||
|
if (values is null || values.Length == 0 || template.IndexOf('{') < 0)
|
||||||
|
return template;
|
||||||
|
|
||||||
|
var sb = new StringBuilder(template.Length + 32);
|
||||||
|
int next = 0;
|
||||||
|
for (int i = 0; i < template.Length; i++)
|
||||||
|
{
|
||||||
|
if (template[i] != '{')
|
||||||
|
{
|
||||||
|
sb.Append(template[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int close = template.IndexOf('}', i);
|
||||||
|
if (close < 0)
|
||||||
|
{
|
||||||
|
sb.Append(template, i, template.Length - i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append(next < values.Length ? values[next]?.ToString() ?? "null" : template[i..(close + 1)]);
|
||||||
|
next++;
|
||||||
|
i = close;
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ using StarterNG.Infrastructure;
|
|||||||
|
|
||||||
namespace StarterNG;
|
namespace StarterNG;
|
||||||
|
|
||||||
class Program
|
static class Program
|
||||||
{
|
{
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
@@ -39,9 +39,15 @@ class Program
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static AppBuilder WithDiagnosticsLogging(this AppBuilder builder)
|
||||||
|
{
|
||||||
|
DiagnosticsLogSink.Install();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
public static AppBuilder BuildAvaloniaApp()
|
public static AppBuilder BuildAvaloniaApp()
|
||||||
=> AppBuilder.Configure<App>()
|
=> AppBuilder.Configure<App>()
|
||||||
.UsePlatformDetect()
|
.UsePlatformDetect()
|
||||||
.WithInterFont()
|
.WithInterFont()
|
||||||
.LogToTrace();
|
.WithDiagnosticsLogging();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user