mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Redid logging, it doesn't work in the UI, but I'll debug that later
This commit is contained in:
parent
d18dd573eb
commit
13f3557e7b
@ -6,6 +6,8 @@ using System.Windows.Threading;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NLog.Extensions.Logging;
|
||||
using NLog.Targets;
|
||||
using ReactiveUI;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.DTOs.Interventions;
|
||||
@ -29,10 +31,7 @@ namespace Wabbajack
|
||||
public App()
|
||||
{
|
||||
_host = Host.CreateDefaultBuilder(Array.Empty<string>())
|
||||
.ConfigureLogging(c =>
|
||||
{
|
||||
c.ClearProviders();
|
||||
})
|
||||
.ConfigureLogging(AddLogging)
|
||||
.ConfigureServices((host, services) =>
|
||||
{
|
||||
ConfigureServices(services);
|
||||
@ -41,6 +40,40 @@ namespace Wabbajack
|
||||
|
||||
_serviceProvider = _host.Services;
|
||||
}
|
||||
|
||||
private void AddLogging(ILoggingBuilder loggingBuilder)
|
||||
{
|
||||
var config = new NLog.Config.LoggingConfiguration();
|
||||
|
||||
var fileTarget = new FileTarget("file")
|
||||
{
|
||||
FileName = "logs/Wabbajack.current.log",
|
||||
ArchiveFileName = "logs/Wabbajack.{##}.log",
|
||||
ArchiveOldFileOnStartup = true,
|
||||
MaxArchiveFiles = 10,
|
||||
Layout = "${processtime} [${level:uppercase=true}] (${logger}) ${message:withexception=true}",
|
||||
Header = "############ Wabbajack log file - ${longdate} ############"
|
||||
};
|
||||
|
||||
var consoleTarget = new ConsoleTarget("console");
|
||||
|
||||
var uiTarget = new LogStream()
|
||||
{
|
||||
Name = "ui",
|
||||
Layout = "${message}",
|
||||
};
|
||||
|
||||
loggingBuilder.Services.AddSingleton(uiTarget);
|
||||
|
||||
config.AddRuleForAllLevels(fileTarget);
|
||||
config.AddRuleForAllLevels(consoleTarget);
|
||||
config.AddRuleForAllLevels(uiTarget);
|
||||
|
||||
loggingBuilder.ClearProviders();
|
||||
loggingBuilder.SetMinimumLevel(LogLevel.Trace);
|
||||
loggingBuilder.AddNLog(config);
|
||||
}
|
||||
|
||||
private static IServiceCollection ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
RxApp.MainThreadScheduler = new DispatcherScheduler(Dispatcher.CurrentDispatcher);
|
||||
@ -56,7 +89,6 @@ namespace Wabbajack
|
||||
services.AddSingleton<SystemParametersConstructor>();
|
||||
services.AddSingleton<LauncherUpdater>();
|
||||
services.AddSingleton<ResourceMonitor>();
|
||||
services.AddAllSingleton<ILoggerProvider, LoggerProvider>();
|
||||
|
||||
services.AddSingleton<MainSettings>();
|
||||
services.AddTransient<CompilerVM>();
|
||||
|
38
Wabbajack.App.Wpf/Models/LogStream.cs
Normal file
38
Wabbajack.App.Wpf/Models/LogStream.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Text;
|
||||
using DynamicData;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NLog;
|
||||
using NLog.Targets;
|
||||
using LogLevel = NLog.LogLevel;
|
||||
|
||||
namespace Wabbajack.Models;
|
||||
|
||||
public class LogStream : TargetWithLayout
|
||||
{
|
||||
|
||||
public readonly SourceCache<ILogMessage, long> MessageLog = new(x => x.MessageId);
|
||||
|
||||
protected override void Write(LogEventInfo logEvent)
|
||||
{
|
||||
MessageLog.AddOrUpdate(new LogMessage(logEvent));
|
||||
}
|
||||
public interface ILogMessage
|
||||
{
|
||||
long MessageId { get; }
|
||||
|
||||
string ShortMessage { get; }
|
||||
DateTime TimeStamp { get; }
|
||||
string LongMessage { get; }
|
||||
}
|
||||
|
||||
private record LogMessage(LogEventInfo info) : ILogMessage
|
||||
{
|
||||
public long MessageId => info.SequenceID;
|
||||
public string ShortMessage => info.ToString();
|
||||
public DateTime TimeStamp => info.TimeStamp;
|
||||
public string LongMessage => info.FormattedMessage;
|
||||
}
|
||||
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using DynamicData;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wabbajack.Paths;
|
||||
using Wabbajack.Paths.IO;
|
||||
using Wabbajack.Services.OSIntegrated;
|
||||
|
||||
namespace Wabbajack.Models;
|
||||
|
||||
public class LoggerProvider : ILoggerProvider
|
||||
{
|
||||
private readonly RelativePath _appName;
|
||||
private readonly Configuration _configuration;
|
||||
private readonly CompositeDisposable _disposables;
|
||||
private readonly Stream _logFile;
|
||||
private readonly StreamWriter _logStream;
|
||||
|
||||
public readonly ReadOnlyObservableCollection<ILogMessage> _messagesFiltered;
|
||||
private readonly DateTime _startupTime;
|
||||
|
||||
private long _messageId;
|
||||
private readonly SourceCache<ILogMessage, long> _messageLog = new(m => m.MessageId);
|
||||
private readonly Subject<ILogMessage> _messages = new();
|
||||
|
||||
public LoggerProvider(Configuration configuration)
|
||||
{
|
||||
_startupTime = DateTime.UtcNow;
|
||||
_configuration = configuration;
|
||||
_configuration.LogLocation.CreateDirectory();
|
||||
|
||||
_disposables = new CompositeDisposable();
|
||||
|
||||
Messages
|
||||
.ObserveOnGuiThread()
|
||||
.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);
|
||||
_logFile.DisposeWith(_disposables);
|
||||
|
||||
_logStream = new StreamWriter(_logFile, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public IObservable<ILogMessage> Messages => _messages;
|
||||
public AbsolutePath LogPath { get; }
|
||||
public ReadOnlyObservableCollection<ILogMessage> MessageLog => _messagesFiltered;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposables.Dispose();
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new Logger(this, categoryName);
|
||||
}
|
||||
|
||||
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 class Logger : ILogger
|
||||
{
|
||||
private readonly string _categoryName;
|
||||
private readonly LoggerProvider _provider;
|
||||
private ImmutableList<object> Scopes = ImmutableList<object>.Empty;
|
||||
|
||||
public Logger(LoggerProvider provider, string categoryName)
|
||||
{
|
||||
_categoryName = categoryName;
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
|
||||
Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
Debug.WriteLine($"{logLevel} - {formatter(state, exception)}");
|
||||
_provider._messages.OnNext(new LogMessage<TState>(DateTime.UtcNow, _provider.NextMessageId(), logLevel,
|
||||
eventId, state, exception, formatter));
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
Scopes = Scopes.Add(state);
|
||||
return Disposable.Create(() => Scopes = Scopes.Remove(state));
|
||||
}
|
||||
}
|
||||
|
||||
public interface ILogMessage
|
||||
{
|
||||
long MessageId { get; }
|
||||
|
||||
string ShortMessage { get; }
|
||||
DateTime TimeStamp { get; }
|
||||
string LongMessage { get; }
|
||||
}
|
||||
|
||||
private 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -95,11 +95,11 @@ namespace Wabbajack
|
||||
|
||||
public ReactiveCommand<Unit, Unit> ExecuteCommand { get; }
|
||||
|
||||
public LoggerProvider LoggerProvider { get; }
|
||||
public LogStream LoggerProvider { get; }
|
||||
public ReadOnlyObservableCollection<CPUDisplayVM> StatusList => _resourceMonitor.Tasks;
|
||||
|
||||
public CompilerVM(ILogger<CompilerVM> logger, DTOSerializer dtos, SettingsManager settingsManager,
|
||||
IServiceProvider serviceProvider, LoggerProvider loggerProvider, ResourceMonitor resourceMonitor) : base(logger)
|
||||
IServiceProvider serviceProvider, LogStream loggerProvider, ResourceMonitor resourceMonitor) : base(logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_dtos = dtos;
|
||||
|
@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using ReactiveUI;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Windows.Media.Imaging;
|
||||
@ -16,6 +19,7 @@ using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Downloaders.GameFile;
|
||||
using Wabbajack.DTOs;
|
||||
using Wabbajack.DTOs.DownloadStates;
|
||||
using Wabbajack.DTOs.JsonConverters;
|
||||
using Wabbajack.Hashing.xxHash64;
|
||||
using Wabbajack.Installer;
|
||||
@ -46,6 +50,7 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
{
|
||||
private const string LastLoadedModlist = "last-loaded-modlist";
|
||||
private const string InstallSettingsPrefix = "install-settings-";
|
||||
private Random _random = new();
|
||||
|
||||
|
||||
[Reactive]
|
||||
@ -100,15 +105,19 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly SystemParametersConstructor _parametersConstructor;
|
||||
private readonly IGameLocator _gameLocator;
|
||||
private readonly LoggerProvider _loggerProvider;
|
||||
private readonly LogStream _loggerProvider;
|
||||
private readonly ResourceMonitor _resourceMonitor;
|
||||
private readonly Services.OSIntegrated.Configuration _configuration;
|
||||
private readonly HttpClient _client;
|
||||
public ReadOnlyObservableCollection<CPUDisplayVM> StatusList => _resourceMonitor.Tasks;
|
||||
|
||||
[Reactive]
|
||||
public bool Installing { get; set; }
|
||||
|
||||
public LoggerProvider LoggerProvider { get; }
|
||||
[Reactive]
|
||||
public bool ShowNSFWSlides { get; set; }
|
||||
|
||||
public LogStream LoggerProvider { get; }
|
||||
|
||||
|
||||
// Command properties
|
||||
@ -121,12 +130,12 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
public ReactiveCommand<Unit, Unit> OpenLogsCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> GoToInstallCommand { get; }
|
||||
public ReactiveCommand<Unit, Unit> BeginCommand { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> BackCommand { get; }
|
||||
|
||||
|
||||
public InstallerVM(ILogger<InstallerVM> logger, DTOSerializer dtos, SettingsManager settingsManager, IServiceProvider serviceProvider,
|
||||
SystemParametersConstructor parametersConstructor, IGameLocator gameLocator, LoggerProvider loggerProvider, ResourceMonitor resourceMonitor,
|
||||
Wabbajack.Services.OSIntegrated.Configuration configuration) : base(logger)
|
||||
SystemParametersConstructor parametersConstructor, IGameLocator gameLocator, LogStream loggerProvider, ResourceMonitor resourceMonitor,
|
||||
Wabbajack.Services.OSIntegrated.Configuration configuration, HttpClient client) : base(logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
@ -137,6 +146,7 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
_parametersConstructor = parametersConstructor;
|
||||
_gameLocator = gameLocator;
|
||||
_resourceMonitor = resourceMonitor;
|
||||
_client = client;
|
||||
|
||||
Installer = new MO2InstallerVM(this);
|
||||
|
||||
@ -203,16 +213,30 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
|
||||
|
||||
ModListLocation.WhenAnyValue(l => l.TargetPath)
|
||||
.Subscribe(p => LoadModlist(p, null).FireAndForget())
|
||||
.DisposeWith(disposables);
|
||||
|
||||
var token = new CancellationTokenSource();
|
||||
BeginSlideShow(token.Token).FireAndForget();
|
||||
Disposable.Create(() => token.Cancel())
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private async Task BeginSlideShow(CancellationToken token)
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(5000, token);
|
||||
if (InstallState == InstallState.Installing)
|
||||
{
|
||||
await PopulateNextModSlide(ModList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadLastModlist()
|
||||
{
|
||||
var lst = await _settingsManager.Load<AbsolutePath>(LastLoadedModlist);
|
||||
@ -306,6 +330,7 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
TaskBarUpdate.Send(update.StatusText, TaskbarItemProgressState.Indeterminate,
|
||||
update.StepsProgress.Value);
|
||||
};
|
||||
|
||||
if (!await installer.Begin(CancellationToken.None))
|
||||
{
|
||||
TaskBarUpdate.Send($"Error during install of {ModList.Name}", TaskbarItemProgressState.Error);
|
||||
@ -348,4 +373,28 @@ public class InstallerVM : BackNavigatingVM, IBackNavigatingVM, ICpuStatusVM
|
||||
SlideShowImage = ModListImage;
|
||||
}
|
||||
|
||||
|
||||
private async Task PopulateNextModSlide(ModList modList)
|
||||
{
|
||||
try
|
||||
{
|
||||
var mods = modList.Archives.Select(a => a.State)
|
||||
.OfType<IMetaState>()
|
||||
.Where(t => ShowNSFWSlides || !t.IsNSFW)
|
||||
.Where(t => t.ImageURL != null)
|
||||
.ToArray();
|
||||
var thisMod = mods[_random.Next(0, mods.Length)];
|
||||
var data = await _client.GetByteArrayAsync(thisMod.ImageURL!);
|
||||
var image = BitmapFrame.Create(new MemoryStream(data));
|
||||
SlideShowTitle = thisMod.Name;
|
||||
SlideShowAuthor = thisMod.Author;
|
||||
SlideShowDescription = thisMod.Description;
|
||||
SlideShowImage = image;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "While loading slide");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -78,6 +78,7 @@
|
||||
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell" Version="1.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1248-prerelease" />
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="5.0.0-rc2" />
|
||||
<PackageReference Include="PInvoke.User32" Version="0.7.104" />
|
||||
<PackageReference Include="ReactiveUI" Version="16.2.6" />
|
||||
<PackageReference Include="ReactiveUI.Fody" Version="16.2.6" />
|
||||
|
@ -64,6 +64,7 @@ public abstract class AInstaller<T>
|
||||
|
||||
public Action<StatusUpdate>? OnStatusUpdate;
|
||||
private readonly IResource<IInstaller> _limiter;
|
||||
private Func<long, string> _statusFormatter = x => x.ToString();
|
||||
|
||||
|
||||
public AInstaller(ILogger<T> logger, InstallerConfiguration config, IGameLocator gameLocator,
|
||||
@ -98,13 +99,14 @@ public abstract class AInstaller<T>
|
||||
|
||||
public ModList ModList => _configuration.ModList;
|
||||
|
||||
public void NextStep(string statusCategory, string statusText, long maxStepProgress)
|
||||
public void NextStep(string statusCategory, string statusText, long maxStepProgress, Func<long, string>? formatter = null)
|
||||
{
|
||||
_updateStopWatch.Restart();
|
||||
MaxStepProgress = maxStepProgress;
|
||||
_currentStep += 1;
|
||||
_statusText = statusText;
|
||||
_statusCategory = statusCategory;
|
||||
_statusFormatter = formatter ?? (x => x.ToString());
|
||||
_logger.LogInformation("Next Step: {Step}", statusText);
|
||||
|
||||
OnStatusUpdate?.Invoke(new StatusUpdate(statusCategory, statusText,
|
||||
@ -115,7 +117,7 @@ public abstract class AInstaller<T>
|
||||
{
|
||||
Interlocked.Add(ref _currentStepProgress, stepProgress);
|
||||
|
||||
OnStatusUpdate?.Invoke(new StatusUpdate(_statusCategory, $"[{_currentStep}/{MaxSteps}] {_statusText} ({_currentStepProgress}/{MaxStepProgress})",
|
||||
OnStatusUpdate?.Invoke(new StatusUpdate(_statusCategory, $"[{_currentStep}/{MaxSteps}] {_statusText} ({_statusFormatter(_currentStepProgress)}/{_statusFormatter(MaxStepProgress)})",
|
||||
Percent.FactoryPutInRange(_currentStep, MaxSteps),
|
||||
Percent.FactoryPutInRange(_currentStepProgress, MaxStepProgress)));
|
||||
}
|
||||
@ -208,7 +210,7 @@ public abstract class AInstaller<T>
|
||||
|
||||
public async Task InstallArchives(CancellationToken token)
|
||||
{
|
||||
NextStep(Consts.StepInstalling, "Installing files", ModList.Directives.Sum(d => d.Size));
|
||||
NextStep(Consts.StepInstalling, "Installing files", ModList.Directives.Sum(d => d.Size), x => x.ToFileSizeString());
|
||||
var grouped = ModList.Directives
|
||||
.OfType<FromArchive>()
|
||||
.Select(a => new {VF = _vfs.Index.FileForArchiveHashPath(a.ArchiveHashPath), Directive = a})
|
||||
|
Loading…
Reference in New Issue
Block a user