log avalonia warnings and write bledy.txt as it goes

This commit is contained in:
maj00r
2026-08-31 00:44:36 +02:00
parent b5fabf79f5
commit eb304aa4c7
3 changed files with 164 additions and 10 deletions

View File

@@ -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 =>

View 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();
}
}

View File

@@ -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();
} }