mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
commit
0f73b20f46
@ -8,5 +8,7 @@ namespace Wabbajack.App
|
||||
public AbsolutePath SavedSettingsLocation { get; set; }
|
||||
|
||||
public AbsolutePath EncryptedDataLocation { get; set; }
|
||||
|
||||
public AbsolutePath LogLocation { get; set; }
|
||||
}
|
||||
}
|
@ -3,20 +3,33 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:Wabbajack.App.Controls"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Wabbajack.App.Controls.LogView">
|
||||
<ScrollViewer ScrollChanged="ScrollViewer_OnScrollChanged" x:Name="ScrollViewer">
|
||||
<ItemsControl x:Name="Messages">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel></StackPanel>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:LogViewItem></controls:LogViewItem>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
<Grid RowDefinitions="Auto, *, Auto">
|
||||
<TextBlock Grid.Row="0">Current Log Contents</TextBlock>
|
||||
<ScrollViewer Grid.Row="1" ScrollChanged="ScrollViewer_OnScrollChanged" x:Name="ScrollViewer"
|
||||
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
|
||||
<ItemsControl x:Name="Messages">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel></StackPanel>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:LogViewItem></controls:LogViewItem>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button x:Name="CopyLog">
|
||||
<avalonia:MaterialIcon Kind="ContentCopy"></avalonia:MaterialIcon>
|
||||
</Button>
|
||||
<Button x:Name="OpenFolder">
|
||||
<avalonia:MaterialIcon Kind="Folder"></avalonia:MaterialIcon>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
@ -17,6 +17,8 @@ public partial class LogView : ReactiveUserControl<LogViewModel>
|
||||
{
|
||||
this.OneWayBind(ViewModel, vm => vm.Messages, view => view.Messages.Items)
|
||||
.DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.CopyLogFile, view => view.CopyLog)
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Disposables;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using Avalonia.Input;
|
||||
using DynamicData;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Wabbajack.App.Utilities;
|
||||
using Wabbajack.App.ViewModels;
|
||||
|
||||
@ -12,26 +18,21 @@ namespace Wabbajack.App.Controls;
|
||||
public class LogViewModel : ViewModelBase, IActivatableViewModel
|
||||
{
|
||||
private readonly LoggerProvider _provider;
|
||||
|
||||
private readonly SourceCache<LoggerProvider.ILogMessage, long> _messages;
|
||||
|
||||
public readonly ReadOnlyObservableCollection<LoggerProvider.ILogMessage> _messagesFiltered;
|
||||
public ReadOnlyObservableCollection<LoggerProvider.ILogMessage> Messages => _messagesFiltered;
|
||||
public ReadOnlyObservableCollection<LoggerProvider.ILogMessage> Messages => _provider.MessageLog;
|
||||
|
||||
[Reactive]
|
||||
public ReactiveCommand<Unit, Unit> CopyLogFile { get; set; }
|
||||
|
||||
public LogViewModel(LoggerProvider provider)
|
||||
{
|
||||
_messages = new SourceCache<LoggerProvider.ILogMessage, long>(m => m.MessageId);
|
||||
//_messages.LimitSizeTo(100);
|
||||
|
||||
Activator = new ViewModelActivator();
|
||||
_provider = provider;
|
||||
|
||||
_messages.Connect()
|
||||
.Bind(out _messagesFiltered)
|
||||
.Subscribe();
|
||||
|
||||
_provider.Messages
|
||||
.Subscribe(m => _messages.AddOrUpdate(m));
|
||||
CopyLogFile = ReactiveCommand.Create(() =>
|
||||
{
|
||||
var obj = new DataObject();
|
||||
obj.Set(DataFormats.FileNames, new List<string> {_provider.LogPath.ToString()});
|
||||
Application.Current.Clipboard.SetDataObjectAsync(obj);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
11
Wabbajack.App/Screens/LogScreenView.axaml
Normal file
11
Wabbajack.App/Screens/LogScreenView.axaml
Normal file
@ -0,0 +1,11 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:Wabbajack.App.Controls"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Wabbajack.App.Screens.LogScreenView">
|
||||
<Grid RowDefinitions="*">
|
||||
<controls:LogView></controls:LogView>
|
||||
</Grid>
|
||||
</UserControl>
|
12
Wabbajack.App/Screens/LogScreenView.axaml.cs
Normal file
12
Wabbajack.App/Screens/LogScreenView.axaml.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using ReactiveUI;
|
||||
using Wabbajack.App.Views;
|
||||
|
||||
namespace Wabbajack.App.Screens;
|
||||
|
||||
public partial class LogScreenView : ScreenBase<LogScreenViewModel>
|
||||
{
|
||||
public LogScreenView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
21
Wabbajack.App/Screens/LogScreenViewModel.cs
Normal file
21
Wabbajack.App/Screens/LogScreenViewModel.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Reactive;
|
||||
using Avalonia;
|
||||
using Avalonia.Input;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Wabbajack.App.Utilities;
|
||||
using Wabbajack.App.ViewModels;
|
||||
|
||||
namespace Wabbajack.App.Screens;
|
||||
|
||||
public class LogScreenViewModel : ViewModelBase, IActivatableViewModel
|
||||
{
|
||||
private readonly LoggerProvider _provider;
|
||||
public LogScreenViewModel(LoggerProvider provider)
|
||||
{
|
||||
_provider = provider;
|
||||
Activator = new ViewModelActivator();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -45,6 +45,7 @@ namespace Wabbajack.App
|
||||
services.AddDTOSerializer();
|
||||
services.AddSingleton<ModeSelectionViewModel>();
|
||||
services.AddTransient<FileSelectionBoxViewModel>();
|
||||
services.AddSingleton<IScreenView, LogScreenView>();
|
||||
services.AddSingleton<IScreenView, ModeSelectionView>();
|
||||
services.AddSingleton<IScreenView, InstallConfigurationView>();
|
||||
services.AddSingleton<IScreenView, CompilerConfigurationView>();
|
||||
@ -57,6 +58,7 @@ namespace Wabbajack.App
|
||||
services.AddSingleton<InstallationStateManager>();
|
||||
services.AddSingleton<HttpClient>();
|
||||
|
||||
services.AddSingleton<LogScreenViewModel>();
|
||||
services.AddAllSingleton<IReceiverMarker, StandardInstallationViewModel>();
|
||||
services.AddAllSingleton<IReceiverMarker, InstallConfigurationViewModel>();
|
||||
services.AddAllSingleton<IReceiverMarker, CompilerConfigurationViewModel>();
|
||||
@ -89,7 +91,8 @@ namespace Wabbajack.App
|
||||
{
|
||||
EncryptedDataLocation = KnownFolders.WabbajackAppLocal.Combine("encrypted"),
|
||||
ModListsDownloadLocation = KnownFolders.EntryPoint.Combine("downloaded_mod_lists"),
|
||||
SavedSettingsLocation = KnownFolders.WabbajackAppLocal.Combine("saved_settings")
|
||||
SavedSettingsLocation = KnownFolders.WabbajackAppLocal.Combine("saved_settings"),
|
||||
LogLocation = KnownFolders.EntryPoint.Combine("logs")
|
||||
});
|
||||
|
||||
services.AddSingleton<SettingsManager>();
|
||||
|
@ -1,9 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using DynamicData;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Paths.IO;
|
||||
|
||||
namespace Wabbajack.App.Utilities;
|
||||
|
||||
@ -12,16 +19,67 @@ public class LoggerProvider : ILoggerProvider
|
||||
private Subject<ILogMessage> _messages = new();
|
||||
public IObservable<ILogMessage> Messages => _messages;
|
||||
|
||||
private long _messageID = 0;
|
||||
private long _messageId = 0;
|
||||
private SourceCache<ILogMessage, long> _messageLog = new(m => m.MessageId);
|
||||
|
||||
public long NextMessageId()
|
||||
public readonly ReadOnlyObservableCollection<ILogMessage> _messagesFiltered;
|
||||
private readonly CompositeDisposable _disposables;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly DateTime _startupTime;
|
||||
private readonly RelativePath _appName;
|
||||
public AbsolutePath LogPath { get; }
|
||||
private readonly Stream _logFile;
|
||||
private readonly StreamWriter _logStream;
|
||||
public ReadOnlyObservableCollection<ILogMessage> MessageLog => _messagesFiltered;
|
||||
|
||||
public LoggerProvider(Configuration configuration)
|
||||
{
|
||||
return Interlocked.Increment(ref _messageID);
|
||||
_startupTime = DateTime.UtcNow;
|
||||
_configuration = configuration;
|
||||
_configuration.LogLocation.CreateDirectory();
|
||||
|
||||
_disposables = new CompositeDisposable();
|
||||
|
||||
Messages.Subscribe(m => _messageLog.AddOrUpdate(m))
|
||||
.DisposeWith(_disposables);
|
||||
|
||||
Messages.Subscribe(m => LogToFile(m))
|
||||
.DisposeWith(_disposables);
|
||||
|
||||
_messageLog.Connect()
|
||||
.Bind(out _messagesFiltered)
|
||||
.Subscribe()
|
||||
.DisposeWith(_disposables);
|
||||
|
||||
_messages.DisposeWith(_disposables);
|
||||
|
||||
_appName = typeof(LoggerProvider).Assembly.Location.ToAbsolutePath().FileName;
|
||||
LogPath = _configuration.LogLocation.Combine($"{_appName}.current.log");
|
||||
_logFile = LogPath.Open(FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
|
||||
_logFile.DisposeWith(_disposables);
|
||||
|
||||
_logStream = new StreamWriter(_logFile, Encoding.UTF8);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void LogToFile(ILogMessage logMessage)
|
||||
{
|
||||
var line = $"[{logMessage.TimeStamp - _startupTime}] {logMessage.LongMessage}";
|
||||
lock (_logStream)
|
||||
{
|
||||
_logStream.Write(line);
|
||||
_logStream.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private long NextMessageId()
|
||||
{
|
||||
return Interlocked.Increment(ref _messageId);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_messages.Dispose();
|
||||
_disposables.Dispose();
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
@ -43,7 +101,7 @@ public class LoggerProvider : ILoggerProvider
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
_provider._messages.OnNext(new LogMessage<TState>(_provider.NextMessageId(), logLevel, eventId, state, exception, formatter));
|
||||
_provider._messages.OnNext(new LogMessage<TState>(DateTime.UtcNow, _provider.NextMessageId(), logLevel, eventId, state, exception, formatter));
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
@ -63,10 +121,28 @@ public class LoggerProvider : ILoggerProvider
|
||||
long MessageId { get; }
|
||||
|
||||
string ShortMessage { get; }
|
||||
DateTime TimeStamp { get; }
|
||||
string LongMessage { get; }
|
||||
}
|
||||
|
||||
record LogMessage<TState>(long MessageId, LogLevel LogLevel, EventId EventId, TState State, Exception? Exception, Func<TState, Exception?, string> Formatter) : ILogMessage
|
||||
record LogMessage<TState>(DateTime TimeStamp, long MessageId, LogLevel LogLevel, EventId EventId, TState State, Exception? Exception, Func<TState, Exception?, string> Formatter) : ILogMessage
|
||||
{
|
||||
public string ShortMessage => Formatter(State, Exception);
|
||||
|
||||
public string LongMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(ShortMessage);
|
||||
if (Exception != null)
|
||||
{
|
||||
sb.Append("Exception: ");
|
||||
sb.Append(Exception);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -43,6 +43,9 @@ namespace Wabbajack.App.ViewModels
|
||||
|
||||
[Reactive]
|
||||
public ReactiveCommand<Unit, Unit> SettingsButton { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public ReactiveCommand<Unit, Unit> LogViewButton { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string ResourceStatus { get; set; }
|
||||
@ -72,9 +75,15 @@ namespace Wabbajack.App.ViewModels
|
||||
.DisposeWith(disposables);
|
||||
|
||||
SettingsButton = ReactiveCommand.Create(() =>
|
||||
{
|
||||
Receive(new NavigateTo(typeof(SettingsViewModel)));
|
||||
})
|
||||
{
|
||||
Receive(new NavigateTo(typeof(SettingsViewModel)));
|
||||
})
|
||||
.DisposeWith(disposables);
|
||||
|
||||
LogViewButton = ReactiveCommand.Create(() =>
|
||||
{
|
||||
Receive(new NavigateTo(typeof(LogScreenViewModel)));
|
||||
})
|
||||
.DisposeWith(disposables);
|
||||
|
||||
});
|
||||
|
@ -34,21 +34,25 @@
|
||||
</Design.DataContext>
|
||||
|
||||
<Grid RowDefinitions="40, *">
|
||||
<Grid ColumnDefinitions="40, *, 40, 40, 40">
|
||||
<Grid ColumnDefinitions="40, *, 40, 40, 40, 40">
|
||||
<Button Grid.Column="0" x:Name="BackButton">
|
||||
<i:MaterialIcon Kind="ArrowBack"> </i:MaterialIcon>
|
||||
</Button>
|
||||
|
||||
<TextBlock Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center" x:Name="ResourceStatus"></TextBlock>
|
||||
|
||||
<Button Grid.Column="2" x:Name="SettingsButton">
|
||||
<Button Grid.Column="2" x:Name="LogButton">
|
||||
<i:MaterialIcon Kind="ViewList"></i:MaterialIcon>
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="3" x:Name="SettingsButton">
|
||||
<i:MaterialIcon Kind="Gear"></i:MaterialIcon>
|
||||
</Button>
|
||||
<Button Grid.Column="3" x:Name="MinimizeButton">
|
||||
<Button Grid.Column="4" x:Name="MinimizeButton">
|
||||
<i:MaterialIcon Kind="WindowMinimize" />
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="4" x:Name="CloseButton">
|
||||
<Button Grid.Column="5" x:Name="CloseButton">
|
||||
<i:MaterialIcon Kind="Close"/>
|
||||
</Button>
|
||||
|
||||
|
@ -31,6 +31,9 @@ namespace Wabbajack.App.Views
|
||||
this.BindCommand(ViewModel, vm => vm.SettingsButton, view => view.SettingsButton)
|
||||
.DisposeWith(dispose);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.LogViewButton, view => view.LogButton)
|
||||
.DisposeWith(dispose);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.CurrentScreen, view => view.Contents.Content)
|
||||
.DisposeWith(dispose);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user