summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/Logging
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-01-16 18:01:19 -0500
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-01-16 18:01:19 -0500
commit1b9ff9cd101621cacc246fc2b7d936f964ae4b3f (patch)
treeff742b2a12270b57c28e8ddecdc5322c64f6c2ce /src/SMAPI/Framework/Logging
parent8a2618d812e4e2f36161907af5af5c484eb37abf (diff)
parentad0e6b315dc7b4e616dcdf6c090cd54a2f3b7499 (diff)
downloadSMAPI-1b9ff9cd101621cacc246fc2b7d936f964ae4b3f.tar.gz
SMAPI-1b9ff9cd101621cacc246fc2b7d936f964ae4b3f.tar.bz2
SMAPI-1b9ff9cd101621cacc246fc2b7d936f964ae4b3f.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI/Framework/Logging')
-rw-r--r--src/SMAPI/Framework/Logging/InterceptingTextWriter.cs40
-rw-r--r--src/SMAPI/Framework/Logging/LogManager.cs37
2 files changed, 49 insertions, 28 deletions
diff --git a/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs b/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs
index d99f1dd2..bad69a2a 100644
--- a/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs
+++ b/src/SMAPI/Framework/Logging/InterceptingTextWriter.cs
@@ -8,15 +8,11 @@ namespace StardewModdingAPI.Framework.Logging
internal class InterceptingTextWriter : TextWriter
{
/*********
- ** Fields
+ ** Accessors
*********/
/// <summary>Prefixing a message with this character indicates that the console interceptor should write the string without intercepting it. (The character itself is not written.)</summary>
- private readonly char IgnoreChar;
-
+ public const char IgnoreChar = '\u200B';
- /*********
- ** Accessors
- *********/
/// <summary>The underlying console output.</summary>
public TextWriter Out { get; }
@@ -26,30 +22,48 @@ namespace StardewModdingAPI.Framework.Logging
/// <summary>The event raised when a message is written to the console directly.</summary>
public event Action<string> OnMessageIntercepted;
+ /// <summary>Whether the text writer should ignore the next input if it's a newline.</summary>
+ /// <remarks>This is used when log output is suppressed from the console, since <c>Console.WriteLine</c> writes the trailing newline as a separate call.</remarks>
+ public bool IgnoreNextIfNewline { get; set; }
+
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name="output">The underlying output writer.</param>
- /// <param name="ignoreChar">Prefixing a message with this character indicates that the console interceptor should write the string without intercepting it. (The character itself is not written.)</param>
- public InterceptingTextWriter(TextWriter output, char ignoreChar)
+ public InterceptingTextWriter(TextWriter output)
{
this.Out = output;
- this.IgnoreChar = ignoreChar;
}
/// <inheritdoc />
public override void Write(char[] buffer, int index, int count)
{
- if (buffer.Length == 0)
+ // track newline skip
+ bool ignoreIfNewline = this.IgnoreNextIfNewline;
+ this.IgnoreNextIfNewline = false;
+
+ // get first character if valid
+ if (count == 0 || index < 0 || index >= buffer.Length)
+ {
this.Out.Write(buffer, index, count);
- else if (buffer[0] == this.IgnoreChar)
+ return;
+ }
+ char firstChar = buffer[index];
+
+ // handle output
+ if (firstChar == InterceptingTextWriter.IgnoreChar)
this.Out.Write(buffer, index + 1, count - 1);
- else if (this.IsEmptyOrNewline(buffer))
+ else if (char.IsControl(firstChar) && firstChar is not ('\r' or '\n'))
this.Out.Write(buffer, index, count);
+ else if (this.IsEmptyOrNewline(buffer))
+ {
+ if (!ignoreIfNewline)
+ this.Out.Write(buffer, index, count);
+ }
else
- this.OnMessageIntercepted?.Invoke(new string(buffer, index, count).TrimEnd('\r', '\n'));
+ this.OnMessageIntercepted?.Invoke(new string(buffer, index, count));
}
/// <inheritdoc />
diff --git a/src/SMAPI/Framework/Logging/LogManager.cs b/src/SMAPI/Framework/Logging/LogManager.cs
index 5a291d0a..a8a8b6ee 100644
--- a/src/SMAPI/Framework/Logging/LogManager.cs
+++ b/src/SMAPI/Framework/Logging/LogManager.cs
@@ -25,8 +25,11 @@ namespace StardewModdingAPI.Framework.Logging
/// <summary>The log file to which to write messages.</summary>
private readonly LogFileManager LogFile;
+ /// <summary>The text writer which intercepts console output.</summary>
+ private readonly InterceptingTextWriter ConsoleInterceptor;
+
/// <summary>Prefixing a low-level message with this character indicates that the console interceptor should write the string without intercepting it. (The character itself is not written.)</summary>
- private readonly char IgnoreChar = '\u200B';
+ private const char IgnoreChar = InterceptingTextWriter.IgnoreChar;
/// <summary>Get a named monitor instance.</summary>
private readonly Func<string, Monitor> GetMonitorImpl;
@@ -34,22 +37,22 @@ namespace StardewModdingAPI.Framework.Logging
/// <summary>Regex patterns which match console non-error messages to suppress from the console and log.</summary>
private readonly Regex[] SuppressConsolePatterns =
{
- new Regex(@"^TextBox\.Selected is now '(?:True|False)'\.$", RegexOptions.Compiled | RegexOptions.CultureInvariant),
- new Regex(@"^loadPreferences\(\); begin", RegexOptions.Compiled | RegexOptions.CultureInvariant),
- new Regex(@"^savePreferences\(\); async=", RegexOptions.Compiled | RegexOptions.CultureInvariant),
- new Regex(@"^DebugOutput:\s+(?:added cricket|dismount tile|Ping|playerPos)", RegexOptions.Compiled | RegexOptions.CultureInvariant),
- new Regex(@"^Ignoring keys: ", RegexOptions.Compiled | RegexOptions.CultureInvariant)
+ new(@"^TextBox\.Selected is now '(?:True|False)'\.$", RegexOptions.Compiled | RegexOptions.CultureInvariant),
+ new(@"^loadPreferences\(\); begin", RegexOptions.Compiled | RegexOptions.CultureInvariant),
+ new(@"^savePreferences\(\); async=", RegexOptions.Compiled | RegexOptions.CultureInvariant),
+ new(@"^DebugOutput:\s+(?:added cricket|dismount tile|Ping|playerPos)", RegexOptions.Compiled | RegexOptions.CultureInvariant),
+ new(@"^Ignoring keys: ", RegexOptions.Compiled | RegexOptions.CultureInvariant)
};
/// <summary>Regex patterns which match console messages to show a more friendly error for.</summary>
private readonly ReplaceLogPattern[] ReplaceConsolePatterns =
{
// Steam not loaded
- new ReplaceLogPattern(
+ new(
search: new Regex(@"^System\.InvalidOperationException: Steamworks is not initialized\.[\s\S]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant),
replacement:
#if SMAPI_FOR_WINDOWS
- "Oops! Steam achievements won't work because Steam isn't loaded. See 'Launch SMAPI through Steam or GOG Galaxy' in the install guide for more info: https://smapi.io/install.",
+ "Oops! Steam achievements won't work because Steam isn't loaded. See 'Configure your game client' in the install guide for more info: https://smapi.io/install.",
#else
"Oops! Steam achievements won't work because Steam isn't loaded. You can launch the game through Steam to fix that.",
#endif
@@ -57,7 +60,7 @@ namespace StardewModdingAPI.Framework.Logging
),
// save file not found error
- new ReplaceLogPattern(
+ new(
search: new Regex(@"^System\.IO\.FileNotFoundException: [^\n]+\n[^:]+: '[^\n]+[/\\]Saves[/\\]([^'\r\n]+)[/\\]([^'\r\n]+)'[\s\S]+LoadGameMenu\.FindSaveGames[\s\S]+$", RegexOptions.Compiled | RegexOptions.CultureInvariant),
replacement: "The game can't find the '$2' file for your '$1' save. See https://stardewvalleywiki.com/Saves#Troubleshooting for help.",
logLevel: LogLevel.Error
@@ -91,7 +94,7 @@ namespace StardewModdingAPI.Framework.Logging
public LogManager(string logPath, ColorSchemeConfig colorConfig, bool writeToConsole, bool isVerbose, bool isDeveloperMode, Func<int?> getScreenIdForLog)
{
// init construction logic
- this.GetMonitorImpl = name => new Monitor(name, this.IgnoreChar, this.LogFile, colorConfig, isVerbose, getScreenIdForLog)
+ this.GetMonitorImpl = name => new Monitor(name, LogManager.IgnoreChar, this.LogFile, colorConfig, isVerbose, getScreenIdForLog)
{
WriteToConsole = writeToConsole,
ShowTraceInConsole = isDeveloperMode,
@@ -104,10 +107,10 @@ namespace StardewModdingAPI.Framework.Logging
this.MonitorForGame = this.GetMonitor("game");
// redirect direct console output
- var output = new InterceptingTextWriter(Console.Out, this.IgnoreChar);
+ this.ConsoleInterceptor = new InterceptingTextWriter(Console.Out);
if (writeToConsole)
- output.OnMessageIntercepted += message => this.HandleConsoleMessage(this.MonitorForGame, message);
- Console.SetOut(output);
+ this.ConsoleInterceptor.OnMessageIntercepted += message => this.HandleConsoleMessage(this.MonitorForGame, message);
+ Console.SetOut(this.ConsoleInterceptor);
// enable Unicode handling on Windows
// (the terminal defaults to UTF-8 on Linux/macOS)
@@ -146,7 +149,7 @@ namespace StardewModdingAPI.Framework.Logging
.Add(new ReloadI18nCommand(reloadTranslations), this.Monitor);
// start handling command line input
- Thread inputThread = new Thread(() =>
+ Thread inputThread = new(() =>
{
while (true)
{
@@ -262,7 +265,7 @@ namespace StardewModdingAPI.Framework.Logging
public void LogIntro(string modsPath, IDictionary<string, object> customSettings)
{
// log platform
- this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GameVersion} on {EnvironmentUtility.GetFriendlyPlatformName(Constants.Platform)}", LogLevel.Info);
+ this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GameVersion} (build {Constants.GetBuildVersionLabel()}) on {EnvironmentUtility.GetFriendlyPlatformName(Constants.Platform)}", LogLevel.Info);
// log basic info
this.Monitor.Log($"Mods go here: {modsPath}", LogLevel.Info);
@@ -363,7 +366,10 @@ namespace StardewModdingAPI.Framework.Logging
// ignore suppressed message
if (level != LogLevel.Error && this.SuppressConsolePatterns.Any(p => p.IsMatch(message)))
+ {
+ this.ConsoleInterceptor.IgnoreNextIfNewline = true;
return;
+ }
// show friendly error if applicable
foreach (ReplaceLogPattern entry in this.ReplaceConsolePatterns)
@@ -383,6 +389,7 @@ namespace StardewModdingAPI.Framework.Logging
// forward to monitor
gameMonitor.Log(message, level);
+ this.ConsoleInterceptor.IgnoreNextIfNewline = true;
}
/// <summary>Write a summary of mod warnings to the console and log.</summary>