Merge pull request #452 from daemitus/Logging

This commit is contained in:
goaaats 2021-08-09 16:50:41 +02:00 committed by GitHub
commit fe389d0762
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 416 additions and 435 deletions

5
.gitmodules vendored
View file

@ -3,7 +3,4 @@
url = https://github.com/goatcorp/ImGuiScene
[submodule "lib/FFXIVClientStructs"]
path = lib/FFXIVClientStructs
url = https://github.com/goatcorp/FFXIVClientStructs.git
[submodule "lib/SharpDX.Desktop"]
path = lib/SharpDX.Desktop
url = https://github.com/goatcorp/SharpDX.Desktop.git
url = https://github.com/goatcorp/FFXIVClientStructs.git

View file

@ -11,23 +11,22 @@
<NoWarn>IDE0003</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>$(appData)\XIVLauncher\devPlugins\</OutputPath>
<PropertyGroup Label="Build">
<OutputPath>$(AppData)\XIVLauncher\devPlugins\Dalamud.CorePlugin</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " Label="Build">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " Label="Build">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>$(appData)\XIVLauncher\devPlugins\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>

View file

@ -2,6 +2,7 @@ using System;
using System.IO;
using Dalamud.Interface.Windowing;
using Dalamud.Logging;
using Dalamud.Plugin;
namespace Dalamud.CorePlugin

View file

@ -11,8 +11,6 @@ namespace Dalamud.CorePlugin
/// </summary>
internal class PluginWindow : Window, IDisposable
{
private static readonly ModuleLog Log = new("CorePlugin");
private readonly Dalamud dalamud;
/// <summary>
@ -20,13 +18,13 @@ namespace Dalamud.CorePlugin
/// </summary>
/// <param name="dalamud">The Dalamud instance.</param>
public PluginWindow(Dalamud dalamud)
: base("CorePlugin", ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoScrollbar)
: base("CorePlugin")
{
this.dalamud = dalamud;
this.IsOpen = true;
this.Size = new Vector2(810, 520);
this.SizeCondition = ImGuiCond.Always;
this.SizeCondition = ImGuiCond.FirstUseEver;
}
/// <inheritdoc/>

View file

@ -51,7 +51,8 @@
</PropertyGroup>
<PropertyGroup Label="Warnings">
<NoWarn>IDE1006;CS1591;CS1701;CS1702</NoWarn>
<NoWarn>IDE0003;IDE0044;IDE1006;CS1591;CS1701;CS1702</NoWarn>
<!-- IDE0003 - Name can be simplified -->
<!-- IDE1006 - Naming violation -->
<!-- CS1591 - Missing XML comment for publicly visible type or member -->
<!-- CS1701 - Runtime policy may be needed -->
@ -59,9 +60,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Iced" Version="1.12.0" />
<PackageReference Include="Iced" Version="1.13.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="PeNet" Version="2.6.3" />
<PackageReference Include="PeNet" Version="2.6.4" />
<PackageReference Include="Reloaded.Memory" Version="4.1.1" />
<PackageReference Include="Reloaded.Memory.Buffers" Version="1.3.5" />
<PackageReference Include="Serilog" Version="2.10.0" />
@ -84,6 +85,5 @@
<Compile Include="..\Dalamud\DalamudStartInfo.cs" />
<Compile Include="..\Dalamud\Game\GameVersion.cs" />
<Compile Include="..\Dalamud\Game\GameVersionConverter.cs" />
<Compile Include="..\Dalamud\Interface\Internal\SerilogEventSink.cs" />
</ItemGroup>
</Project>

View file

@ -8,7 +8,6 @@ using System.Text;
using System.Threading;
using Dalamud.Game;
using Dalamud.Interface.Internal;
using Newtonsoft.Json;
using Reloaded.Memory.Buffers;
using Serilog;
@ -129,13 +128,58 @@ namespace Dalamud.Injector
levelSwitch.MinimumLevel = LogEventLevel.Information;
#endif
CullLogFile(logPath, 1 * 1024 * 1024);
Log.Logger = new LoggerConfiguration()
.WriteTo.Async(a => a.File(logPath))
.WriteTo.Sink(SerilogEventSink.Instance)
.MinimumLevel.ControlledBy(levelSwitch)
.CreateLogger();
}
private static void CullLogFile(string logPath, int cullingFileSize)
{
try
{
var bufferSize = 4096;
var logFile = new FileInfo(logPath);
if (!logFile.Exists)
logFile.Create();
if (logFile.Length <= cullingFileSize)
return;
var amountToCull = logFile.Length - cullingFileSize;
if (amountToCull < bufferSize)
return;
using var reader = new BinaryReader(logFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
using var writer = new BinaryWriter(logFile.Open(FileMode.Open, FileAccess.Write, FileShare.ReadWrite));
reader.BaseStream.Seek(amountToCull, SeekOrigin.Begin);
var read = -1;
var total = 0;
var buffer = new byte[bufferSize];
while (read != 0)
{
read = reader.Read(buffer, 0, buffer.Length);
writer.Write(buffer, 0, read);
total += read;
}
writer.BaseStream.SetLength(total);
}
catch (Exception ex)
{
var caption = "XIVLauncher Error";
var message = $"Log cull threw an exception: {ex.Message}\n{ex.StackTrace ?? string.Empty}";
_ = MessageBoxW(IntPtr.Zero, message, caption, MessageBoxType.IconError | MessageBoxType.Ok);
}
}
private static Process GetProcess(string arg)
{
Process process;

View file

@ -11,6 +11,7 @@ using PeNet.Header.Pe;
using Reloaded.Memory.Buffers;
using Reloaded.Memory.Sources;
using Reloaded.Memory.Utilities;
using Serilog;
using static Dalamud.Injector.NativeFunctions;
using static Iced.Intel.AssemblerRegisters;
@ -27,7 +28,7 @@ namespace Dalamud.Injector
private readonly Process targetProcess;
private readonly ExternalMemory extMemory;
private readonly CircularBuffer circularBuffer;
private readonly PrivateMemoryBuffer privateBuffer;
private readonly PrivateMemoryBuffer memoryBuffer;
private IntPtr loadLibraryShellPtr;
private IntPtr loadLibraryRetPtr;
@ -45,7 +46,7 @@ namespace Dalamud.Injector
this.extMemory = new ExternalMemory(targetProcess);
this.circularBuffer = new CircularBuffer(4096, this.extMemory);
this.privateBuffer = new MemoryBufferHelper(targetProcess).CreatePrivateMemoryBuffer(4096);
this.memoryBuffer = new MemoryBufferHelper(targetProcess).CreatePrivateMemoryBuffer(4096);
using var kernel32Module = this.GetProcessModule("KERNEL32.DLL");
var kernel32PeFile = new PeFile(kernel32Module.FileName);
@ -67,7 +68,7 @@ namespace Dalamud.Injector
this.targetProcess?.Dispose();
this.circularBuffer?.Dispose();
this.privateBuffer?.Dispose();
this.memoryBuffer?.Dispose();
}
/// <summary>
@ -82,6 +83,8 @@ namespace Dalamud.Injector
if (lpParameter == IntPtr.Zero)
throw new Exception("Unable to allocate LoadLibraryW parameter");
Log.Verbose($"CreateRemoteThread: call 0x{this.loadLibraryShellPtr.ToInt64():X} with 0x{lpParameter.ToInt64():X}");
var threadHandle = CreateRemoteThread(
this.targetProcess.Handle,
IntPtr.Zero,
@ -93,7 +96,7 @@ namespace Dalamud.Injector
_ = WaitForSingleObject(threadHandle, uint.MaxValue);
this.extMemory.Read(this.loadLibraryRetPtr, out address);
address = this.extMemory.Read<IntPtr>(this.loadLibraryRetPtr);
if (address == IntPtr.Zero)
throw new Exception($"Error calling LoadLibraryW with {modulePath}");
@ -107,7 +110,8 @@ namespace Dalamud.Injector
/// <param name="address">Address to the function.</param>
public void GetFunctionAddress(IntPtr module, string functionName, out IntPtr address)
{
var getProcAddressParams = new GetProcAddressParams(module, this.WriteNullTerminatedASCIIString(functionName));
var functionNamePtr = this.WriteNullTerminatedASCIIString(functionName);
var getProcAddressParams = new GetProcAddressParams(module, functionNamePtr);
var lpParameter = this.circularBuffer.Add(ref getProcAddressParams);
if (lpParameter == IntPtr.Zero)
@ -151,19 +155,25 @@ namespace Dalamud.Injector
_ = WaitForSingleObject(threadHandle, uint.MaxValue);
GetExitCodeThread(threadHandle, out exitCode);
CloseHandle(threadHandle);
}
private void SetupLoadLibrary(ProcessModule kernel32Module, ExportFunction[] kernel32Exports)
{
var offset = this.GetExportedFunctionOffset(kernel32Exports, "LoadLibraryW");
var functionAddr = kernel32Module.BaseAddress + (int)offset;
var functionPtr = this.privateBuffer.Add(ref functionAddr);
Log.Verbose($"LoadLibraryW: 0x{functionAddr.ToInt64():X}");
var functionPtr = this.memoryBuffer.Add(ref functionAddr);
Log.Verbose($"LoadLibraryPtr: 0x{functionPtr.ToInt64():X}");
if (functionPtr == IntPtr.Zero)
throw new Exception("Unable to allocate LoadLibraryW function ptr");
var dummy = 0L;
this.loadLibraryRetPtr = this.privateBuffer.Add(ref dummy);
var dummy = IntPtr.Zero;
this.loadLibraryRetPtr = this.memoryBuffer.Add(ref dummy);
Log.Verbose($"LoadLibraryRetPtr: 0x{this.loadLibraryRetPtr.ToInt64():X}");
if (this.loadLibraryRetPtr == IntPtr.Zero)
throw new Exception("Unable to allocate LoadLibraryW return value");
@ -180,23 +190,41 @@ namespace Dalamud.Injector
asm.ret(); // ret // Restore stack ptr. (Callee cleanup)
var bytes = this.Assemble(asm);
this.loadLibraryShellPtr = this.privateBuffer.Add(bytes);
this.loadLibraryShellPtr = this.memoryBuffer.Add(bytes);
Log.Verbose($"LoadLibraryShellPtr: 0x{this.loadLibraryShellPtr.ToInt64():X}");
if (this.loadLibraryShellPtr == IntPtr.Zero)
throw new Exception("Unable to allocate LoadLibraryW shellcode");
this.extMemory.ChangePermission(this.loadLibraryShellPtr, bytes.Length, Reloaded.Memory.Kernel32.Kernel32.MEM_PROTECTION.PAGE_EXECUTE_READWRITE);
#if DEBUG
var outFunctionPtr = this.extMemory.Read<IntPtr>(functionPtr);
Log.Verbose($"LoadLibraryPtr: {this.GetResultMarker(outFunctionPtr == functionAddr)}");
var outRetPtr = this.extMemory.Read<IntPtr>(this.loadLibraryRetPtr);
Log.Verbose($"LoadLibraryRet: {this.GetResultMarker(dummy == outRetPtr)}");
this.extMemory.ReadRaw(this.loadLibraryShellPtr, out var outBytes, bytes.Length);
Log.Verbose($"LoadLibraryShellPtr: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))}");
#endif
}
private void SetupGetProcAddress(ProcessModule kernel32Module, ExportFunction[] kernel32Exports)
{
var offset = this.GetExportedFunctionOffset(kernel32Exports, "GetProcAddress");
var functionAddr = kernel32Module.BaseAddress + (int)offset;
var functionPtr = this.privateBuffer.Add(ref functionAddr);
Log.Verbose($"GetProcAddress: 0x{functionAddr.ToInt64():X}");
var functionPtr = this.memoryBuffer.Add(ref functionAddr);
Log.Verbose($"GetProcAddressPtr: 0x{functionPtr.ToInt64():X}");
if (functionPtr == IntPtr.Zero)
throw new Exception("Unable to allocate GetProcAddress function ptr");
var dummy = 0L;
this.getProcAddressRetPtr = this.privateBuffer.Add(ref dummy);
var dummy = IntPtr.Zero;
this.getProcAddressRetPtr = this.memoryBuffer.Add(ref dummy);
Log.Verbose($"GetProcAddressRetPtr: 0x{this.loadLibraryRetPtr.ToInt64():X}");
if (this.getProcAddressRetPtr == IntPtr.Zero)
throw new Exception("Unable to allocate GetProcAddress return value");
@ -215,10 +243,24 @@ namespace Dalamud.Injector
asm.ret(); // ret // Restore stack ptr. (Callee cleanup)
var bytes = this.Assemble(asm);
this.getProcAddressShellPtr = this.privateBuffer.Add(bytes);
this.getProcAddressShellPtr = this.memoryBuffer.Add(bytes);
Log.Verbose($"GetProcAddressShellPtr: 0x{this.getProcAddressShellPtr.ToInt64():X}");
if (this.getProcAddressShellPtr == IntPtr.Zero)
throw new Exception("Unable to allocate GetProcAddress shellcode");
this.extMemory.ChangePermission(this.getProcAddressShellPtr, bytes.Length, Reloaded.Memory.Kernel32.Kernel32.MEM_PROTECTION.PAGE_EXECUTE_READWRITE);
#if DEBUG
var outFunctionPtr = this.extMemory.Read<IntPtr>(functionPtr);
Log.Verbose($"GetProcAddressPtr: {this.GetResultMarker(outFunctionPtr == functionAddr)}");
var outRetPtr = this.extMemory.Read<IntPtr>(this.loadLibraryRetPtr);
Log.Verbose($"GetProcAddressRet: {this.GetResultMarker(dummy == outRetPtr)}");
this.extMemory.ReadRaw(this.getProcAddressShellPtr, out var outBytes, bytes.Length);
Log.Verbose($"GetProcAddressShellPtr: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))}");
#endif
}
private byte[] Assemble(Assembler assembler)
@ -264,28 +306,42 @@ namespace Dalamud.Injector
return exportFunction.Address;
}
private IntPtr WriteNullTerminatedASCIIString(string libraryPath)
private IntPtr WriteNullTerminatedASCIIString(string value)
{
var libraryNameBytes = Encoding.ASCII.GetBytes(libraryPath + '\0');
var value = this.circularBuffer.Add(libraryNameBytes);
var bytes = Encoding.ASCII.GetBytes(value + '\0');
var address = this.circularBuffer.Add(bytes);
if (value == IntPtr.Zero)
if (address == IntPtr.Zero)
throw new Exception("Unable to write ASCII string to buffer");
return value;
#if DEBUG
this.extMemory.ReadRaw(address, out var outBytes, bytes.Length);
Log.Verbose($"WriteASCII: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))} 0x{address.ToInt64():X} {value}");
#endif
return address;
}
private IntPtr WriteNullTerminatedUnicodeString(string libraryPath)
private IntPtr WriteNullTerminatedUnicodeString(string value)
{
var libraryNameBytes = Encoding.Unicode.GetBytes(libraryPath + '\0');
var value = this.circularBuffer.Add(libraryNameBytes);
var bytes = Encoding.Unicode.GetBytes(value + '\0');
var address = this.circularBuffer.Add(bytes);
if (value == IntPtr.Zero)
if (address == IntPtr.Zero)
throw new Exception("Unable to write Unicode string to buffer");
return value;
#if DEBUG
this.extMemory.ReadRaw(address, out var outBytes, bytes.Length);
Log.Verbose($"WriteUnicode: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))} 0x{address.ToInt64():X} {value}");
#endif
return address;
}
#if DEBUG
private string GetResultMarker(bool result) => result ? "✅" : "❌";
#endif
[StructLayout(LayoutKind.Sequential)]
private struct GetProcAddressParams
{

View file

@ -582,6 +582,22 @@ namespace Dalamud.Injector
WAIT_FAILED = 0xFFFFFFF,
}
/// <summary>
/// Closes an open object handle.
/// </summary>
/// <param name="hObject">
/// A valid handle to an open object.
/// </param>
/// <returns>
/// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error
/// information, call GetLastError. If the application is running under a debugger, the function will throw an exception if it receives
/// either a handle value that is not valid or a pseudo-handle value. This can happen if you close a handle twice, or if you call
/// CloseHandle on a handle returned by the FindFirstFile function instead of calling the FindClose function.
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
/// <summary>
/// Creates a thread that runs in the virtual address space of another process. Use the CreateRemoteThreadEx function
/// to create a thread that runs in the virtual address space of another process and optionally specify extended attributes.
@ -651,6 +667,34 @@ namespace Dalamud.Injector
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
/// <summary>
/// Opens an existing local process object.
/// </summary>
/// <param name="dwDesiredAccess">
/// The access to the process object. This access right is checked against the security descriptor for the process. This parameter can be one or
/// more of the process access rights. If the caller has enabled the SeDebugPrivilege privilege, the requested access is granted regardless of the
/// contents of the security descriptor.
/// </param>
/// <param name="bInheritHandle">
/// If this value is TRUE, processes created by this process will inherit the handle. Otherwise, the processes do not inherit this handle.
/// </param>
/// <param name="dwProcessId">
/// The identifier of the local process to be opened. If the specified process is the System Idle Process(0x00000000), the function fails and the
/// last error code is ERROR_INVALID_PARAMETER.If the specified process is the System process or one of the Client Server Run-Time Subsystem(CSRSS)
/// processes, this function fails and the last error code is ERROR_ACCESS_DENIED because their access restrictions prevent user-level code from
/// opening them. If you are using GetCurrentProcessId as an argument to this function, consider using GetCurrentProcess instead of OpenProcess, for
/// improved performance.
/// </param>
/// <returns>
/// If the function succeeds, the return value is an open handle to the specified process.
/// If the function fails, the return value is NULL.To get extended error information, call GetLastError.
/// </returns>
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
ProcessAccessFlags dwDesiredAccess,
bool bInheritHandle,
int dwProcessId);
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex.
/// Reserves, commits, or changes the state of a region of memory within the virtual address space of a specified process.

View file

@ -1,79 +0,0 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using static Dalamud.Injector.NativeFunctions;
namespace Dalamud.Injector
{
/// <summary>
/// Pin an arbitrary string to a remote process.
/// </summary>
internal class RemotePinnedData : IDisposable
{
private readonly Process process;
private readonly byte[] data;
private readonly IntPtr allocAddr;
/// <summary>
/// Initializes a new instance of the <see cref="RemotePinnedData"/> class.
/// </summary>
/// <param name="process">Process to write in.</param>
/// <param name="data">Data to write.</param>
public unsafe RemotePinnedData(Process process, byte[] data)
{
this.process = process;
this.data = data;
this.allocAddr = VirtualAllocEx(
this.process.Handle,
IntPtr.Zero,
this.data.Length,
AllocationType.Commit,
MemoryProtection.ReadWrite);
if (this.allocAddr == IntPtr.Zero || Marshal.GetLastWin32Error() != 0)
{
throw new Exception("Error allocating memory");
}
var result = WriteProcessMemory(
this.process.Handle,
this.allocAddr,
this.data,
this.data.Length,
out _);
if (!result || Marshal.GetLastWin32Error() != 0)
{
throw new Exception("Error writing memory");
}
}
/// <summary>
/// Gets the address of the pinned data.
/// </summary>
public IntPtr Address => this.allocAddr;
/// <inheritdoc/>
public void Dispose()
{
if (this.allocAddr == IntPtr.Zero)
{
return;
}
var result = VirtualFreeEx(
this.process.Handle,
this.allocAddr,
0,
AllocationType.Release);
if (!result || Marshal.GetLastWin32Error() != 0)
{
throw new Exception("Error freeing memory");
}
}
}
}

View file

@ -47,12 +47,12 @@ namespace Dalamud.Configuration.Internal
/// <summary>
/// Gets or sets the language code to load Dalamud localization with.
/// </summary>
public string LanguageOverride { get; set; }
public string LanguageOverride { get; set; } = null;
/// <summary>
/// Gets or sets the last loaded Dalamud version.
/// </summary>
public string LastVersion { get; set; }
public string LastVersion { get; set; } = null;
/// <summary>
/// Gets or sets the chat type used by default for plugin messages.
@ -62,17 +62,17 @@ namespace Dalamud.Configuration.Internal
/// <summary>
/// Gets or sets a value indicating whether or not plugin testing builds should be shown.
/// </summary>
public bool DoPluginTest { get; set; }
public bool DoPluginTest { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether or not Dalamud testing builds should be used.
/// </summary>
public bool DoDalamudTest { get; set; }
public bool DoDalamudTest { get; set; } = false;
/// <summary>
/// Gets or sets a value indicating whether or not XL should download the Dalamud .NET runtime.
/// </summary>
public bool DoDalamudRuntime { get; set; }
public bool DoDalamudRuntime { get; set; } = false;
/// <summary>
/// Gets or sets a list of custom repos.

View file

@ -59,9 +59,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CheapLoc" Version="1.1.5" />
<PackageReference Include="JetBrains.Annotations" Version="2021.1.0" />
<PackageReference Include="Lib.Harmony" Version="2.1.0" />
<PackageReference Include="CheapLoc" Version="1.1.6" />
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" />
<PackageReference Include="Lib.Harmony" Version="2.1.1" />
<PackageReference Include="Lumina" Version="3.3.0" />
<PackageReference Include="Lumina.Excel" Version="5.50.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />

View file

@ -6,12 +6,14 @@ using System.Threading;
using System.Threading.Tasks;
using Dalamud.Configuration.Internal;
using Dalamud.Interface.Internal;
using Dalamud.Logging.Internal;
using Newtonsoft.Json;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using static Dalamud.NativeFunctions;
namespace Dalamud
{
/// <summary>
@ -43,11 +45,16 @@ namespace Dalamud
/// <param name="info">The <see cref="DalamudStartInfo"/> containing information needed to initialize Dalamud.</param>
private static void RunThread(DalamudStartInfo info)
{
// Setup logger
var levelSwitch = InitLogging(info.WorkingDirectory);
// Load configuration first to get some early persistent state, like log level
var configuration = DalamudConfiguration.Load(info.ConfigurationPath);
// Setup logger
var levelSwitch = InitLogging(info.WorkingDirectory, configuration);
// Set the appropriate logging level from the configuration
#if !DEBUG
levelSwitch.MinimumLevel = configuration.LogLevel;
#endif
// Log any unhandled exception.
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
@ -88,30 +95,96 @@ namespace Dalamud
}
}
private static LoggingLevelSwitch InitLogging(string baseDirectory, DalamudConfiguration configuration)
private static LoggingLevelSwitch InitLogging(string baseDirectory)
{
#if DEBUG
var logPath = Path.Combine(baseDirectory, "dalamud.log");
var oldPath = Path.Combine(baseDirectory, "dalamud.log.old");
#else
var logPath = Path.Combine(baseDirectory, "..", "..", "..", "dalamud.log");
var oldPath = Path.Combine(baseDirectory, "..", "..", "..", "dalamud.log.old");
#endif
var levelSwitch = new LoggingLevelSwitch();
CullLogFile(logPath, oldPath, 1 * 1024 * 1024);
CullLogFile(oldPath, null, 10 * 1024 * 1024);
#if DEBUG
levelSwitch.MinimumLevel = LogEventLevel.Verbose;
#else
levelSwitch.MinimumLevel = configuration.LogLevel;
#endif
var levelSwitch = new LoggingLevelSwitch(LogEventLevel.Verbose);
Log.Logger = new LoggerConfiguration()
.WriteTo.Async(a => a.File(logPath, fileSizeLimitBytes: 5 * 1024 * 1024, rollOnFileSizeLimit: true))
.WriteTo.Sink(SerilogEventSink.Instance)
.MinimumLevel.ControlledBy(levelSwitch)
.CreateLogger();
.WriteTo.Async(a => a.File(logPath))
.WriteTo.Sink(SerilogEventSink.Instance)
.MinimumLevel.ControlledBy(levelSwitch)
.CreateLogger();
return levelSwitch;
}
private static void CullLogFile(string logPath, string? oldPath, int cullingFileSize)
{
try
{
var bufferSize = 4096;
var logFile = new FileInfo(logPath);
if (!logFile.Exists)
logFile.Create();
if (logFile.Length <= cullingFileSize)
return;
var amountToCull = logFile.Length - cullingFileSize;
if (amountToCull < bufferSize)
return;
if (oldPath != null)
{
var oldFile = new FileInfo(oldPath);
if (!oldFile.Exists)
oldFile.Create();
using var reader = new BinaryReader(logFile.Open(FileMode.Open, FileAccess.Read));
using var writer = new BinaryWriter(oldFile.Open(FileMode.Append, FileAccess.Write));
var read = -1;
var total = 0;
var buffer = new byte[bufferSize];
while (read != 0 && total < amountToCull)
{
read = reader.Read(buffer, 0, buffer.Length);
writer.Write(buffer, 0, read);
total += read;
}
}
{
using var reader = new BinaryReader(logFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
using var writer = new BinaryWriter(logFile.Open(FileMode.Open, FileAccess.Write, FileShare.ReadWrite));
reader.BaseStream.Seek(amountToCull, SeekOrigin.Begin);
var read = -1;
var total = 0;
var buffer = new byte[bufferSize];
while (read != 0)
{
read = reader.Read(buffer, 0, buffer.Length);
writer.Write(buffer, 0, read);
total += read;
}
writer.BaseStream.SetLength(total);
}
}
catch (Exception ex)
{
var caption = "XIVLauncher Error";
var message = $"Log cull threw an exception: {ex.Message}\n{ex.StackTrace ?? string.Empty}";
_ = MessageBoxW(IntPtr.Zero, message, caption, MessageBoxType.IconError | MessageBoxType.Ok);
}
}
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs args)
{
switch (args.ExceptionObject)

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Dalamud.Logging.Internal;
using Dalamud.Memory;
using Microsoft.Win32;

View file

@ -6,7 +6,8 @@ using System.Runtime.InteropServices;
using Dalamud.Interface.Internal.Windows;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Logging;
using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal;
using ImGuiNET;
using Serilog.Events;

View file

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Dalamud.Plugin;
using Dalamud.Logging;
namespace Dalamud.Interface.Internal.Scratchpad
{

View file

@ -273,8 +273,11 @@ namespace Dalamud.Interface.Internal
ImGui.Text($"texture type: {texType} part_id={imageNode->PartId} part_id_count={imageNode->PartsList->PartCount}");
if (texType == TextureType.Resource)
{
var texFileNamePtr = textureInfo->AtkTexture.Resource->TexFileResourceHandle->ResourceHandle.FileName;
var texString = Marshal.PtrToStringAnsi(new IntPtr(texFileNamePtr));
var texFileNameStdString = &textureInfo->AtkTexture.Resource->TexFileResourceHandle->ResourceHandle.FileName;
var texString = texFileNameStdString->Length < 16
? Marshal.PtrToStringAnsi((IntPtr)texFileNameStdString->Buffer)
: Marshal.PtrToStringAnsi((IntPtr)texFileNameStdString->BufferPtr);
ImGui.Text($"texture path: {texString}");
var kernelTexture = textureInfo->AtkTexture.Resource->KernelTextureObject;
@ -327,7 +330,7 @@ namespace Dalamud.Interface.Internal
var childCount = componentInfo.NodeListCount;
var objectInfo = (ULDComponentInfo*)componentInfo.Objects;
var objectInfo = (AtkUldComponentInfo*)componentInfo.Objects;
if (ImGui.TreeNode($"{treePrefix}{objectInfo->ComponentType} Component Node (ptr = {(long)node:X}, component ptr = {(long)compNode->Component:X}) child count = {childCount} ###{(long)node}"))
{
if (ImGui.IsItemHovered())

View file

@ -6,10 +6,9 @@ using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using Dalamud.Configuration.Internal;
using Dalamud.Game.Command;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Windowing;
using Dalamud.Logging.Internal;
using ImGuiNET;
using Serilog;
using Serilog.Events;
@ -307,7 +306,7 @@ namespace Dalamud.Interface.Internal.Windows
try
{
this.historyPos = -1;
for (int i = this.history.Count - 1; i >= 0; i--)
for (var i = this.history.Count - 1; i >= 0; i--)
{
if (this.history[i] == this.commandText)
{

View file

@ -13,6 +13,7 @@ using CheapLoc;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Windowing;
using Dalamud.Logging.Internal;
using Dalamud.Plugin;
using Dalamud.Plugin.Internal;
using Dalamud.Plugin.Internal.Exceptions;

View file

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using ImGuiNET;
using Serilog;
namespace Dalamud.Interface.Windowing
{

View file

@ -1,6 +1,6 @@
using System;
namespace Dalamud
namespace Dalamud.Logging.Internal
{
/// <summary>
/// Class offering various methods to allow for logging in Dalamud modules.

View file

@ -3,7 +3,7 @@ using System;
using Serilog.Core;
using Serilog.Events;
namespace Dalamud.Interface.Internal
namespace Dalamud.Logging.Internal
{
/// <summary>
/// Serilog event sink.

View file

@ -0,0 +1,113 @@
using System;
using System.Reflection;
namespace Dalamud.Logging
{
/// <summary>
/// Class offering various static methods to allow for logging in plugins.
/// </summary>
public static class PluginLog
{
/// <summary>
/// Log a templated verbose message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Verbose(string messageTemplate, params object[] values)
=> Serilog.Log.Verbose($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated verbose message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Verbose(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Verbose(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated debug message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Debug(string messageTemplate, params object[] values)
=> Serilog.Log.Debug($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated debug message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Debug(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Debug(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated information message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Information(string messageTemplate, params object[] values)
=> Serilog.Log.Information($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated information message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Information(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Information(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated warning message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Warning(string messageTemplate, params object[] values)
=> Serilog.Log.Warning($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated warning message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Warning(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Warning(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated error message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Error(string messageTemplate, params object[] values)
=> Serilog.Log.Error($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated error message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Error(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Error(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated fatal message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Fatal(string messageTemplate, params object[] values)
=> Serilog.Log.Fatal($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated fatal message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Fatal(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Fatal(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
}
}

View file

@ -347,35 +347,6 @@ namespace Dalamud.Plugin
#endregion
#region Logging
/// <summary>
/// Log a templated message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
[Obsolete("Use PluginLog")]
public void Log(string messageTemplate, params object[] values) => Serilog.Log.Information(messageTemplate, values);
/// <summary>
/// Log a templated error message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
[Obsolete("Use PluginLog")]
public void LogError(string messageTemplate, params object[] values) => Serilog.Log.Error(messageTemplate, values);
/// <summary>
/// Log a templated error message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
[Obsolete("Use PluginLog")]
public void LogError(Exception exception, string messageTemplate, params object[] values) => Serilog.Log.Error(exception, messageTemplate, values);
#endregion
/// <summary>
/// Unregister your plugin and dispose all references. You have to call this when your IDalamudPlugin is disposed.
/// </summary>

View file

@ -1,10 +1,10 @@
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Configuration.Internal;
using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal.Types;
namespace Dalamud.Plugin.Internal

View file

@ -3,8 +3,8 @@ using System.IO;
using System.Linq;
using System.Reflection;
using Dalamud.Configuration.Internal;
using Dalamud.Game;
using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal.Exceptions;
using Dalamud.Plugin.Internal.Types;
using McMaster.NETCore.Plugins;

View file

@ -14,6 +14,7 @@ using System.Threading.Tasks;
using CheapLoc;
using Dalamud.Configuration;
using Dalamud.Game.Text;
using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal.Exceptions;
using Dalamud.Plugin.Internal.Types;
using HarmonyLib;

View file

@ -1,9 +1,9 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Dalamud.Logging.Internal;
using Dalamud.Plugin.Internal.Types;
using Newtonsoft.Json;

View file

@ -1,240 +0,0 @@
using System;
using System.Reflection;
namespace Dalamud.Plugin
{
/// <summary>
/// Class offering various static methods to allow for logging in plugins.
/// </summary>
public static class PluginLog
{
#region "Log" prefixed Serilog style methods
/// <summary>
/// Log a templated message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Log(string messageTemplate, params object[] values)
=> Serilog.Log.Information($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Log(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Information(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated verbose message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogVerbose(string messageTemplate, params object[] values)
=> Serilog.Log.Verbose($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated verbose message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogVerbose(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Verbose(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated debug message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogDebug(string messageTemplate, params object[] values)
=> Serilog.Log.Debug($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated debug message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogDebug(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Debug(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated information message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogInformation(string messageTemplate, params object[] values)
=> Serilog.Log.Information($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated information message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogInformation(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Information(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated warning message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogWarning(string messageTemplate, params object[] values)
=> Serilog.Log.Warning($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated warning message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogWarning(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Warning(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated error message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogError(string messageTemplate, params object[] values)
=> Serilog.Log.Error($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated error message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogError(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Error(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated fatal message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogFatal(string messageTemplate, params object[] values)
=> Serilog.Log.Fatal($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated fatal message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void LogFatal(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Fatal(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
#endregion
#region Serilog style methods
/// <summary>
/// Log a templated verbose message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Verbose(string messageTemplate, params object[] values)
=> Serilog.Log.Verbose($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated verbose message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Verbose(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Verbose(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated debug message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Debug(string messageTemplate, params object[] values)
=> Serilog.Log.Debug($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated debug message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Debug(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Debug(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated information message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Information(string messageTemplate, params object[] values)
=> Serilog.Log.Information($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated information message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Information(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Information(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated warning message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Warning(string messageTemplate, params object[] values)
=> Serilog.Log.Warning($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated warning message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Warning(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Warning(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated error message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Error(string messageTemplate, params object[] values)
=> Serilog.Log.Error($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated error message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Error(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Error(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated fatal message to the in-game debug log.
/// </summary>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Fatal(string messageTemplate, params object[] values)
=> Serilog.Log.Fatal($"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
/// <summary>
/// Log a templated fatal message to the in-game debug log.
/// </summary>
/// <param name="exception">The exception that caused the error.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="values">Values to log.</param>
public static void Fatal(Exception exception, string messageTemplate, params object[] values)
=> Serilog.Log.Fatal(exception, $"[{Assembly.GetCallingAssembly().GetName().Name}] {messageTemplate}", values);
#endregion
}
}

Binary file not shown.

View file

@ -9,6 +9,6 @@
<NukeScriptDirectory>..</NukeScriptDirectory>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nuke.Common" Version="5.1.3" />
<PackageReference Include="Nuke.Common" Version="5.3.0" />
</ItemGroup>
</Project>

@ -1 +1 @@
Subproject commit e3bec118909b0eafdd0ee7c3312c646ce0c3f3dd
Subproject commit cbbea8ea911dda876d4a996f214f337e4db88a01

@ -1 +1 @@
Subproject commit 3724190279a24f638c9627688a837f9b61457668
Subproject commit c6ed214c2f7b269c007e5f7f70788745e45e9eb3

@ -1 +0,0 @@
Subproject commit 7fc56bc0a240030d4736e6b16da33b08c73c3ba4