Simplify ConsoleSetOut fix; redirect console messages to logging system

This commit is contained in:
ghorsington committed 2020-07-05 12:02:23 +03:00
1 parent dc6c76b59f
commit f2b7b9993e
5 files changed
+30 -76

No files matched your search

@@ -1,29 +1,20 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using BepInEx.Logging;
using HarmonyLib;
namespace BepInEx.Preloader.RuntimeFixes
{
/*
* By default, setting Console.Out removes the previous listener
* This can be a problem in Unity because it installs its own TextWriter while BepInEx needs to install it
* one too for the console. This runtime fix collects all Console.Out setters and aggregates them into a single
* text writer.
*
* This allows to both fix the old problem with log overwriting and problem with writing stdout when console is
* enabled.
*/
internal static class ConsoleSetOutFix
{
private static AggregatedTextWriter aggregatedTextWriter;
private static LoggedTextWriter loggedTextWriter;
internal static ManualLogSource ConsoleLogSource = Logger.CreateLogSource("Console");
public static void Apply()
{
aggregatedTextWriter = new AggregatedTextWriter(Console.Out);
Console.SetOut(aggregatedTextWriter);
loggedTextWriter = new LoggedTextWriter { Parent = Console.Out };
Console.SetOut(loggedTextWriter);
HarmonyLib.Harmony.CreateAndPatchAll(typeof(ConsoleSetOutFix));
}
@@ -31,39 +22,29 @@ namespace BepInEx.Preloader.RuntimeFixes
[HarmonyPrefix]
private static bool OnSetOut(TextWriter newOut)
{
if (newOut == TextWriter.Null)
return false;
aggregatedTextWriter.Add(newOut);
loggedTextWriter.Parent = newOut;
return false;
}
}
internal class AggregatedTextWriter : TextWriter
internal class LoggedTextWriter : TextWriter
{
public override Encoding Encoding { get; } = Encoding.UTF8;
private List<TextWriter> writers = new List<TextWriter>();
public TextWriter Parent { get; set; }
public AggregatedTextWriter(params TextWriter[] initialWriters)
public override void Flush() => Parent.Flush();
public override void Write(string value)
{
writers.AddRange(initialWriters.Where(w => w != null));
ConsoleSetOutFix.ConsoleLogSource.LogInfo(value);
Parent.Write(value);
}
public void Add(TextWriter tw)
public override void WriteLine(string value)
{
if (writers.Any(t => t == tw))
return;
writers.Add(tw);
ConsoleSetOutFix.ConsoleLogSource.LogInfo(value);
Parent.WriteLine(value);
}
public override void Flush() => writers.ForEach(w => w.Flush());
public override void Write(object value) => writers.ForEach(w => w.Write(value));
public override void Write(string value) => writers.ForEach(w => w.Write(value));
public override void Write(char value) => writers.ForEach(w => w.Write(value));
public override void WriteLine(object value) => writers.ForEach(w => w.WriteLine(value));
public override void WriteLine(string value) => writers.ForEach(w => w.WriteLine(value));
public override void WriteLine(char value) => writers.ForEach(w => w.WriteLine(value));
}
}
+1 -11
View File
@@ -86,9 +86,7 @@ namespace BepInEx.Bootstrap
// Add Unity log source only after replaying to prevent duplication in console
if (ConfigUnityLogging.Value)
Logger.Sources.Add(new UnityLogSource());
// Write to Unity logs directly only if console is not present (or user explicitly wants to)
if (ConfigWriteToUnityLog.Value || !ConsoleManager.ConsoleActive)
Logger.Listeners.Add(new UnityLogListener());
Logger.Listeners.Add(new UnityLogListener());
if (Utility.CurrentOs == Platform.Linux)
{
@@ -415,14 +413,6 @@ namespace BepInEx.Bootstrap
true,
"Enables showing unity log messages in the BepInEx logging system.");
private static readonly ConfigEntry<bool> ConfigWriteToUnityLog = ConfigFile.CoreConfig.Bind(
"Logging", "WriteToUnityLog",
false,
new StringBuilder()
.AppendLine("Enables writing log messages to Unity's log system")
.AppendLine("NOTE: By default, Unity already writes BepInEx console output to Unity logs")
.Append("Use this setting only if no BepInEx log messages are present in Unity's output_log").ToString());
private static readonly ConfigEntry<bool> ConfigDiskWriteUnityLog = ConfigFile.CoreConfig.Bind(
"Logging.Disk", "WriteUnityLog",
false,
-17
View File
@@ -56,7 +56,6 @@ namespace BepInEx
throw new InvalidOperationException("Driver has not been initialized");
}
public static void CreateConsole()
{
if (ConsoleActive)
@@ -70,7 +69,6 @@ namespace BepInEx
var codepage = ConfigConsoleShiftJis.Value ? SHIFT_JIS_CP: (uint)Encoding.UTF8.CodePage;
Driver.CreateConsole(codepage);
SetConsoleStreams();
}
public static void DetachConsole()
@@ -81,7 +79,6 @@ namespace BepInEx
DriverCheck();
Driver.DetachConsole();
SetConsoleStreams();
}
public static void SetConsoleTitle(string title)
@@ -98,20 +95,6 @@ namespace BepInEx
Driver.SetConsoleColor(color);
}
internal static void SetConsoleStreams()
{
if (ConsoleActive)
{
Console.SetOut(ConsoleStream);
Console.SetError(ConsoleStream);
}
else
{
Console.SetOut(TextWriter.Null);
Console.SetError(TextWriter.Null);
}
}
public static readonly ConfigEntry<bool> ConfigConsoleEnabled = ConfigFile.CoreConfig.Bind(
"Logging.Console", "Enabled",
false,
+1 -11
View File
@@ -14,20 +14,10 @@ namespace BepInEx.Logging
return;
ConsoleManager.SetConsoleColor(eventArgs.Level.GetConsoleColor());
Log(eventArgs);
ConsoleManager.ConsoleStream?.Write(eventArgs.ToStringLine());
ConsoleManager.SetConsoleColor(ConsoleColor.Gray);
}
private void Log(LogEventArgs eventArgs)
{
// Special case: if message comes from Unity log, it's already logged by Unity, in which case
// we replay it only in the visible console
if (eventArgs.Source is UnityLogSource)
ConsoleManager.ConsoleStream?.Write(eventArgs.ToStringLine());
else
Console.Write(eventArgs.ToStringLine());
}
public void Dispose() { }
private static readonly ConfigEntry<LogLevel> ConfigConsoleDisplayedLevel = ConfigFile.CoreConfig.Bind(
+12 -2
View File
@@ -1,6 +1,8 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx.Configuration;
namespace BepInEx.Logging
{
@@ -36,11 +38,19 @@ namespace BepInEx.Logging
{
if (eventArgs.Source is UnityLogSource)
return;
WriteStringToUnityLog?.Invoke(eventArgs.ToStringLine());
// Special case: don't write console twice since Unity can already do that
if (LogConsoleToUnity.Value || eventArgs.Source.SourceName != "Console")
WriteStringToUnityLog?.Invoke(eventArgs.ToStringLine());
}
public void Dispose() { }
private ConfigEntry<bool> LogConsoleToUnity = ConfigFile.CoreConfig.Bind("Logging",
"LogConsoleToUnityLog", false,
new StringBuilder()
.AppendLine("If enabled, writes Standard Output messages to Unity log")
.AppendLine("NOTE: By default, Unity does so automatically. Only use this option if no console messages are visible in Unity log").ToString());
}
}