Completely change logger workflow

This commit is contained in:
Bepis committed 2019-02-09 00:12:54 +11:00
1 parent 9715f8ade1
commit 69efae63ed
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>
+28 -17
View File
@@ -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");
+91
View File
@@ -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);
}
}
+8 -4
View File
@@ -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" />
+16 -10
View File
@@ -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;
+7 -4
View File
@@ -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)]
+1 -1
View File
@@ -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;
-99
View File
@@ -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);
}
}
}
+22
View File
@@ -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() { }
}
}
+9
View File
@@ -0,0 +1,9 @@
using System;
namespace BepInEx.Logging
{
public interface ILogListener : IDisposable
{
void LogEvent(object sender, LogEventArgs eventArgs);
}
}
+11
View File
@@ -0,0 +1,11 @@
using System;
namespace BepInEx.Logging
{
public interface ILogSource : IDisposable
{
string SourceName { get; }
event EventHandler<LogEventArgs> LogEvent;
}
}
+20
View File
@@ -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
View File
@@ -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;
}
}
}
}
+29
View File
@@ -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() { }
}
}
-128
View File
@@ -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
+54
View File
@@ -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);
}
}
+86
View File
@@ -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
}
}
-125
View File
@@ -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);
}
}