LoggerTraceListener.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. using System;
  2. using System.Diagnostics;
  3. using System.Linq;
  4. using System.Reflection;
  5. using Harmony;
  6. namespace BepInEx.Logging
  7. {
  8. public class LoggerTraceListener : TraceListener
  9. {
  10. public BaseLogger Logger;
  11. static LoggerTraceListener()
  12. {
  13. try
  14. {
  15. TraceFixer.ApplyFix();
  16. }
  17. catch { } //ignore everything, if it's thrown an exception, we're using an assembly that has already fixed this
  18. }
  19. public LoggerTraceListener(BaseLogger logger)
  20. {
  21. Logger = logger;
  22. }
  23. public override void Write(string message)
  24. {
  25. Logger.Write(message);
  26. }
  27. public override void WriteLine(string message)
  28. {
  29. Logger.WriteLine(message);
  30. }
  31. public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
  32. => TraceEvent(eventCache, source, eventType, id, string.Format(format, args));
  33. public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
  34. {
  35. LogLevel level;
  36. switch (eventType)
  37. {
  38. case TraceEventType.Critical:
  39. level = LogLevel.Fatal;
  40. break;
  41. case TraceEventType.Error:
  42. level = LogLevel.Error;
  43. break;
  44. case TraceEventType.Warning:
  45. level = LogLevel.Warning;
  46. break;
  47. case TraceEventType.Information:
  48. level = LogLevel.Info;
  49. break;
  50. case TraceEventType.Verbose:
  51. default:
  52. level = LogLevel.Debug;
  53. break;
  54. }
  55. Logger.Log(level, $"{source} : {message}");
  56. }
  57. private static class TraceFixer
  58. {
  59. private static Type TraceImplType;
  60. private static object ListenersSyncRoot;
  61. private static TraceListenerCollection Listeners;
  62. private static PropertyInfo prop_AutoFlush;
  63. private static bool AutoFlush => (bool)prop_AutoFlush.GetValue(null, null);
  64. public static void ApplyFix()
  65. {
  66. TraceImplType = AppDomain.CurrentDomain.GetAssemblies()
  67. .First(x => x.GetName().Name == "System")
  68. .GetTypes()
  69. .First(x => x.Name == "TraceImpl");
  70. ListenersSyncRoot = AccessTools.Property(TraceImplType, "ListenersSyncRoot").GetValue(null, null);
  71. Listeners = (TraceListenerCollection)AccessTools.Property(TraceImplType, "Listeners").GetValue(null, null);
  72. prop_AutoFlush = AccessTools.Property(TraceImplType, "AutoFlush");
  73. HarmonyInstance instance = HarmonyInstance.Create("com.bepis.bepinex.tracefix");
  74. instance.Patch(
  75. typeof(Trace).GetMethod("DoTrace", BindingFlags.Static | BindingFlags.NonPublic),
  76. new HarmonyMethod(typeof(TraceFixer).GetMethod(nameof(DoTraceReplacement), BindingFlags.Static | BindingFlags.Public)),
  77. null);
  78. }
  79. public static bool DoTraceReplacement(string kind, Assembly report, string message)
  80. {
  81. string arg = string.Empty;
  82. try
  83. {
  84. arg = report.GetName().Name;
  85. }
  86. catch (MethodAccessException) { }
  87. TraceEventType type = (TraceEventType)Enum.Parse(typeof(TraceEventType), kind);
  88. lock (ListenersSyncRoot)
  89. {
  90. foreach (object obj in Listeners)
  91. {
  92. TraceListener traceListener = (TraceListener)obj;
  93. traceListener.TraceEvent(new TraceEventCache(), arg, type, 0, message);
  94. if (AutoFlush)
  95. {
  96. traceListener.Flush();
  97. }
  98. }
  99. }
  100. return false;
  101. }
  102. }
  103. }
  104. }