mirror of
https://github.com/BepInEx/BepInEx.git
synced 2026-10-20 01:46:13 +00:00
Completely change logger workflow
This commit is contained in:
22 files changed
+479
-428
No files matched your search
@@ -42,6 +42,7 @@
|
||||
<Compile Include="Entrypoint.cs" />
|
||||
<Compile Include="Patcher\PatcherPlugin.cs" />
|
||||
<Compile Include="Preloader.cs" />
|
||||
<Compile Include="PreloaderLogWriter.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="UnityPatches.cs" />
|
||||
</ItemGroup>
|
||||
@@ -59,5 +60,8 @@
|
||||
<Name>Harmony</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Logger\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -22,7 +22,7 @@ namespace BepInEx.Preloader
|
||||
/// <summary>
|
||||
/// The log writer that is specific to the preloader.
|
||||
/// </summary>
|
||||
private static PreloaderLogWriter PreloaderLog { get; set; }
|
||||
private static PreloaderConsoleListener PreloaderLog { get; set; }
|
||||
|
||||
public static void Run()
|
||||
{
|
||||
@@ -31,19 +31,20 @@ namespace BepInEx.Preloader
|
||||
AllocateConsole();
|
||||
|
||||
UnityPatches.Apply();
|
||||
|
||||
Logger.Sources.Add(TraceLogSource.CreateSource());
|
||||
|
||||
PreloaderLog =
|
||||
new PreloaderLogWriter(
|
||||
PreloaderLog = new PreloaderConsoleListener(
|
||||
Utility.SafeParseBool(Config.GetEntry("preloader-logconsole", "false", "BepInEx")));
|
||||
PreloaderLog.Enabled = true;
|
||||
|
||||
Logger.Listeners.Add(PreloaderLog);
|
||||
|
||||
|
||||
string consoleTile =
|
||||
$"BepInEx {typeof(Paths).Assembly.GetName().Version} - {Process.GetCurrentProcess().ProcessName}";
|
||||
ConsoleWindow.Title = consoleTile;
|
||||
|
||||
Logger.SetLogger(PreloaderLog);
|
||||
|
||||
PreloaderLog.WriteLine(consoleTile);
|
||||
Logger.LogMessage(consoleTile);
|
||||
|
||||
//See BuildInfoAttribute for more information about this section.
|
||||
object[] attributes = typeof(BuildInfoAttribute).Assembly.GetCustomAttributes(typeof(BuildInfoAttribute), false);
|
||||
@@ -52,10 +53,10 @@ namespace BepInEx.Preloader
|
||||
{
|
||||
var attribute = (BuildInfoAttribute)attributes[0];
|
||||
|
||||
PreloaderLog.WriteLine(attribute.Info);
|
||||
Logger.LogMessage(attribute.Info);
|
||||
}
|
||||
|
||||
Logger.Log(LogLevel.Message, "Preloader started");
|
||||
Logger.LogMessage("Preloader started");
|
||||
|
||||
string entrypointAssembly = Config.GetEntry("entrypoint-assembly", "UnityEngine.dll", "Preloader");
|
||||
|
||||
@@ -66,22 +67,33 @@ namespace BepInEx.Preloader
|
||||
AssemblyPatcher.PatchAndLoad(Paths.ManagedPath);
|
||||
|
||||
AssemblyPatcher.DisposePatchers();
|
||||
|
||||
Logger.LogMessage("Preloader finished");
|
||||
|
||||
UnityLogListener.WriteStringToUnityLog?.Invoke(PreloaderLog.ToString());
|
||||
|
||||
Logger.Listeners.Remove(PreloaderLog);
|
||||
Logger.Listeners.Add(new ConsoleLogListener());
|
||||
|
||||
PreloaderLog.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(LogLevel.Fatal, "Could not run preloader!");
|
||||
Logger.Log(LogLevel.Fatal, ex);
|
||||
|
||||
PreloaderLog.Enabled = false;
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Log(LogLevel.Fatal, "Could not run preloader!");
|
||||
Logger.Log(LogLevel.Fatal, ex);
|
||||
|
||||
PreloaderLog?.Dispose();
|
||||
|
||||
if (!ConsoleWindow.IsAttatched)
|
||||
{
|
||||
//if we've already attached the console, then the log will already be written to the console
|
||||
AllocateConsole();
|
||||
Console.Write(PreloaderLog);
|
||||
}
|
||||
|
||||
PreloaderLog = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -89,7 +101,7 @@ namespace BepInEx.Preloader
|
||||
Path.Combine(Paths.GameRootPath, $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"),
|
||||
PreloaderLog + "\r\n" + ex);
|
||||
|
||||
PreloaderLog.Dispose();
|
||||
PreloaderLog?.Dispose();
|
||||
PreloaderLog = null;
|
||||
}
|
||||
}
|
||||
@@ -191,8 +203,7 @@ namespace BepInEx.Preloader
|
||||
public static void PatchEntrypoint(ref AssemblyDefinition assembly)
|
||||
{
|
||||
if (assembly.MainModule.AssemblyReferences.Any(x => x.Name.Contains("BepInEx")))
|
||||
throw new Exception(
|
||||
"BepInEx has been detected to be patched! Please unpatch before using a patchless variant!");
|
||||
throw new Exception("BepInEx has been detected to be patched! Please unpatch before using a patchless variant!");
|
||||
|
||||
string entrypointType = Config.GetEntry("entrypoint-type", "Application", "Preloader");
|
||||
string entrypointMethod = Config.GetEntry("entrypoint-method", ".cctor", "Preloader");
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BepInEx.ConsoleUtil;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
public class PreloaderConsoleListener : ILogListener
|
||||
{
|
||||
public List<LogEventArgs> LogEvents { get; } = new List<LogEventArgs>();
|
||||
protected StringBuilder LogBuilder = new StringBuilder();
|
||||
|
||||
public static TextWriter StandardOut { get; set; }
|
||||
protected PreloaderConsoleSource LoggerSource { get; set; }
|
||||
|
||||
|
||||
public PreloaderConsoleListener(bool redirectConsole)
|
||||
{
|
||||
StandardOut = Console.Out;
|
||||
|
||||
if (redirectConsole)
|
||||
{
|
||||
LoggerSource = new PreloaderConsoleSource();
|
||||
|
||||
Logger.Sources.Add(LoggerSource);
|
||||
Console.SetOut(LoggerSource);
|
||||
}
|
||||
}
|
||||
|
||||
public void LogEvent(object sender, LogEventArgs eventArgs)
|
||||
{
|
||||
LogEvents.Add(eventArgs);
|
||||
|
||||
string log = $"[{eventArgs.Level}:{((ILogSource)sender).SourceName}] {eventArgs.Data}\r\n";
|
||||
|
||||
LogBuilder.Append(log);
|
||||
|
||||
Kon.ForegroundColor = eventArgs.Level.GetConsoleColor();
|
||||
ConsoleDirectWrite(log);
|
||||
Kon.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
|
||||
public void ConsoleDirectWrite(string value)
|
||||
{
|
||||
StandardOut.Write(value);
|
||||
}
|
||||
|
||||
public void ConsoleDirectWriteLine(string value)
|
||||
{
|
||||
StandardOut.WriteLine(value);
|
||||
}
|
||||
|
||||
public override string ToString() => LogBuilder.ToString();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
if (LoggerSource != null)
|
||||
{
|
||||
Console.SetOut(StandardOut);
|
||||
Logger.Sources.Remove(LoggerSource);
|
||||
LoggerSource.Dispose();
|
||||
LoggerSource = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PreloaderConsoleSource : TextWriter, ILogSource
|
||||
{
|
||||
public override Encoding Encoding { get; } = Console.OutputEncoding;
|
||||
|
||||
public string SourceName { get; } = "BepInEx Preloader";
|
||||
|
||||
public event EventHandler<LogEventArgs> LogEvent;
|
||||
|
||||
public override void Write(object value)
|
||||
=> LogEvent?.Invoke(this, new LogEventArgs(value, LogLevel.Info, this));
|
||||
|
||||
public override void Write(string value)
|
||||
=> Write((object)value);
|
||||
|
||||
public override void WriteLine() { }
|
||||
|
||||
public override void WriteLine(object value)
|
||||
=> Write(value);
|
||||
|
||||
public override void WriteLine(string value)
|
||||
=> Write(value);
|
||||
}
|
||||
}
|
||||
@@ -57,12 +57,16 @@
|
||||
<Compile Include="ConsoleUtil\SafeConsole.cs" />
|
||||
<Compile Include="Bootstrap\Chainloader.cs" />
|
||||
<Compile Include="Contract\BaseUnityPlugin.cs" />
|
||||
<Compile Include="Logging\BaseLogger.cs" />
|
||||
<Compile Include="Logging\LogEventArgs.cs" />
|
||||
<Compile Include="Logging\Logger.cs" />
|
||||
<Compile Include="Logging\LogLevel.cs" />
|
||||
<Compile Include="Logging\PreloaderLogWriter.cs" />
|
||||
<Compile Include="Logging\LoggerTraceListener.cs" />
|
||||
<Compile Include="Logging\UnityLogWriter.cs" />
|
||||
<Compile Include="Logging\ILogListener.cs" />
|
||||
<Compile Include="Logging\ILogSource.cs" />
|
||||
<Compile Include="Logging\ManualLogSource.cs" />
|
||||
<Compile Include="Logging\TraceLogSource.cs" />
|
||||
<Compile Include="Logging\ConsoleLogListener.cs" />
|
||||
<Compile Include="Logging\UnityLogListener.cs" />
|
||||
<Compile Include="Logging\UnityLogSource.cs" />
|
||||
<Compile Include="Paths.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Bootstrap\TypeLoader.cs" />
|
||||
|
||||
@@ -8,7 +8,6 @@ using System.Text;
|
||||
using BepInEx.Logging;
|
||||
using UnityEngine;
|
||||
using UnityInjector.ConsoleUtil;
|
||||
using UnityLogWriter = BepInEx.Logging.UnityLogWriter;
|
||||
|
||||
namespace BepInEx.Bootstrap
|
||||
{
|
||||
@@ -25,7 +24,7 @@ namespace BepInEx.Bootstrap
|
||||
/// <summary>
|
||||
/// The GameObject that all plugins are attached to as components.
|
||||
/// </summary>
|
||||
public static GameObject ManagerObject { get; private set; } = new GameObject("BepInEx_Manager");
|
||||
public static GameObject ManagerObject { get; private set; }
|
||||
|
||||
|
||||
private static bool _loaded = false;
|
||||
@@ -52,14 +51,23 @@ namespace BepInEx.Bootstrap
|
||||
|
||||
ConsoleEncoding.ConsoleCodePage = (uint)Encoding.UTF8.CodePage;
|
||||
Console.OutputEncoding = Encoding.UTF8;
|
||||
Logger.Listeners.Add(new ConsoleLogListener());
|
||||
}
|
||||
|
||||
UnityLogWriter unityLogWriter = new UnityLogWriter();
|
||||
//Fix for standard output getting overwritten by UnityLogger
|
||||
Console.SetOut(ConsoleWindow.StandardOut);
|
||||
|
||||
if (Logger.CurrentLogger != null && Logger.CurrentLogger is PreloaderLogWriter preloaderLogger)
|
||||
unityLogWriter.WriteToLog($"{preloaderLogger}\r\n");
|
||||
|
||||
Logger.SetLogger(unityLogWriter);
|
||||
Logger.Listeners.Add(new UnityLogListener());
|
||||
|
||||
if (!TraceLogSource.IsListening)
|
||||
Logger.Sources.Add(TraceLogSource.CreateSource());
|
||||
|
||||
if (bool.Parse(Config.GetEntry("chainloader-log-unity-messages", "false", "BepInEx")))
|
||||
Logger.Sources.Add(new UnityLogSource());
|
||||
|
||||
|
||||
Logger.Log(LogLevel.Message, "Chainloader ready");
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
@@ -83,10 +91,6 @@ namespace BepInEx.Bootstrap
|
||||
|
||||
try
|
||||
{
|
||||
if (bool.Parse(Config.GetEntry("chainloader-log-unity-messages", "false", "BepInEx")))
|
||||
UnityLogWriter.ListenUnityLogs();
|
||||
|
||||
|
||||
var productNameProp = typeof(Application).GetProperty("productName", BindingFlags.Public | BindingFlags.Static);
|
||||
if (productNameProp != null)
|
||||
ConsoleWindow.Title =
|
||||
@@ -94,6 +98,8 @@ namespace BepInEx.Bootstrap
|
||||
|
||||
Logger.Log(LogLevel.Message, "Chainloader started");
|
||||
|
||||
ManagerObject = new GameObject("BepInEx_Manager");
|
||||
|
||||
UnityEngine.Object.DontDestroyOnLoad(ManagerObject);
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace UnityInjector.ConsoleUtil
|
||||
// using only safe (managed) code
|
||||
// --------------------------------------------------
|
||||
|
||||
partial class ConsoleEncoding
|
||||
internal partial class ConsoleEncoding
|
||||
{
|
||||
private byte[] _byteBuffer = new byte[256];
|
||||
private char[] _charBuffer = new char[256];
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace UnityInjector.ConsoleUtil
|
||||
// http://jonskeet.uk/csharp/ebcdic/
|
||||
// using only safe (managed) code
|
||||
// --------------------------------------------------
|
||||
partial class ConsoleEncoding
|
||||
internal partial class ConsoleEncoding
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern uint GetConsoleOutputCP();
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace UnityInjector.ConsoleUtil
|
||||
// http://jonskeet.uk/csharp/ebcdic/
|
||||
// using only safe (managed) code
|
||||
// --------------------------------------------------
|
||||
public partial class ConsoleEncoding : Encoding
|
||||
internal partial class ConsoleEncoding : Encoding
|
||||
{
|
||||
private readonly uint _codePage;
|
||||
public override int CodePage => (int)_codePage;
|
||||
|
||||
@@ -10,12 +10,14 @@ using System.Text;
|
||||
|
||||
namespace UnityInjector.ConsoleUtil
|
||||
{
|
||||
public class ConsoleWindow
|
||||
internal class ConsoleWindow
|
||||
{
|
||||
public static bool IsAttatched { get; private set; }
|
||||
private static IntPtr _cOut;
|
||||
private static IntPtr _oOut;
|
||||
|
||||
public static TextWriter StandardOut { get; private set; }
|
||||
|
||||
public static void Attach()
|
||||
{
|
||||
if (IsAttatched)
|
||||
@@ -121,12 +123,13 @@ namespace UnityInjector.ConsoleUtil
|
||||
private static void Init()
|
||||
{
|
||||
var stdOut = Console.OpenStandardOutput();
|
||||
var stdWriter = new StreamWriter(stdOut, Encoding.Default)
|
||||
StandardOut = new StreamWriter(stdOut, Encoding.Default)
|
||||
{
|
||||
AutoFlush = true
|
||||
};
|
||||
Console.SetOut(stdWriter);
|
||||
Console.SetError(stdWriter);
|
||||
|
||||
Console.SetOut(StandardOut);
|
||||
Console.SetError(StandardOut);
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
|
||||
@@ -8,7 +8,7 @@ using System.Reflection;
|
||||
|
||||
namespace UnityInjector.ConsoleUtil
|
||||
{
|
||||
public static class SafeConsole
|
||||
internal static class SafeConsole
|
||||
{
|
||||
private static GetColorDelegate _getBackgroundColor;
|
||||
private static GetColorDelegate _getForegroundColor;
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// The base implementation of a logging class.
|
||||
/// </summary>
|
||||
public abstract class BaseLogger : TextWriter
|
||||
{
|
||||
protected BaseLogger()
|
||||
{
|
||||
DisplayedLevels = GetDefaultLogLevel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The encoding that the underlying text writer should use. Defaults to UTF-8 BOM.
|
||||
/// </summary>
|
||||
public override Encoding Encoding { get; } = new UTF8Encoding(true);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The handler for a entry logged event.
|
||||
/// </summary>
|
||||
/// <param name="entry">The text element of the log itself.</param>
|
||||
/// <param name="show">Whether or not it should be dislpayed to the user.</param>
|
||||
public delegate void EntryLoggedEventHandler(LogLevel level, object entry);
|
||||
|
||||
/// <summary>
|
||||
/// The listener event for an entry being logged.
|
||||
/// </summary>
|
||||
public event EntryLoggedEventHandler EntryLogged;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the default log level to use for this logger.
|
||||
/// </summary>
|
||||
/// <returns>The default log level to use.</returns>
|
||||
protected virtual LogLevel GetDefaultLogLevel()
|
||||
{
|
||||
LogLevel lowest;
|
||||
|
||||
try
|
||||
{
|
||||
lowest = (LogLevel)Enum.Parse(typeof(LogLevel), Config.GetEntry("logger-displayed-levels", nameof(LogLevel.Info), "BepInEx"));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return LogLevel.All;
|
||||
}
|
||||
|
||||
if (lowest == LogLevel.None)
|
||||
return LogLevel.None;
|
||||
|
||||
LogLevel value = lowest;
|
||||
|
||||
foreach (LogLevel e in Enum.GetValues(typeof(LogLevel)))
|
||||
{
|
||||
if (lowest > e)
|
||||
value |= e;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A filter which is used to specify which log levels are not ignored by the logger.
|
||||
/// </summary>
|
||||
public LogLevel DisplayedLevels { get; set; }
|
||||
|
||||
private readonly object logLockObj = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Logs an entry to the Logger instance.
|
||||
/// </summary>
|
||||
/// <param name="level">The level of the entry.</param>
|
||||
/// <param name="entry">The textual value of the entry.</param>
|
||||
public virtual void Log(LogLevel level, object entry)
|
||||
{
|
||||
if ((DisplayedLevels & level) != LogLevel.None)
|
||||
{
|
||||
lock (logLockObj)
|
||||
{
|
||||
EntryLogged?.Invoke(level, entry);
|
||||
WriteLine($"[{level.GetHighestLevel()}] {entry}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an entry to the Logger instance, with a <see cref="LogLevel"/> of Message.
|
||||
/// </summary>
|
||||
/// <param name="entry">The text value of this log entry.</param>
|
||||
public virtual void Log(object entry)
|
||||
{
|
||||
Log(LogLevel.Message, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using BepInEx.ConsoleUtil;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Logs entries using Unity specific outputs.
|
||||
/// </summary>
|
||||
public class ConsoleLogListener : ILogListener
|
||||
{
|
||||
public void LogEvent(object sender, LogEventArgs eventArgs)
|
||||
{
|
||||
string log = $"[{eventArgs.Level}:{((ILogSource)sender).SourceName}] {eventArgs.Data}\r\n";
|
||||
|
||||
Kon.ForegroundColor = eventArgs.Level.GetConsoleColor();
|
||||
Console.Write(log);
|
||||
Kon.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
public interface ILogListener : IDisposable
|
||||
{
|
||||
void LogEvent(object sender, LogEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
public interface ILogSource : IDisposable
|
||||
{
|
||||
string SourceName { get; }
|
||||
|
||||
event EventHandler<LogEventArgs> LogEvent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
public class LogEventArgs : EventArgs
|
||||
{
|
||||
public object Data { get; protected set; }
|
||||
|
||||
public LogLevel Level { get; protected set; }
|
||||
|
||||
public ILogSource Source { get; protected set; }
|
||||
|
||||
public LogEventArgs(object data, LogLevel level, ILogSource source)
|
||||
{
|
||||
Data = data;
|
||||
Level = level;
|
||||
Source = source;
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
-21
@@ -1,4 +1,6 @@
|
||||
using BepInEx.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BepInEx.Logging;
|
||||
|
||||
namespace BepInEx
|
||||
{
|
||||
@@ -7,37 +9,75 @@ namespace BepInEx
|
||||
/// </summary>
|
||||
public static class Logger
|
||||
{
|
||||
/// <summary>
|
||||
/// The current instance of a <see cref="BaseLogger"/> that is being used.
|
||||
/// </summary>
|
||||
public static BaseLogger CurrentLogger { get; set; }
|
||||
private static readonly ManualLogSource InternalLogSource = new ManualLogSource("BepInEx");
|
||||
|
||||
/// <summary>
|
||||
/// The listener event for an entry being logged.
|
||||
/// </summary>
|
||||
public static event BaseLogger.EntryLoggedEventHandler EntryLogged;
|
||||
public static ICollection<ILogListener> Listeners { get; } = new List<ILogListener>();
|
||||
|
||||
public static ICollection<ILogSource> Sources { get; } = new LogSourceCollection()
|
||||
{
|
||||
InternalLogSource
|
||||
};
|
||||
|
||||
private static void InternalLogEvent(object sender, LogEventArgs eventArgs)
|
||||
{
|
||||
foreach (var listener in Listeners)
|
||||
{
|
||||
listener?.LogEvent(sender, eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an entry to the current logger instance.
|
||||
/// </summary>
|
||||
/// <param name="level">The level of the entry.</param>
|
||||
/// <param name="entry">The textual value of the entry.</param>
|
||||
public static void Log(LogLevel level, object entry)
|
||||
public static void Log(LogLevel level, object data)
|
||||
{
|
||||
EntryLogged?.Invoke(level, entry);
|
||||
|
||||
CurrentLogger?.Log(level, entry);
|
||||
InternalLogEvent(InternalLogSource, new LogEventArgs(data, level, InternalLogSource));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the instance being used by the static <see cref="Logger"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">The instance to use in the static class.</param>
|
||||
public static void SetLogger(BaseLogger logger)
|
||||
{
|
||||
CurrentLogger?.Dispose();
|
||||
public static void LogFatal(object data) => Log(LogLevel.Fatal, data);
|
||||
public static void LogError(object data) => Log(LogLevel.Error, data);
|
||||
public static void LogWarning(object data) => Log(LogLevel.Warning, data);
|
||||
public static void LogMessage(object data) => Log(LogLevel.Message, data);
|
||||
public static void LogInfo(object data) => Log(LogLevel.Info, data);
|
||||
public static void LogDebug(object data) => Log(LogLevel.Debug, data);
|
||||
|
||||
CurrentLogger = logger;
|
||||
|
||||
private class LogSourceCollection : List<ILogSource>, ICollection<ILogSource>
|
||||
{
|
||||
void ICollection<ILogSource>.Add(ILogSource item)
|
||||
{
|
||||
if (item == null)
|
||||
throw new ArgumentNullException(nameof(item), "Log sources cannot be null when added to the source list.");
|
||||
|
||||
item.LogEvent += InternalLogEvent;
|
||||
|
||||
base.Add(item);
|
||||
}
|
||||
|
||||
void ICollection<ILogSource>.Clear()
|
||||
{
|
||||
foreach (var item in base.ToArray())
|
||||
{
|
||||
((ICollection<ILogSource>)this).Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
bool ICollection<ILogSource>.Remove(ILogSource item)
|
||||
{
|
||||
if (item == null)
|
||||
return false;
|
||||
|
||||
if (!base.Contains(item))
|
||||
return false;
|
||||
|
||||
item.LogEvent -= InternalLogEvent;
|
||||
|
||||
base.Remove(item);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
public class ManualLogSource : ILogSource
|
||||
{
|
||||
public string SourceName { get; }
|
||||
public event EventHandler<LogEventArgs> LogEvent;
|
||||
|
||||
public ManualLogSource(string sourceName)
|
||||
{
|
||||
SourceName = sourceName;
|
||||
}
|
||||
|
||||
public void Log(LogLevel level, object data)
|
||||
{
|
||||
LogEvent?.Invoke(this, new LogEventArgs(data, level, this));
|
||||
}
|
||||
|
||||
public void LogFatal(object data) => Log(LogLevel.Fatal, data);
|
||||
public void LogError(object data) => Log(LogLevel.Error, data);
|
||||
public void LogWarning(object data) => Log(LogLevel.Warning, data);
|
||||
public void LogMessage(object data) => Log(LogLevel.Message, data);
|
||||
public void LogInfo(object data) => Log(LogLevel.Info, data);
|
||||
public void LogDebug(object data) => Log(LogLevel.Debug, data);
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BepInEx.Bootstrap;
|
||||
using BepInEx.ConsoleUtil;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// A log writer specific to the <see cref="Preloader"/>.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="BaseLogger"/>
|
||||
public class PreloaderLogWriter : BaseLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="System.Text.StringBuilder"/> which contains all logged entries, so it may be passed onto another log writer.
|
||||
/// </summary>
|
||||
public StringBuilder StringBuilder { get; protected set; } = new StringBuilder();
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the log writer is redirecting console output, so it can be logged.
|
||||
/// </summary>
|
||||
public bool IsRedirectingConsole { get; protected set; }
|
||||
|
||||
protected TextWriter stdout;
|
||||
protected LoggerTraceListener traceListener;
|
||||
|
||||
private bool _enabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the log writer is writing and/or redirecting.
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
Enable();
|
||||
else
|
||||
Disable();
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="redirectConsole">Whether or not to redirect the console standard output.</param>
|
||||
public PreloaderLogWriter(bool redirectConsole)
|
||||
{
|
||||
IsRedirectingConsole = redirectConsole;
|
||||
|
||||
stdout = Console.Out;
|
||||
traceListener = new LoggerTraceListener(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the log writer.
|
||||
/// </summary>
|
||||
public void Enable()
|
||||
{
|
||||
if (_enabled)
|
||||
return;
|
||||
|
||||
if (IsRedirectingConsole)
|
||||
Console.SetOut(this);
|
||||
else
|
||||
Console.SetOut(Null);
|
||||
|
||||
Trace.Listeners.Add(traceListener);
|
||||
|
||||
_enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the log writer.
|
||||
/// </summary>
|
||||
public void Disable()
|
||||
{
|
||||
if (!_enabled)
|
||||
return;
|
||||
|
||||
Console.SetOut(stdout);
|
||||
|
||||
Trace.Listeners.Remove(traceListener);
|
||||
|
||||
_enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an entry to the Logger instance.
|
||||
/// </summary>
|
||||
/// <param name="level">The level of the entry.</param>
|
||||
/// <param name="entry">The textual value of the entry.</param>
|
||||
public override void Log(LogLevel level, object entry)
|
||||
{
|
||||
Kon.ForegroundColor = level.GetConsoleColor();
|
||||
base.Log(level, entry);
|
||||
Kon.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
|
||||
public override void Write(char value)
|
||||
{
|
||||
StringBuilder.Append(value);
|
||||
|
||||
stdout.Write(value);
|
||||
}
|
||||
|
||||
public override void Write(string value)
|
||||
{
|
||||
StringBuilder.Append(value);
|
||||
|
||||
stdout.Write(value);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
Disable();
|
||||
StringBuilder.Length = 0;
|
||||
|
||||
traceListener?.Dispose();
|
||||
traceListener = null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return StringBuilder.ToString().Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Harmony;
|
||||
using UnityEngineInternal;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
@@ -10,14 +11,9 @@ namespace BepInEx.Logging
|
||||
/// A trace listener that writes to an underlying <see cref="BaseLogger"/> instance.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="TraceListener"/>
|
||||
public class LoggerTraceListener : TraceListener
|
||||
public class TraceLogSource : TraceListener
|
||||
{
|
||||
/// <summary>
|
||||
/// The logger instance that is being written to.
|
||||
/// </summary>
|
||||
public BaseLogger Logger { get; }
|
||||
|
||||
static LoggerTraceListener()
|
||||
static TraceLogSource()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -26,10 +22,28 @@ namespace BepInEx.Logging
|
||||
catch { } //ignore everything, if it's thrown an exception, we're using an assembly that has already fixed this
|
||||
}
|
||||
|
||||
/// <param name="logger">The logger instance to write to.</param>
|
||||
public LoggerTraceListener(BaseLogger logger)
|
||||
public static bool IsListening { get; protected set; } = false;
|
||||
|
||||
private static TraceLogSource traceListener;
|
||||
|
||||
public static ILogSource CreateSource()
|
||||
{
|
||||
Logger = logger;
|
||||
if (traceListener == null)
|
||||
{
|
||||
traceListener = new TraceLogSource();
|
||||
Trace.Listeners.Add(traceListener);
|
||||
IsListening = true;
|
||||
}
|
||||
|
||||
return traceListener.LogSource;
|
||||
}
|
||||
|
||||
protected ManualLogSource LogSource { get; }
|
||||
|
||||
/// <param name="logger">The logger instance to write to.</param>
|
||||
protected TraceLogSource()
|
||||
{
|
||||
LogSource = new ManualLogSource("Trace");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -38,7 +52,7 @@ namespace BepInEx.Logging
|
||||
/// <param name="message">The message to write.</param>
|
||||
public override void Write(string message)
|
||||
{
|
||||
Logger.Write(message);
|
||||
LogSource.Log(LogLevel.None, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -47,7 +61,7 @@ namespace BepInEx.Logging
|
||||
/// <param name="message">The message to write.</param>
|
||||
public override void WriteLine(string message)
|
||||
{
|
||||
Logger.WriteLine(message);
|
||||
LogSource.Log(LogLevel.None, $"{message}\r\n");
|
||||
}
|
||||
|
||||
public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
|
||||
@@ -77,7 +91,7 @@ namespace BepInEx.Logging
|
||||
break;
|
||||
}
|
||||
|
||||
Logger.Log(level, $"{source} : {message}");
|
||||
LogSource.Log(level, $"{source} : {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,12 +127,11 @@ namespace BepInEx.Logging
|
||||
|
||||
instance.Patch(
|
||||
typeof(Trace).GetMethod("DoTrace", BindingFlags.Static | BindingFlags.NonPublic),
|
||||
new HarmonyMethod(typeof(TraceFixer).GetMethod(nameof(DoTraceReplacement), BindingFlags.Static | BindingFlags.Public)),
|
||||
null);
|
||||
prefix: new HarmonyMethod(typeof(TraceFixer).GetMethod(nameof(DoTraceReplacement), BindingFlags.Static | BindingFlags.NonPublic)));
|
||||
}
|
||||
|
||||
|
||||
public static bool DoTraceReplacement(string kind, Assembly report, string message)
|
||||
private static bool DoTraceReplacement(string kind, Assembly report, string message)
|
||||
{
|
||||
string arg = string.Empty;
|
||||
try
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Logs entries using Unity specific outputs.
|
||||
/// </summary>
|
||||
public class UnityLogListener : ILogListener
|
||||
{
|
||||
internal static readonly Action<string> WriteStringToUnityLog;
|
||||
|
||||
[DllImport("mono.dll", EntryPoint = "mono_lookup_internal_call")]
|
||||
private static extern IntPtr MonoLookupInternalCall(IntPtr gconstpointer);
|
||||
|
||||
static UnityLogListener()
|
||||
{
|
||||
foreach (MethodInfo methodInfo in typeof(UnityEngine.UnityLogWriter).GetMethods(BindingFlags.Static | BindingFlags.Public))
|
||||
{
|
||||
if (MonoLookupInternalCall(methodInfo.MethodHandle.Value) == IntPtr.Zero)
|
||||
continue;
|
||||
|
||||
WriteStringToUnityLog = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), methodInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void LogEvent(object sender, LogEventArgs eventArgs)
|
||||
{
|
||||
if (sender is UnityLogSource)
|
||||
return;
|
||||
|
||||
string log = $"[{eventArgs.Level}:{((ILogSource)sender).SourceName}] {eventArgs.Data}\r\n";
|
||||
|
||||
WriteStringToUnityLog?.Invoke(log);
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace UnityEngine
|
||||
{
|
||||
internal sealed class UnityLogWriter
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
public static extern void WriteStringToUnityLogImpl(string s);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
public static extern void WriteStringToUnityLog(string s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Logs entries using Unity specific outputs.
|
||||
/// </summary>
|
||||
public class UnityLogSource : ILogSource
|
||||
{
|
||||
public string SourceName { get; } = "Unity Log";
|
||||
public event EventHandler<LogEventArgs> LogEvent;
|
||||
|
||||
public UnityLogSource()
|
||||
{
|
||||
InternalUnityLogMessage += unityLogMessageHandler;
|
||||
}
|
||||
|
||||
private void unityLogMessageHandler(object sender, LogEventArgs eventArgs)
|
||||
{
|
||||
var newEventArgs = new LogEventArgs(eventArgs.Data, eventArgs.Level, this);
|
||||
LogEvent?.Invoke(this, newEventArgs);
|
||||
}
|
||||
|
||||
private bool disposed = false;
|
||||
public void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
InternalUnityLogMessage -= unityLogMessageHandler;
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#region Static Unity handler
|
||||
|
||||
private static event EventHandler<LogEventArgs> InternalUnityLogMessage;
|
||||
|
||||
static UnityLogSource()
|
||||
{
|
||||
var callback = new Application.LogCallback(OnUnityLogMessageReceived);
|
||||
|
||||
EventInfo logEvent = typeof(Application).GetEvent("logMessageReceived", BindingFlags.Public | BindingFlags.Static);
|
||||
if (logEvent != null)
|
||||
{
|
||||
logEvent.AddEventHandler(null, callback);
|
||||
//UnsubscribeAction = () => logEvent.RemoveEventHandler(null, callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo registerLogCallback = typeof(Application).GetMethod("RegisterLogCallback", BindingFlags.Public | BindingFlags.Static);
|
||||
registerLogCallback.Invoke(null, new object[] { callback });
|
||||
//UnsubscribeAction = () => registerLogCallback.Invoke(null, new object[] { null });
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnUnityLogMessageReceived(string message, string stackTrace, LogType type)
|
||||
{
|
||||
LogLevel logLevel;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case LogType.Error:
|
||||
case LogType.Assert:
|
||||
case LogType.Exception:
|
||||
logLevel = LogLevel.Error;
|
||||
break;
|
||||
case LogType.Warning:
|
||||
logLevel = LogLevel.Warning;
|
||||
break;
|
||||
case LogType.Log:
|
||||
default:
|
||||
logLevel = LogLevel.Info;
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == LogType.Exception)
|
||||
message += $"\nStack trace:\n{stackTrace}";
|
||||
|
||||
InternalUnityLogMessage?.Invoke(null, new LogEventArgs(message, logLevel, null));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using BepInEx.ConsoleUtil;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BepInEx.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Logs entries using Unity specific outputs.
|
||||
/// </summary>
|
||||
public class UnityLogWriter : BaseLogger
|
||||
{
|
||||
private static readonly Action<string> WriteStringToUnityLog;
|
||||
|
||||
[DllImport("mono.dll", EntryPoint = "mono_lookup_internal_call")]
|
||||
private static extern IntPtr MonoLookupInternalCall(IntPtr gconstpointer);
|
||||
|
||||
static UnityLogWriter()
|
||||
{
|
||||
foreach (MethodInfo methodInfo in typeof(UnityEngine.UnityLogWriter).GetMethods(BindingFlags.Static | BindingFlags.Public))
|
||||
{
|
||||
if (MonoLookupInternalCall(methodInfo.MethodHandle.Value) == IntPtr.Zero)
|
||||
continue;
|
||||
|
||||
WriteStringToUnityLog = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), methodInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a string specifically to the game output log.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to write.</param>
|
||||
public void WriteToLog(string value)
|
||||
{
|
||||
WriteStringToUnityLog?.Invoke(value);
|
||||
}
|
||||
|
||||
protected void InternalWrite(string value)
|
||||
{
|
||||
Console.Write(value);
|
||||
WriteToLog(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an entry to the Logger instance.
|
||||
/// </summary>
|
||||
/// <param name="level">The level of the entry.</param>
|
||||
/// <param name="entry">The textual value of the entry.</param>
|
||||
public override void Log(LogLevel level, object entry)
|
||||
{
|
||||
Kon.ForegroundColor = level.GetConsoleColor();
|
||||
base.Log(level, entry);
|
||||
Kon.ForegroundColor = ConsoleColor.Gray;
|
||||
|
||||
// If the display level got ignored, still write it to the log
|
||||
if ((DisplayedLevels & level) == LogLevel.None)
|
||||
WriteToLog($"[{level.GetHighestLevel()}] {entry}\r\n");
|
||||
}
|
||||
|
||||
public override void WriteLine(string value) => InternalWrite($"{value}\r\n");
|
||||
public override void Write(char value) => InternalWrite(value.ToString());
|
||||
public override void Write(string value) => InternalWrite(value);
|
||||
|
||||
/// <summary>
|
||||
/// Start listening to Unity's log message events and sending the messages to BepInEx logger.
|
||||
/// </summary>
|
||||
public static void ListenUnityLogs()
|
||||
{
|
||||
Type application = typeof(Application);
|
||||
|
||||
EventInfo logEvent = application.GetEvent("logMessageReceived", BindingFlags.Public | BindingFlags.Static);
|
||||
if (logEvent != null)
|
||||
{
|
||||
logEvent.AddEventHandler(null, new Application.LogCallback(OnUnityLogMessageReceived));
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo registerLogCallback = application.GetMethod("RegisterLogCallback", BindingFlags.Public | BindingFlags.Static);
|
||||
registerLogCallback.Invoke(null, new object[] { new Application.LogCallback(OnUnityLogMessageReceived) });
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnUnityLogMessageReceived(string message, string stackTrace, LogType type)
|
||||
{
|
||||
LogLevel logLevel = LogLevel.Message;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case LogType.Error:
|
||||
case LogType.Assert:
|
||||
case LogType.Exception:
|
||||
logLevel = LogLevel.Error;
|
||||
break;
|
||||
case LogType.Warning:
|
||||
logLevel = LogLevel.Warning;
|
||||
break;
|
||||
case LogType.Log:
|
||||
default:
|
||||
logLevel = LogLevel.Info;
|
||||
break;
|
||||
}
|
||||
|
||||
Logger.Log(logLevel, message);
|
||||
if (type == LogType.Exception)
|
||||
{
|
||||
Logger.Log(logLevel, $"Stack trace:\n{stackTrace}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace UnityEngine
|
||||
{
|
||||
internal sealed class UnityLogWriter
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
public static extern void WriteStringToUnityLogImpl(string s);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
public static extern void WriteStringToUnityLog(string s);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user