mirror of
https://github.com/Stone-Red-Code/Markdowser.git
synced 2026-09-04 09:06:15 +02:00
Add logging
This commit is contained in:
@@ -3,10 +3,15 @@ using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Styling;
|
||||
|
||||
using CuteUtils.Logging;
|
||||
|
||||
using Markdowser.Models;
|
||||
using Markdowser.Utilities;
|
||||
using Markdowser.ViewModels;
|
||||
using Markdowser.Views;
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Markdowser;
|
||||
|
||||
public partial class App : Application
|
||||
@@ -18,6 +23,11 @@ public partial class App : Application
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (!Directory.Exists(Configuration.ApplicationDataPath))
|
||||
{
|
||||
_ = Directory.CreateDirectory(Configuration.ApplicationDataPath);
|
||||
}
|
||||
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new MainWindow
|
||||
@@ -25,12 +35,23 @@ public partial class App : Application
|
||||
DataContext = new MainWindowViewModel(),
|
||||
};
|
||||
|
||||
// Clear log files
|
||||
GlobalState.Logger.ClearLogFile(LogSeverity.Debug);
|
||||
GlobalState.Logger.ClearLogFile(LogSeverity.Info);
|
||||
GlobalState.Logger.ClearLogFile(LogSeverity.Warn);
|
||||
GlobalState.Logger.ClearLogFile(LogSeverity.Error);
|
||||
GlobalState.Logger.ClearLogFile(LogSeverity.Fatal);
|
||||
|
||||
desktop.MainWindow.Closing += (sender, e) =>
|
||||
{
|
||||
GlobalState.Logger.LogInfo("Saving settings...");
|
||||
Settings.SaveSettings();
|
||||
GlobalState.Logger.LogInfo("Settings saved.");
|
||||
};
|
||||
}
|
||||
|
||||
GlobalState.Logger.LogInfo("Application started.");
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
|
||||
if (Current is not null)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Markdowser.Utilities;
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Markdowser.Commands;
|
||||
@@ -23,7 +22,7 @@ public class HyperlinkCommand : ICommand
|
||||
{
|
||||
if (parameter is string url)
|
||||
{
|
||||
Debug.WriteLine($"Hyperlink clicked: {url}");
|
||||
GlobalState.Logger.LogDebug($"Hyperlink clicked: {url}");
|
||||
|
||||
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||
{
|
||||
@@ -35,7 +34,7 @@ public class HyperlinkCommand : ICommand
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine($"Invalid URL: {url}");
|
||||
GlobalState.Logger.LogDebug($"Invalid URL: {url}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +30,14 @@
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.10" />
|
||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.10" />
|
||||
<PackageReference Include="CuteUtils" Version="1.0.0" />
|
||||
<PackageReference Include="Markdown.Avalonia" Version="11.0.2" />
|
||||
<PackageReference Include="Projektanker.Icons.Avalonia" Version="9.1.2" />
|
||||
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.1.2" />
|
||||
<PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.1.2" />
|
||||
<PackageReference Include="ReverseMarkdown" Version="4.3.0" />
|
||||
<PackageReference Include="Semi.Avalonia" Version="11.0.7.1" />
|
||||
<PackageReference Include="Syroot.Windows.IO.KnownFolders" Version="1.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Markdowser.Utilities;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
@@ -32,7 +33,15 @@ public class Settings
|
||||
{
|
||||
if (File.Exists(Configuration.SettingsFilePath))
|
||||
{
|
||||
return JsonSerializer.Deserialize<Settings>(File.ReadAllText(Configuration.SettingsFilePath)) ?? new Settings();
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<Settings>(File.ReadAllText(Configuration.SettingsFilePath)) ?? new Settings();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GlobalState.Logger.LogWarn(ex.Message);
|
||||
return new Settings();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using HtmlAgilityPack;
|
||||
|
||||
using Markdowser.Utilities;
|
||||
using Markdowser.ViewModels;
|
||||
using Markdowser.ViewModels.Content;
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
@@ -56,11 +56,11 @@ internal partial class HtmlProcessor : IContentProcessor
|
||||
htmlDoc.LoadHtml(html.ToString());
|
||||
string title = htmlDoc.DocumentNode.SelectSingleNode("html/head/title")?.InnerText?.Trim() ?? httpResponseMessage.RequestMessage?.RequestUri?.ToString() ?? "Untitled";
|
||||
|
||||
Debug.WriteLine("Converting HTML to markdown...");
|
||||
GlobalState.Logger.LogDebug("Converting HTML to markdown...");
|
||||
|
||||
string markdown = markdownConverter.Convert(html.ToString());
|
||||
|
||||
Debug.WriteLine("Processing markdown...");
|
||||
GlobalState.Logger.LogDebug("Processing markdown...");
|
||||
|
||||
int currentLine = 0;
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
using Avalonia;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
using Markdowser.Utilities;
|
||||
|
||||
using Projektanker.Icons.Avalonia;
|
||||
using Projektanker.Icons.Avalonia.FontAwesome;
|
||||
using Projektanker.Icons.Avalonia.MaterialDesign;
|
||||
|
||||
using ReactiveUI;
|
||||
|
||||
using System;
|
||||
using System.Reactive;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Markdowser;
|
||||
|
||||
@@ -17,8 +23,30 @@ internal static class Program
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
_ = BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
|
||||
{
|
||||
GlobalState.Logger.LogFatal(e.ExceptionObject.ToString() ?? "Unknown error.");
|
||||
};
|
||||
|
||||
TaskScheduler.UnobservedTaskException += (sender, e) =>
|
||||
{
|
||||
GlobalState.Logger.LogFatal(e.Exception.ToString());
|
||||
};
|
||||
|
||||
RxApp.DefaultExceptionHandler = Observer.Create<Exception>(ex =>
|
||||
{
|
||||
GlobalState.Logger.LogFatal(ex.ToString());
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
_ = BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GlobalState.Logger.LogFatal(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
using CuteUtils.Logging;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -16,6 +18,42 @@ internal static class GlobalState
|
||||
|
||||
private static string url = string.Empty;
|
||||
|
||||
public static Logger Logger { get; } = new()
|
||||
{
|
||||
Config = new()
|
||||
{
|
||||
DebugConfig = new()
|
||||
{
|
||||
LogTarget = LogTarget.DebugConsole
|
||||
},
|
||||
InfoConfig = new()
|
||||
{
|
||||
LogTarget = LogTarget.DebugConsole | LogTarget.File,
|
||||
FilePath = Configuration.LogFilePath
|
||||
},
|
||||
WarnConfig = new()
|
||||
{
|
||||
LogTarget = LogTarget.DebugConsole | LogTarget.File,
|
||||
FilePath = Configuration.LogFilePath
|
||||
},
|
||||
ErrorConfig = new()
|
||||
{
|
||||
LogTarget = LogTarget.DebugConsole | LogTarget.File,
|
||||
FilePath = Configuration.LogFilePath
|
||||
},
|
||||
FatalConfig = new()
|
||||
{
|
||||
LogTarget = LogTarget.DebugConsole | LogTarget.File,
|
||||
FilePath = Configuration.LogFilePath
|
||||
},
|
||||
FormatConfig = new()
|
||||
{
|
||||
DebugConsoleFormat = new LogFormatBuilder().DateTime().Text(" ").LogSeverity(padding: -6).FilePath().Text(":").MemberName().Text(":").LineNumber().Text(Environment.NewLine).Message(),
|
||||
FileFormat = new LogFormatBuilder().DateTime().Text(" ").LogSeverity(padding: -6).FilePath().Text(":").MemberName().Text(":").LineNumber().Text(Environment.NewLine).Message(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static Stack<string> BackHistory { get; } = new();
|
||||
|
||||
public static Stack<string> ForwardHistory { get; } = new();
|
||||
|
||||
@@ -10,7 +10,6 @@ using Markdowser.Models;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
@@ -40,7 +39,7 @@ public class HttpPathResolver : IPathResolver
|
||||
relativeOrAbsolutePath = new Uri(new Uri(GlobalState.Url), relativeOrAbsolutePath).ToString();
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Resolving image: {relativeOrAbsolutePath}");
|
||||
GlobalState.Logger.LogDebug($"Resolving image: {relativeOrAbsolutePath}");
|
||||
|
||||
HttpResponseMessage httpResponseMessage;
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
namespace Markdowser.ViewModels;
|
||||
using CuteUtils.Logging;
|
||||
|
||||
using Markdowser.Utilities;
|
||||
|
||||
namespace Markdowser.ViewModels;
|
||||
|
||||
public abstract class ContentViewModelBase(string title)
|
||||
{
|
||||
public string Title { get; } = title;
|
||||
|
||||
public Logger Logger { get; } = GlobalState.Logger;
|
||||
}
|
||||
@@ -12,7 +12,6 @@ using ReactiveUI;
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
@@ -220,7 +219,7 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
if (string.IsNullOrWhiteSpace(Url))
|
||||
{
|
||||
Content = DefaultContent;
|
||||
Debug.WriteLine("URL is empty.");
|
||||
Logger.LogDebug("URL is empty.");
|
||||
CurrentTab.Header = "New Tab";
|
||||
return;
|
||||
}
|
||||
@@ -257,7 +256,7 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
Debug.WriteLine("Fetching URL...");
|
||||
Logger.LogInfo($"Fetching URL: {Url}");
|
||||
|
||||
try
|
||||
{
|
||||
@@ -288,6 +287,8 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
Content = await contentProcessorManager.ProcessContent(httpResponseMessage, new Progress<ProcessingProgress>(p => Progress = p.Percentage));
|
||||
|
||||
Dispatcher.UIThread.Post(() => CurrentTab.Header = Content.Title);
|
||||
|
||||
Logger.LogInfo($"Fetched URL: {Url}");
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
@@ -307,6 +308,8 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
_ = errorMessage.AppendLine($"Failed to fetch URL: {e.Message}");
|
||||
|
||||
Content = new MarkdownContentViewModel("Error", errorMessage.ToString());
|
||||
|
||||
Logger.LogError(e.Message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -317,6 +320,8 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
|
||||
Content = new MarkdownContentViewModel("Error", errorMessage.ToString());
|
||||
Dispatcher.UIThread.Post(() => CurrentTab.Header = $"Error: {e.GetType().Name}");
|
||||
|
||||
Logger.LogError(e.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
using Avalonia.Controls.Notifications;
|
||||
using Avalonia.Platform;
|
||||
|
||||
using CuteUtils.Logging;
|
||||
|
||||
using Markdowser.Models;
|
||||
using Markdowser.Utilities;
|
||||
|
||||
using ReactiveUI;
|
||||
|
||||
@@ -19,6 +22,8 @@ public class ViewModelBase : ReactiveObject
|
||||
|
||||
public WindowNotificationManager WindowNotificationManager => windowNotificationManager!;
|
||||
|
||||
public Logger Logger => GlobalState.Logger;
|
||||
|
||||
protected internal void InitializeWindowNotificationManager(Window window)
|
||||
{
|
||||
TopLevel? topLevel = TopLevel.GetTopLevel(window);
|
||||
|
||||
Reference in New Issue
Block a user