解耦日志器

This commit is contained in:
ww-rm
2025-03-29 16:30:32 +08:00
parent d365a5060b
commit 1b7b0dcb13
20 changed files with 170 additions and 120 deletions

View File

@@ -12,16 +12,11 @@ using SpineViewer.Spine;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Specialized;
using NLog;
namespace SpineViewer.Controls
{
public partial class SpineListView : UserControl
{
/// <summary>
/// 显示骨骼信息的属性面板
/// </summary>
[Category("自定义"), Description("用于显示骨骼属性的属性页")]
public PropertyGrid? PropertyGrid { get; set; }
/// <summary>
/// Spine 列表只读视图, 访问时必须使用 lock 语句锁定视图本身
/// </summary>
@@ -32,12 +27,23 @@ namespace SpineViewer.Controls
/// </summary>
private readonly List<Spine.Spine> spines = [];
/// <summary>
/// 日志器
/// </summary>
protected readonly Logger logger = LogManager.GetCurrentClassLogger();
public SpineListView()
{
InitializeComponent();
Spines = spines.AsReadOnly();
}
/// <summary>
/// 显示骨骼信息的属性面板
/// </summary>
[Category("自定义"), Description("用于显示骨骼属性的属性页")]
public PropertyGrid? PropertyGrid { get; set; }
/// <summary>
/// 选中的索引
/// </summary>
@@ -87,12 +93,12 @@ namespace SpineViewer.Controls
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to load {} {}", result.SkelPath, result.AtlasPath);
logger.Error(ex.ToString());
logger.Error("Failed to load {} {}", result.SkelPath, result.AtlasPath);
MessageBox.Error(ex.ToString(), "骨骼加载失败");
}
Program.LogCurrentMemoryUsage();
logger.LogCurrentProcessMemoryUsage();
}
/// <summary>
@@ -109,7 +115,7 @@ namespace SpineViewer.Controls
/// <summary>
/// 从结果批量添加
/// </summary>
public void BatchAdd(Dialogs.BatchOpenSpineDialogResult result)
private void BatchAdd(Dialogs.BatchOpenSpineDialogResult result)
{
var progressDialog = new Dialogs.ProgressDialog();
progressDialog.DoWork += BatchAdd_Work;
@@ -157,8 +163,8 @@ namespace SpineViewer.Controls
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to load {}", skelPath);
logger.Error(ex.ToString());
logger.Error("Failed to load {}", skelPath);
error++;
}
@@ -176,11 +182,11 @@ namespace SpineViewer.Controls
});
if (error > 0)
Program.Logger.Warn("Batch load {} successfully, {} failed", success, error);
logger.Warn("Batch load {} successfully, {} failed", success, error);
else
Program.Logger.Info("{} skel loaded successfully", success);
logger.Info("{} skel loaded successfully", success);
Program.LogCurrentMemoryUsage();
logger.LogCurrentProcessMemoryUsage();
}
/// <summary>
@@ -500,13 +506,16 @@ namespace SpineViewer.Controls
private void toolStripMenuItem_CopyPreview_Click(object sender, EventArgs e)
{
var fileDropList = new StringCollection();
var tempDir = Path.Combine(Path.GetTempPath(), Process.GetCurrentProcess().ProcessName);
Directory.CreateDirectory(tempDir);
lock (Spines)
{
foreach (int i in listView.SelectedIndices)
{
var a = Process.GetCurrentProcess();
var spine = spines[i];
var path = Path.Combine(Program.TempDir, $"{spine.ID}.png");
var path = Path.Combine(tempDir, $"{spine.ID}.png");
spine.Preview.Save(path);
fileDropList.Add(path);
}

View File

@@ -15,12 +15,20 @@ namespace SpineViewer.Dialogs
public AboutDialog()
{
InitializeComponent();
Text = $"关于 {Program.Name}";
Text = $"关于 {ProgramName}";
label_Version.Text = $"v{InformationalVersion}";
}
public string InformationalVersion =>
Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
public string ProgramName => Process.GetCurrentProcess().ProcessName;
public string InformationalVersion
=> Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
public string ProgramUrl
{
get => linkLabel_RepoUrl.Text;
set => linkLabel_RepoUrl.Text = value;
}
private void linkLabel_RepoUrl_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{

View File

@@ -21,7 +21,7 @@ namespace SpineViewer.Dialogs
public BatchOpenSpineDialog()
{
InitializeComponent();
comboBox_Version.DataSource = VersionHelper.Names.ToList();
comboBox_Version.DataSource = SpineHelper.Names.ToList();
comboBox_Version.DisplayMember = "Value";
comboBox_Version.ValueMember = "Key";
comboBox_Version.SelectedValue = Spine.Version.Auto;

View File

@@ -22,13 +22,13 @@ namespace SpineViewer.Dialogs
{
InitializeComponent();
comboBox_SourceVersion.DataSource = VersionHelper.Names.ToList();
comboBox_SourceVersion.DataSource = SpineHelper.Names.ToList();
comboBox_SourceVersion.DisplayMember = "Value";
comboBox_SourceVersion.ValueMember = "Key";
comboBox_SourceVersion.SelectedValue = Spine.Version.Auto;
// 目标版本不包含自动
var versionsWithoutAuto = VersionHelper.Names.ToDictionary();
var versionsWithoutAuto = SpineHelper.Names.ToDictionary();
versionsWithoutAuto.Remove(Spine.Version.Auto);
comboBox_TargetVersion.DataSource = versionsWithoutAuto.ToList();
comboBox_TargetVersion.DisplayMember = "Value";

View File

@@ -20,7 +20,7 @@ namespace SpineViewer.Dialogs
public OpenSpineDialog()
{
InitializeComponent();
comboBox_Version.DataSource = VersionHelper.Names.ToList();
comboBox_Version.DataSource = SpineHelper.Names.ToList();
comboBox_Version.DisplayMember = "Value";
comboBox_Version.ValueMember = "Key";
comboBox_Version.SelectedValue = Spine.Version.Auto;

View File

@@ -1,4 +1,5 @@
using System;
using NLog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
@@ -12,6 +13,13 @@ namespace SpineViewer.Dialogs
{
public partial class ProgressDialog : Form
{
private Logger logger = LogManager.GetCurrentClassLogger();
public ProgressDialog()
{
InitializeComponent();
}
/// <summary>
/// BackgroundWorker.DoWork 接口暴露
/// </summary>
@@ -32,11 +40,6 @@ namespace SpineViewer.Dialogs
/// </summary>
public void RunWorkerAsync(object? argument) => backgroundWorker.RunWorkerAsync(argument);
public ProgressDialog()
{
InitializeComponent();
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label_Tip.Text = e.UserState as string;
@@ -47,7 +50,7 @@ namespace SpineViewer.Dialogs
{
if (e.Error != null)
{
Program.Logger.Error(e.Error.ToString());
logger.Error(e.Error.ToString());
MessageBox.Error(e.Error.ToString(), "执行出错");
DialogResult = DialogResult.Abort;
}

View File

@@ -25,7 +25,7 @@ namespace SpineViewer.Exporter
/// <param name="renderSelectedOnly">仅渲染选中</param>
/// <returns>返回与指定 <paramref name="exportType"/> 匹配的导出参数实例</returns>
public static ExportArgs New(ExportType exportType, Size resolution, SFML.Graphics.View view, bool renderSelectedOnly)
=> New(exportType, resolution, view, renderSelectedOnly);
=> New(exportType, [resolution, view, renderSelectedOnly]);
public ExportArgs(Size resolution, SFML.Graphics.View view, bool renderSelectedOnly)
{

View File

@@ -1,4 +1,5 @@
using SpineViewer.Spine;
using NLog;
using SpineViewer.Spine;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@@ -20,7 +21,12 @@ namespace SpineViewer.Exporter
/// <param name="exportType">导出类型</param>
/// <param name="exportArgs">与 <paramref name="exportType"/> 匹配的导出参数</param>
/// <returns>与 <paramref name="exportType"/> 匹配的导出器</returns>
public static Exporter New(ExportType exportType, ExportArgs exportArgs) => New(exportType, exportArgs);
public static Exporter New(ExportType exportType, ExportArgs exportArgs) => New(exportType, [exportArgs]);
/// <summary>
/// 日志器
/// </summary>
protected Logger logger = LogManager.GetCurrentClassLogger();
/// <summary>
/// 导出参数
@@ -91,7 +97,7 @@ namespace SpineViewer.Exporter
if (ExportArgs.ExportSingle) ExportSingle(spinesToRender, worker);
else ExportIndividual(spinesToRender, worker);
Program.LogCurrentMemoryUsage();
logger.LogCurrentProcessMemoryUsage();
}
}
}

View File

@@ -16,7 +16,7 @@ namespace SpineViewer.Exporter.Implementations.ExportArgs
public GifExportArgs(Size resolution, SFML.Graphics.View view, bool renderSelectedOnly) : base(resolution, view, renderSelectedOnly)
{
// GIF 的帧率不能太高, 超过 50 帧反而会变慢
FPS = 25;
FPS = 12;
}
/// <summary>

View File

@@ -35,8 +35,8 @@ namespace SpineViewer.Exporter.Implementations.Exporter
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to save single frame");
logger.Error(ex.ToString());
logger.Error("Failed to save single frame");
}
worker?.ReportProgress(100, $"已处理 1/1");
}
@@ -68,8 +68,8 @@ namespace SpineViewer.Exporter.Implementations.Exporter
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to save frame {} {}", savePath, spine.SkelPath);
logger.Error(ex.ToString());
logger.Error("Failed to save frame {} {}", savePath, spine.SkelPath);
error++;
}
@@ -77,9 +77,9 @@ namespace SpineViewer.Exporter.Implementations.Exporter
}
if (error > 0)
Program.Logger.Warn("Frames save {} successfully, {} failed", success, error);
logger.Warn("Frames save {} successfully, {} failed", success, error);
else
Program.Logger.Info("{} frames saved successfully", success);
logger.Info("{} frames saved successfully", success);
}
}

View File

@@ -37,8 +37,8 @@ namespace SpineViewer.Exporter.Implementations.Exporter
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to save frame {}", savePath);
logger.Error(ex.ToString());
logger.Error("Failed to save frame {}", savePath);
}
finally
{
@@ -72,8 +72,8 @@ namespace SpineViewer.Exporter.Implementations.Exporter
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to save frame {} {}", savePath, spine.SkelPath);
logger.Error(ex.ToString());
logger.Error("Failed to save frame {} {}", savePath, spine.SkelPath);
}
finally
{

View File

@@ -39,8 +39,8 @@ namespace SpineViewer.Exporter.Implementations.Exporter
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to export gif {}", savePath);
logger.Error(ex.ToString());
logger.Error("Failed to export gif {}", savePath);
}
}
@@ -67,8 +67,8 @@ namespace SpineViewer.Exporter.Implementations.Exporter
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to export gif {} {}", savePath, spine.SkelPath);
logger.Error(ex.ToString());
logger.Error("Failed to export gif {} {}", savePath, spine.SkelPath);
}
}
}

View File

@@ -35,7 +35,7 @@ namespace SpineViewer.Exporter.Implementations.Exporter
{
if (worker?.CancellationPending == true)
{
Program.Logger.Info("Export cancelled");
logger.Info("Export cancelled");
break;
}
@@ -61,7 +61,7 @@ namespace SpineViewer.Exporter.Implementations.Exporter
{
if (worker?.CancellationPending == true)
{
Program.Logger.Info("Export cancelled");
logger.Info("Export cancelled");
break;
}

View File

@@ -41,7 +41,7 @@ namespace SpineViewer
ImplementationTypes[key] = type;
}
}
Program.Logger.Debug("Found implementations for {}: {}", baseType, string.Join(", ", ImplementationTypes.Keys));
NLog.LogManager.GetCurrentClassLogger().Debug("Found implementations for {}: {}", baseType, string.Join(", ", ImplementationTypes.Keys));
}
/// <summary>
@@ -56,7 +56,7 @@ namespace SpineViewer
/// <param name="args"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
protected static TBase New(TKey impKey, params object?[]? args)
protected static TBase New(TKey impKey, object?[] args)
{
if (!ImplementationTypes.TryGetValue(impKey, out var type))
throw new NotImplementedException($"Not implemented type for {typeof(TBase)}: {impKey}");

View File

@@ -14,6 +14,8 @@ namespace SpineViewer
{
public partial class MainForm : Form
{
private Logger logger = LogManager.GetCurrentClassLogger();
public MainForm()
{
InitializeComponent();
@@ -187,10 +189,14 @@ namespace SpineViewer
{
if (srcVersion == Spine.Version.Auto)
{
if (Spine.Spine.GetVersion(skelPath) is Spine.Version detectedSrcVersion)
srcCvter = SkeletonConverter.New(detectedSrcVersion);
else
throw new InvalidDataException($"Auto version detection failed for {skelPath}, try to use a specific version");
try
{
srcCvter = SkeletonConverter.New(Spine.Spine.GetVersion(skelPath));
}
catch (Exception ex)
{
throw new InvalidDataException($"Auto version detection failed for {skelPath}, try to use a specific version", ex);
}
}
var root = srcCvter.Read(skelPath);
root = srcCvter.ToVersion(root, tgtVersion);
@@ -199,8 +205,8 @@ namespace SpineViewer
}
catch (Exception ex)
{
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to convert {}", skelPath);
logger.Error(ex.ToString());
logger.Error("Failed to convert {}", skelPath);
error++;
}
@@ -209,11 +215,11 @@ namespace SpineViewer
if (error > 0)
{
Program.Logger.Warn("Batch convert {} successfully, {} failed", success, error);
logger.Warn("Batch convert {} successfully, {} failed", success, error);
}
else
{
Program.Logger.Info("{} skel converted successfully", success);
logger.Info("{} skel converted successfully", success);
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SpineViewer
{
public static class NLogExtension
{
/// <summary>
/// 输出当前进程的内存占用
/// </summary>
public static void LogCurrentProcessMemoryUsage(this NLog.Logger logger)
{
var process = Process.GetCurrentProcess();
logger.Info("Current memory usage for {}: {:F2} MB", process.ProcessName, process.WorkingSet64 / 1024.0 / 1024.0);
}
}
}

View File

@@ -5,35 +5,30 @@ namespace SpineViewer
{
internal static class Program
{
/// <summary>
/// 程序路径
/// </summary>
public static readonly string FilePath = Environment.ProcessPath;
///// <summary>
///// 程序路径
///// </summary>
//public static readonly string FilePath = Environment.ProcessPath;
/// <summary>
/// 程序名
/// </summary>
public static readonly string Name = Path.GetFileNameWithoutExtension(FilePath);
///// <summary>
///// 程序名
///// </summary>
//public static readonly string Name = Path.GetFileNameWithoutExtension(FilePath);
/// <summary>
/// 程序目录
/// </summary>
public static readonly string RootDir = Path.GetDirectoryName(FilePath);
///// <summary>
///// 程序目录
///// </summary>
//public static readonly string RootDir = Path.GetDirectoryName(FilePath);
/// <summary>
/// 程序临时目录
/// </summary>
public static readonly string TempDir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Name)).FullName;
/// <summary>
/// 程序进程
/// </summary>
public static readonly Process Process = Process.GetCurrentProcess();
///// <summary>
///// 程序临时目录
///// </summary>
//public static readonly string TempDir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Name)).FullName;
/// <summary>
/// 程序日志器
/// </summary>
public static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
/// <summary>
/// 应用入口点
@@ -41,8 +36,9 @@ namespace SpineViewer
[STAThread]
static void Main()
{
// 此处先初始化全局配置再触发静态字段 Logger 引用构造, 才能将配置应用到新的日志器上
InitializeLogConfiguration();
Logger.Info("Program Started");
logger.Info("Program Started");
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
@@ -54,7 +50,7 @@ namespace SpineViewer
}
catch (Exception ex)
{
Logger.Fatal(ex.ToString());
logger.Fatal(ex.ToString());
MessageBox.Error(ex.ToString(), "程序已崩溃");
}
}
@@ -82,10 +78,5 @@ namespace SpineViewer
config.AddRule(LogLevel.Debug, LogLevel.Fatal, fileTarget);
LogManager.Configuration = config;
}
/// <summary>
/// 输出当前内存使用情况
/// </summary>
public static void LogCurrentMemoryUsage() => Logger.Info("Current memory usage: {:F2} MB", Process.WorkingSet64 / 1024.0 / 1024.0);
}
}

View File

@@ -20,7 +20,7 @@ namespace SpineViewer.Spine
/// <summary>
/// 创建特定版本的 SkeletonConverter
/// </summary>
public static SkeletonConverter New(Version version) => New(version);
public static SkeletonConverter New(Version version) => New(version, []);
/// <summary>
/// Json 格式控制

View File

@@ -76,19 +76,22 @@ namespace SpineViewer.Spine
catch (Exception ex)
{
FragmentShader = null;
Program.Logger.Error(ex.ToString());
Program.Logger.Error("Failed to load fragment shader");
MessageBox.Warn("Fragment shader 加载失败预乘Alpha通道属性失效");
var logger = NLog.LogManager.GetCurrentClassLogger();
logger.Error(ex.ToString());
logger.Error("Failed to load fragment shader");
MessageBox.Warn("Fragment shader 加载失败预乘Alpha通道属性失效"); // TODO: 去除对窗体的调用
}
}
/// <summary>
/// 尝试检测骨骼文件版本
/// </summary>
public static Version? GetVersion(string skelPath)
/// <param name="skelPath"></param>
/// <returns></returns>
/// <exception cref="InvalidDataException"></exception>
public static Version GetVersion(string skelPath)
{
string versionString = null;
Version? version = null;
using var input = File.OpenRead(skelPath);
var reader = new SkeletonConverter.BinaryReader(input);
@@ -129,20 +132,9 @@ namespace SpineViewer.Spine
catch { }
}
if (versionString is not null)
{
if (versionString.StartsWith("2.1.")) version = Version.V21;
else if (versionString.StartsWith("3.6.")) version = Version.V36;
else if (versionString.StartsWith("3.7.")) version = Version.V37;
else if (versionString.StartsWith("3.8.")) version = Version.V38;
else if (versionString.StartsWith("4.0.")) version = Version.V40;
else if (versionString.StartsWith("4.1.")) version = Version.V41;
else if (versionString.StartsWith("4.2.")) version = Version.V42;
else if (versionString.StartsWith("4.3.")) version = Version.V43;
else Program.Logger.Error("Unknown verison: {}, {}", versionString, skelPath);
}
return version;
if (versionString is null)
throw new InvalidDataException($"No verison detected: {skelPath}");
return SpineHelper.GetVersion(versionString);
}
/// <summary>
@@ -150,14 +142,8 @@ namespace SpineViewer.Spine
/// </summary>
public static Spine New(Version version, string skelPath, string? atlasPath = null)
{
if (version == Version.Auto)
{
if (GetVersion(skelPath) is Version detectedVersion)
version = detectedVersion;
else
throw new InvalidDataException($"Auto version detection failed for {skelPath}, try to use a specific version");
}
var spine = New(version, skelPath, atlasPath);
if (version == Version.Auto) version = GetVersion(skelPath);
var spine = New(version, [skelPath, atlasPath]);
// 统一初始化
spine.initBounds = spine.Bounds;

View File

@@ -37,7 +37,7 @@ namespace SpineViewer.Spine
/// <summary>
/// Spine 版本静态辅助类
/// </summary>
public static class VersionHelper
public static class SpineHelper
{
/// <summary>
/// 版本名称
@@ -50,7 +50,7 @@ namespace SpineViewer.Spine
/// </summary>
private static readonly Dictionary<Version, string> runtimes = [];
static VersionHelper()
static SpineHelper()
{
// 初始化缓存
foreach (var value in Enum.GetValues(typeof(Version)))
@@ -85,5 +85,25 @@ namespace SpineViewer.Spine
{
return runtimes.TryGetValue(version, out var val) ? val : GetName(version);
}
/// <summary>
/// 获取字符串对应的版本号
/// </summary>
/// <param name="versionString"></param>
/// <returns></returns>
/// <exception cref="InvalidDataException"></exception>
public static Version GetVersion(string versionString)
{
ArgumentNullException.ThrowIfNullOrEmpty(versionString);
if (versionString.StartsWith("2.1.")) return Version.V21;
else if (versionString.StartsWith("3.6.")) return Version.V36;
else if (versionString.StartsWith("3.7.")) return Version.V37;
else if (versionString.StartsWith("3.8.")) return Version.V38;
else if (versionString.StartsWith("4.0.")) return Version.V40;
else if (versionString.StartsWith("4.1.")) return Version.V41;
else if (versionString.StartsWith("4.2.")) return Version.V42;
else if (versionString.StartsWith("4.3.")) return Version.V43;
else throw new InvalidDataException($"Unknown verison: {versionString}");
}
}
}