Browse Source

Simplify ConsoleSetOut fix; redirect console messages to logging system

ghorsington 4 years ago
parent
commit
f2b7b9993e

+ 16 - 35
BepInEx.Preloader/RuntimeFixes/ConsoleSetOutFix.cs

@@ -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
BepInEx/Bootstrap/Chainloader.cs

@@ -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,

+ 0 - 17
BepInEx/Console/ConsoleManager.cs

@@ -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
BepInEx/Logging/ConsoleLogListener.cs

@@ -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
BepInEx/Logging/UnityLogListener.cs

@@ -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());
 	}
 }