Browse Source

Merge branch 'main' into inference

pull/165/head
Ionite 1 year ago committed by GitHub
parent
commit
4d50661ff8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 74
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Controls/LogViewerControl.axaml
  2. 52
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Controls/LogViewerControl.axaml.cs
  3. 25
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Converters/ChangeColorTypeConverter.cs
  4. 22
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Converters/EventIdConverter.cs
  5. 55
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Extensions/LoggerExtensions.cs
  6. 47
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/DataStoreLoggerConfiguration.cs
  7. 9
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/ILogDataStore.cs
  8. 6
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/ILogDataStoreImpl.cs
  9. 38
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/LogDataStore.cs
  10. 20
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/LogEntryColor.cs
  11. 33
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/LogModel.cs
  12. 21
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/ViewModels/LogViewerControlViewModel.cs
  13. 21
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/ViewModels/ObservableObject.cs
  14. 3
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/ViewModels/ViewModel.cs
  15. 75
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/DataStoreLoggerTarget.cs
  16. 65
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Extensions/ServicesExtension.cs
  17. 21
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/LICENSE
  18. 13
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/Logging/LogDataStore.cs
  19. 3
      StabilityMatrix.Avalonia.Diagnostics/LogViewer/README.md
  20. 36
      StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj
  21. 20
      StabilityMatrix.Avalonia.Diagnostics/ViewModels/LogWindowViewModel.cs
  22. 22
      StabilityMatrix.Avalonia.Diagnostics/Views/LogWindow.axaml
  23. 39
      StabilityMatrix.Avalonia.Diagnostics/Views/LogWindow.axaml.cs
  24. 43
      StabilityMatrix.Avalonia/App.axaml.cs
  25. 8
      StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj
  26. 6
      StabilityMatrix.Avalonia/Views/LaunchPageView.axaml.cs
  27. 2
      StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs
  28. 6
      StabilityMatrix.sln

74
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Controls/LogViewerControl.axaml

@ -0,0 +1,74 @@
<UserControl x:Class="StabilityMatrix.Avalonia.Diagnostics.LogViewer.Controls.LogViewerControl"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Diagnostics.LogViewer.Converters"
xmlns:logging="clr-namespace:StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Logging"
x:CompileBindings="True"
x:DataType="logging:ILogDataStoreImpl"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" >
<Grid RowDefinitions="*,Auto">
<Grid.Resources>
<converters:ChangeColorTypeConverter x:Key="ColorConverter" />
<converters:EventIdConverter x:Key="EventIdConverter"/>
<SolidColorBrush x:Key="ColorBlack">Black</SolidColorBrush>
<SolidColorBrush x:Key="ColorTransparent">Transparent</SolidColorBrush>
</Grid.Resources>
<Grid.Styles>
<Style Selector="DataGridRow">
<Setter Property="Padding" Value="0" />
<Setter Property="Foreground"
x:DataType="logging:LogModel"
Value="{Binding Color.Foreground,
FallbackValue=White,
Converter={StaticResource ColorConverter}, ConverterParameter={StaticResource ColorBlack}}" />
<Setter Property="Background"
x:DataType="logging:LogModel"
Value="{Binding Color.Background,
FallbackValue=Black,
Converter={StaticResource ColorConverter}, ConverterParameter={StaticResource ColorTransparent}}" />
</Style>
<Style Selector="DataGridCell.size">
<Setter Property="FontSize" Value="13" />
<Setter Property="Padding" Value="0" />
</Style>
</Grid.Styles>
<DataGrid x:Name="MyDataGrid"
ItemsSource="{Binding DataStore.Entries}" AutoGenerateColumns="False"
CanUserResizeColumns="True"
CanUserReorderColumns="True"
CanUserSortColumns="False"
LayoutUpdated="OnLayoutUpdated">
<DataGrid.Styles>
<Style Selector="TextBlock">
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
</Style>
</DataGrid.Styles>
<DataGrid.Columns>
<DataGridTextColumn CellStyleClasses="size" Header="Time" Width="Auto" Binding="{Binding Timestamp}" IsVisible="{Binding #IsTimestampVisible.IsChecked}"/>
<DataGridTextColumn CellStyleClasses="size" Header="Level" Width="Auto" Binding="{Binding LogLevel}" />
<!--<DataGridTextColumn CellStyleClasses="size" Header="Event Id" Width="120" Binding="{Binding EventId, Converter={StaticResource EventIdConverter}}" />-->
<DataGridTextColumn CellStyleClasses="size" Header="Callsite" Width="Auto" Binding="{Binding LoggerDisplayName}" />
<DataGridTextColumn CellStyleClasses="size" Header="State" Width="*" Binding="{Binding State}" />
<DataGridTextColumn CellStyleClasses="size" Header="Exception" Width="Auto" Binding="{Binding Exception}" />
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="1" Margin="20 10" Orientation="Horizontal">
<CheckBox x:Name="CanAutoScroll"
FontSize="11"
Content="Auto Scroll log"
IsChecked="True"/>
<CheckBox x:Name="IsTimestampVisible"
FontSize="11"
Content="Show Timestamp"/>
</StackPanel>
</Grid>
</UserControl>

52
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Controls/LogViewerControl.axaml.cs

@ -0,0 +1,52 @@
using System.Collections.Specialized;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.Threading;
using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Logging;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Controls;
public partial class LogViewerControl : UserControl
{
public LogViewerControl()
=> InitializeComponent();
private ILogDataStoreImpl? vm;
private LogModel? item;
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
if (DataContext is null)
return;
vm = (ILogDataStoreImpl)DataContext;
vm.DataStore.Entries.CollectionChanged += OnCollectionChanged;
}
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
Dispatcher.UIThread.Post(() =>
{
item = MyDataGrid.ItemsSource.Cast<LogModel>().LastOrDefault();
});
}
protected void OnLayoutUpdated(object? sender, EventArgs e)
{
if (CanAutoScroll.IsChecked != true || item is null)
return;
MyDataGrid.ScrollIntoView(item, null);
item = null;
}
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
if (vm is null) return;
vm.DataStore.Entries.CollectionChanged -= OnCollectionChanged;
}
}

25
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Converters/ChangeColorTypeConverter.cs

@ -0,0 +1,25 @@
using System.Globalization;
using Avalonia.Data.Converters;
using Avalonia.Media;
using SysDrawColor = System.Drawing.Color;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Converters;
public class ChangeColorTypeConverter : IValueConverter
{
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is null)
return new SolidColorBrush((Color)(parameter ?? Colors.Black));
var sysDrawColor = (SysDrawColor)value!;
return new SolidColorBrush(Color.FromArgb(
sysDrawColor.A,
sysDrawColor.R,
sysDrawColor.G,
sysDrawColor.B));
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotImplementedException();
}

22
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Converters/EventIdConverter.cs

@ -0,0 +1,22 @@
using System.Globalization;
using Avalonia.Data.Converters;
using Microsoft.Extensions.Logging;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Converters;
public class EventIdConverter : IValueConverter
{
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is null)
return "0";
var eventId = (EventId)value;
return eventId.ToString();
}
// If not implemented, an error is thrown
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> new EventId(0, value?.ToString() ?? string.Empty);
}

55
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Extensions/LoggerExtensions.cs

@ -0,0 +1,55 @@
using Microsoft.Extensions.Logging;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Extensions;
public static class LoggerExtensions
{
public static void Emit(this ILogger logger, EventId eventId,
LogLevel logLevel, string message, Exception? exception = null, params object?[] args)
{
if (logger is null)
return;
//if (!logger.IsEnabled(logLevel))
// return;
switch (logLevel)
{
case LogLevel.Trace:
logger.LogTrace(eventId, message, args);
break;
case LogLevel.Debug:
logger.LogDebug(eventId, message, args);
break;
case LogLevel.Information:
logger.LogInformation(eventId, message, args);
break;
case LogLevel.Warning:
logger.LogWarning(eventId, exception, message, args);
break;
case LogLevel.Error:
logger.LogError(eventId, exception, message, args);
break;
case LogLevel.Critical:
logger.LogCritical(eventId, exception, message, args);
break;
}
}
public static void TestPattern(this ILogger logger, EventId eventId)
{
var exception = new Exception("Test Error Message");
logger.Emit(eventId, LogLevel.Trace, "Trace Test Pattern");
logger.Emit(eventId, LogLevel.Debug, "Debug Test Pattern");
logger.Emit(eventId, LogLevel.Information, "Information Test Pattern");
logger.Emit(eventId, LogLevel.Warning, "Warning Test Pattern");
logger.Emit(eventId, LogLevel.Error, "Error Test Pattern", exception);
logger.Emit(eventId, LogLevel.Critical, "Critical Test Pattern", exception);
}
}

47
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/DataStoreLoggerConfiguration.cs

@ -0,0 +1,47 @@
using System.Drawing;
using Microsoft.Extensions.Logging;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Logging;
public class DataStoreLoggerConfiguration
{
#region Properties
public EventId EventId { get; set; }
public Dictionary<LogLevel, LogEntryColor> Colors { get; } = new()
{
[LogLevel.Trace] = new LogEntryColor
{
Foreground = Color.DarkGray
},
[LogLevel.Debug] = new LogEntryColor
{
Foreground = Color.Gray
},
[LogLevel.Information] = new LogEntryColor
{
Foreground = Color.WhiteSmoke,
},
[LogLevel.Warning] = new LogEntryColor
{
Foreground = Color.Orange
},
[LogLevel.Error] = new LogEntryColor
{
Foreground = Color.White,
Background = Color.OrangeRed
},
[LogLevel.Critical] = new LogEntryColor
{
Foreground = Color.White,
Background = Color.Red
},
[LogLevel.None] = new LogEntryColor
{
Foreground = Color.Magenta
}
};
#endregion
}

9
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/ILogDataStore.cs

@ -0,0 +1,9 @@
using System.Collections.ObjectModel;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Logging;
public interface ILogDataStore
{
ObservableCollection<LogModel> Entries { get; }
void AddEntry(LogModel logModel);
}

6
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/ILogDataStoreImpl.cs

@ -0,0 +1,6 @@
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Logging;
public interface ILogDataStoreImpl
{
public ILogDataStore DataStore { get; }
}

38
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/LogDataStore.cs

@ -0,0 +1,38 @@
using System.Collections.ObjectModel;
using Avalonia.Threading;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Logging;
public class LogDataStore : ILogDataStore
{
public static LogDataStore Instance { get; } = new();
#region Fields
private static readonly SemaphoreSlim _semaphore = new(initialCount: 1);
#endregion
#region Properties
public ObservableCollection<LogModel> Entries { get; } = new();
#endregion
#region Methods
public virtual void AddEntry(LogModel logModel)
{
// ensure only one operation at time from multiple threads
_semaphore.Wait();
Dispatcher.UIThread.Post(() =>
{
Entries.Add(logModel);
});
_semaphore.Release();
}
#endregion
}

20
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/LogEntryColor.cs

@ -0,0 +1,20 @@
using System.Drawing;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Logging;
public class LogEntryColor
{
public LogEntryColor()
{
}
public LogEntryColor(Color foreground, Color background)
{
Foreground = foreground;
Background = background;
}
public Color Foreground { get; set; } = Color.Black;
public Color Background { get; set; } = Color.Transparent;
}

33
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/Logging/LogModel.cs

@ -0,0 +1,33 @@
using Microsoft.Extensions.Logging;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Logging;
public class LogModel
{
#region Properties
public DateTime Timestamp { get; set; }
public LogLevel LogLevel { get; set; }
public EventId EventId { get; set; }
public object? State { get; set; }
public string? LoggerName { get; set; }
public string? CallerClassName { get; set; }
public string? CallerMemberName { get; set; }
public string? Exception { get; set; }
public LogEntryColor? Color { get; set; }
#endregion
public string LoggerDisplayName =>
LoggerName?
.Split('.', StringSplitOptions.RemoveEmptyEntries)
.LastOrDefault() ?? "";
}

21
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/ViewModels/LogViewerControlViewModel.cs

@ -0,0 +1,21 @@
using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Logging;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.ViewModels;
public class LogViewerControlViewModel : ViewModel, ILogDataStoreImpl
{
#region Constructor
public LogViewerControlViewModel(ILogDataStore dataStore)
{
DataStore = dataStore;
}
#endregion
#region Properties
public ILogDataStore DataStore { get; set; }
#endregion
}

21
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/ViewModels/ObservableObject.cs

@ -0,0 +1,21 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.ViewModels;
public class ObservableObject : INotifyPropertyChanged
{
protected bool Set<TValue>(ref TValue field, TValue newValue, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<TValue>.Default.Equals(field, newValue)) return false;
field = newValue;
OnPropertyChanged(propertyName);
return true;
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

3
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Core/ViewModels/ViewModel.cs

@ -0,0 +1,3 @@
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.ViewModels;
public class ViewModel : ObservableObject { /* skip */ }

75
StabilityMatrix.Avalonia.Diagnostics/LogViewer/DataStoreLoggerTarget.cs

@ -0,0 +1,75 @@
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Targets;
using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Logging;
using MsLogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer;
[Target("DataStoreLogger")]
public class DataStoreLoggerTarget : TargetWithLayout
{
#region Fields
private ILogDataStore? _dataStore;
private DataStoreLoggerConfiguration? _config;
#endregion
#region methods
protected override void InitializeTarget()
{
// we need to inject dependencies
// var serviceProvider = ResolveService<IServiceProvider>();
// reference the shared instance
_dataStore = LogDataStore.Instance;
// _dataStore = serviceProvider.GetRequiredService<ILogDataStore>();
// load the config options
/*var options
= serviceProvider.GetService<IOptionsMonitor<DataStoreLoggerConfiguration>>();*/
// _config = options?.CurrentValue ?? new DataStoreLoggerConfiguration();
_config = new DataStoreLoggerConfiguration();
base.InitializeTarget();
}
protected override void Write(LogEventInfo logEvent)
{
// cast NLog Loglevel to Microsoft LogLevel type
var logLevel = (MsLogLevel)Enum.ToObject(typeof(MsLogLevel), logEvent.Level.Ordinal);
// format the message
var message = RenderLogEvent(Layout, logEvent);
// retrieve the EventId
logEvent.Properties.TryGetValue("EventId", out var result);
if (result is not EventId eventId)
{
eventId = _config!.EventId;
}
// add log entry
_dataStore?.AddEntry(new LogModel
{
Timestamp = DateTime.UtcNow,
LogLevel = logLevel,
// do we override the default EventId if it exists?
EventId = eventId.Id == 0 && (_config?.EventId.Id ?? 0) != 0 ? _config!.EventId : eventId,
State = message,
LoggerName = logEvent.LoggerName,
CallerClassName = logEvent.CallerClassName,
CallerMemberName = logEvent.CallerMemberName,
Exception = logEvent.Exception?.Message ?? (logLevel == MsLogLevel.Error ? message : ""),
Color = _config!.Colors[logLevel],
});
Debug.WriteLine($"--- [{logLevel.ToString()[..3]}] {message} - {logEvent.Exception?.Message ?? "no error"}");
}
#endregion
}

65
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Extensions/ServicesExtension.cs

@ -0,0 +1,65 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog;
using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.Logging;
using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.ViewModels;
using LogDataStore = StabilityMatrix.Avalonia.Diagnostics.LogViewer.Logging.LogDataStore;
using MsLogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Extensions;
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public static class ServicesExtension
{
public static IServiceCollection AddLogViewer(this IServiceCollection services)
{
services.AddSingleton<ILogDataStore, LogDataStore>();
services.AddSingleton<LogViewerControlViewModel>();
return services;
}
public static IServiceCollection AddLogViewer(
this IServiceCollection services,
Action<DataStoreLoggerConfiguration> configure)
{
services.AddSingleton<ILogDataStore>(Core.Logging.LogDataStore.Instance);
services.AddSingleton<LogViewerControlViewModel>();
services.Configure(configure);
return services;
}
public static ILoggingBuilder AddNLogTargets(this ILoggingBuilder builder, IConfiguration config)
{
LogManager
.Setup()
// Register custom Target
.SetupExtensions(extensionBuilder =>
extensionBuilder.RegisterTarget<DataStoreLoggerTarget>("DataStoreLogger"));
/*builder
.ClearProviders()
.SetMinimumLevel(MsLogLevel.Trace)
// Load NLog settings from appsettings*.json
.AddNLog(config,
// custom options for capturing the EventId information
new NLogProviderOptions
{
// https://nlog-project.org/2021/08/25/nlog-5-0-preview1-ready.html#nlogextensionslogging-changes-capture-of-eventid
IgnoreEmptyEventId = false,
CaptureEventId = EventIdCaptureType.Legacy
});*/
return builder;
}
public static ILoggingBuilder AddNLogTargets(this ILoggingBuilder builder, IConfiguration config, Action<DataStoreLoggerConfiguration> configure)
{
builder.AddNLogTargets(config);
builder.Services.Configure(configure);
return builder;
}
}

21
StabilityMatrix.Avalonia.Diagnostics/LogViewer/LICENSE

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Graeme Grant
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

13
StabilityMatrix.Avalonia.Diagnostics/LogViewer/Logging/LogDataStore.cs

@ -0,0 +1,13 @@
using Avalonia.Threading;
namespace StabilityMatrix.Avalonia.Diagnostics.LogViewer.Logging;
public class LogDataStore : Core.Logging.LogDataStore
{
#region Methods
public override async void AddEntry(Core.Logging.LogModel logModel)
=> await Dispatcher.UIThread.InvokeAsync(() => base.AddEntry(logModel));
#endregion
}

3
StabilityMatrix.Avalonia.Diagnostics/LogViewer/README.md

@ -0,0 +1,3 @@
## LogViewer
Source code in the `StabilityMatrix.Avalonia.Diagnostics.LogViewer `namespace is included from [CodeProject](https://www.codeproject.com/Articles/5357417/LogViewer-Control-for-WinForms-WPF-and-Avalonia-in) under the [MIT License](LICENSE).

36
StabilityMatrix.Avalonia.Diagnostics/StabilityMatrix.Avalonia.Diagnostics.csproj

@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64;osx-arm64</RuntimeIdentifiers>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="StabilityMatrix.Tests" />
</ItemGroup>
<ItemGroup>
<Folder Include="LogViewer\Core\" />
<Folder Include="Views\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.4" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.4" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="NLog" Version="5.2.3" />
</ItemGroup>
<ItemGroup>
<Compile Update="Views\LogWindow.axaml.cs">
<DependentUpon>LogWindow.axaml</DependentUpon>
</Compile>
</ItemGroup>
</Project>

20
StabilityMatrix.Avalonia.Diagnostics/ViewModels/LogWindowViewModel.cs

@ -0,0 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Core.ViewModels;
namespace StabilityMatrix.Avalonia.Diagnostics.ViewModels;
public class LogWindowViewModel
{
public LogViewerControlViewModel LogViewer { get; }
public LogWindowViewModel(LogViewerControlViewModel logViewer)
{
LogViewer = logViewer;
}
public static LogWindowViewModel FromServiceProvider(IServiceProvider services)
{
return new LogWindowViewModel(
services.GetRequiredService<LogViewerControlViewModel>());
}
}

22
StabilityMatrix.Avalonia.Diagnostics/Views/LogWindow.axaml

@ -0,0 +1,22 @@
<Window
x:Class="StabilityMatrix.Avalonia.Diagnostics.Views.LogWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Diagnostics.LogViewer.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:StabilityMatrix.Avalonia.Diagnostics.ViewModels"
Title="Log Viewer"
Width="900"
Height="750"
Focusable="True"
d:DesignHeight="450"
d:DesignWidth="800"
x:CompileBindings="True"
x:DataType="vm:LogWindowViewModel"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<controls:LogViewerControl DataContext="{Binding LogViewer}" />
</Window>

39
StabilityMatrix.Avalonia.Diagnostics/Views/LogWindow.axaml.cs

@ -0,0 +1,39 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using StabilityMatrix.Avalonia.Diagnostics.ViewModels;
namespace StabilityMatrix.Avalonia.Diagnostics.Views;
public partial class LogWindow : Window
{
public LogWindow()
{
InitializeComponent();
}
public static IDisposable Attach(TopLevel root, IServiceProvider serviceProvider)
{
return Attach(root, serviceProvider, new KeyGesture(Key.F11));
}
public static IDisposable Attach(TopLevel root, IServiceProvider serviceProvider, KeyGesture gesture)
{
return (root ?? throw new ArgumentNullException(nameof(root))).AddDisposableHandler(
KeyDownEvent,
PreviewKeyDown,
RoutingStrategies.Tunnel);
void PreviewKeyDown(object? sender, KeyEventArgs e)
{
if (gesture.Matches(e))
{
var window = new LogWindow()
{
DataContext = LogWindowViewModel.FromServiceProvider(serviceProvider)
};
window.Show();
}
}
}
}

43
StabilityMatrix.Avalonia/App.axaml.cs

@ -34,6 +34,8 @@ using Sentry;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Controls.CodeCompletion;
using StabilityMatrix.Avalonia.DesignData;
using StabilityMatrix.Avalonia.Diagnostics.LogViewer;
using StabilityMatrix.Avalonia.Diagnostics.LogViewer.Extensions;
using StabilityMatrix.Avalonia.Helpers;
using StabilityMatrix.Avalonia.Languages;
using StabilityMatrix.Avalonia.Models;
@ -64,6 +66,7 @@ using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
using StabilityMatrix.Core.Updater;
using Application = Avalonia.Application;
using DrawingColor = System.Drawing.Color;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace StabilityMatrix.Avalonia;
@ -536,6 +539,8 @@ public sealed class App : Application
RefitSettings = apiFactoryRefitSettings,
});
ConditionalAddLogViewer(services);
// Add logging
services.AddLogging(builder =>
{
@ -545,7 +550,16 @@ public sealed class App : Application
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning);
builder.SetMinimumLevel(LogLevel.Debug);
#if DEBUG
builder.AddNLog(ConfigureLogging(),
new NLogProviderOptions
{
IgnoreEmptyEventId = false,
CaptureEventId = EventIdCaptureType.Legacy
});
#else
builder.AddNLog(ConfigureLogging());
#endif
});
return services;
@ -597,7 +611,11 @@ public sealed class App : Application
private static LoggingConfiguration ConfigureLogging()
{
LogManager.Setup().LoadConfiguration(builder => {
var setupBuilder = LogManager.Setup();
ConditionalAddLogViewerNLog(setupBuilder);
setupBuilder.LoadConfiguration(builder => {
var debugTarget = builder.ForTarget("console").WriteTo(new DebuggerTarget
{
Layout = "${message}"
@ -623,6 +641,14 @@ public sealed class App : Application
builder.ForLogger().FilterMinLevel(NLog.LogLevel.Trace).WriteTo(debugTarget);
builder.ForLogger().FilterMinLevel(NLog.LogLevel.Debug).WriteTo(fileTarget);
#if DEBUG
var logViewerTarget = builder.ForTarget("DataStoreLogger").WriteTo(new DataStoreLoggerTarget()
{
Layout = "${message}"
});
builder.ForLogger().FilterMinLevel(NLog.LogLevel.Trace).WriteTo(logViewerTarget);
#endif
});
// Sentry
@ -642,6 +668,21 @@ public sealed class App : Application
});
}
LogManager.ReconfigExistingLoggers();
return LogManager.Configuration;
}
[Conditional("DEBUG")]
private static void ConditionalAddLogViewer(IServiceCollection services)
{
services.AddLogViewer();
}
[Conditional("DEBUG")]
private static void ConditionalAddLogViewerNLog(ISetupBuilder setupBuilder)
{
setupBuilder.SetupExtensions(extensionBuilder =>
extensionBuilder.RegisterTarget<DataStoreLoggerTarget>("DataStoreLogger"));
}
}

8
StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj

@ -17,9 +17,9 @@
<ItemGroup>
<PackageReference Include="AsyncImageLoader.Avalonia" Version="3.0.0" />
<PackageReference Include="AutoComplete.Net" Version="1.2211.2014.42"/>
<PackageReference Include="Avalonia" Version="11.0.2" />
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.1" />
<PackageReference Include="Avalonia.Controls.PanAndZoom" Version="11.0.0" />
<PackageReference Include="Avalonia" Version="11.0.4" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.2" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.2" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
@ -44,7 +44,7 @@
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.8" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="NLog" Version="5.2.2" />
<PackageReference Include="NLog" Version="5.2.3" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.2" />
<PackageReference Include="Polly" Version="7.2.4" />
<PackageReference Include="Polly.Contrib.WaitAndRetry" Version="1.1.1" />
@ -72,6 +72,10 @@
<ItemGroup>
<ProjectReference Include="..\StabilityMatrix.Core\StabilityMatrix.Core.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Condition="'$(Configuration)' == 'Debug'" Include="..\StabilityMatrix.Avalonia.Diagnostics\StabilityMatrix.Avalonia.Diagnostics.csproj" />
</ItemGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\noimage.png" />

6
StabilityMatrix.Avalonia/Views/LaunchPageView.axaml.cs

@ -1,7 +1,6 @@
using System;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Threading;
using AvaloniaEdit;
@ -61,9 +60,4 @@ public partial class LaunchPageView : UserControlBase
editor.ScrollToLine(line);
});
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

2
StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs

@ -22,6 +22,7 @@ using FluentAvalonia.UI.Windowing;
using Microsoft.Extensions.DependencyInjection;
using StabilityMatrix.Avalonia.Animations;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Diagnostics.Views;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels;
using StabilityMatrix.Avalonia.ViewModels.Base;
@ -52,6 +53,7 @@ public partial class MainWindow : AppWindowBase
#if DEBUG
this.AttachDevTools();
LogWindow.Attach(this, App.Services);
#endif
TitleBar.ExtendsContentIntoTitleBar = true;

6
StabilityMatrix.sln

@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StabilityMatrix.Core", "Sta
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StabilityMatrix.Avalonia", "StabilityMatrix.Avalonia\StabilityMatrix.Avalonia.csproj", "{3F9F96FA-12E7-4B82-BFE5-1FAD88F3ACB2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StabilityMatrix.Avalonia.Diagnostics", "StabilityMatrix.Avalonia.Diagnostics\StabilityMatrix.Avalonia.Diagnostics.csproj", "{6D088B89-12D4-4EA0-BA6B-305C7D10C084}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -31,6 +33,10 @@ Global
{3F9F96FA-12E7-4B82-BFE5-1FAD88F3ACB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F9F96FA-12E7-4B82-BFE5-1FAD88F3ACB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F9F96FA-12E7-4B82-BFE5-1FAD88F3ACB2}.Release|Any CPU.Build.0 = Release|Any CPU
{6D088B89-12D4-4EA0-BA6B-305C7D10C084}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6D088B89-12D4-4EA0-BA6B-305C7D10C084}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D088B89-12D4-4EA0-BA6B-305C7D10C084}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D088B89-12D4-4EA0-BA6B-305C7D10C084}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Loading…
Cancel
Save