using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx.ConsoleUtil;
using UnityEngine;
namespace BepInEx.Logging
{
///
/// Logs entries using Unity specific outputs.
///
public class UnityLogWriter : BaseLogger
{
private delegate void WriteStringToUnityLogDelegate(string s);
private static readonly WriteStringToUnityLogDelegate WriteStringToUnityLog;
static UnityLogWriter()
{
Type logWriter = typeof(UnityEngine.Logger).Assembly.GetType("UnityEngine.UnityLogWriter");
MethodInfo writeLog = logWriter.GetMethod("WriteStringToUnityLog",
BindingFlags.Static
| BindingFlags.Public
| BindingFlags.NonPublic);
if (writeLog == null)
{
writeLog = logWriter.GetMethod("WriteStringToUnityLogImpl",
BindingFlags.Static
| BindingFlags.Public
| BindingFlags.NonPublic);
if (writeLog == null)
return;
}
WriteStringToUnityLog = (WriteStringToUnityLogDelegate)Delegate.CreateDelegate(typeof(WriteStringToUnityLogDelegate), writeLog);
}
///
/// Writes a string specifically to the game output log.
///
/// The value to write.
public void WriteToLog(string value)
{
WriteStringToUnityLog?.Invoke(value);
}
protected void InternalWrite(string value)
{
Console.Write(value);
WriteToLog(value);
}
///
/// Logs an entry to the Logger instance.
///
/// The level of the entry.
/// The textual value of the entry.
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);
///
/// Start listening to Unity's log message events and sending the messages to BepInEx logger.
///
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}");
}
}
}
}