summaryrefslogtreecommitdiff
path: root/src/StardewModdingAPI/Framework/Monitor.cs
blob: 39b567d88df01a9028fe7840cf28503d9938f77a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
using System;
using System.Collections.Generic;
using System.Linq;

namespace StardewModdingAPI.Framework
{
    /// <summary>Encapsulates monitoring and logic for a given module.</summary>
    internal class Monitor : IMonitor
    {
        /*********
        ** Properties
        *********/
        /// <summary>The name of the module which logs messages using this instance.</summary>
        private readonly string Source;

        /// <summary>The log file to which to write messages.</summary>
        private readonly LogFileManager LogFile;

        /// <summary>The maximum length of the <see cref="LogLevel"/> values.</summary>
        private static readonly int MaxLevelLength = (from level in Enumerable.Cast<LogLevel>(Enum.GetValues(typeof(LogLevel))) select level.ToString().Length).Max();

        /// <summary>The console text color for each log level.</summary>
        private static readonly Dictionary<LogLevel, ConsoleColor> Colors = new Dictionary<LogLevel, ConsoleColor>
        {
            [LogLevel.Trace] = ConsoleColor.DarkGray,
            [LogLevel.Debug] = ConsoleColor.DarkGray,
            [LogLevel.Info] = ConsoleColor.White,
            [LogLevel.Warn] = ConsoleColor.Yellow,
            [LogLevel.Error] = ConsoleColor.Red,
            [LogLevel.Alert] = ConsoleColor.Magenta
        };


        /*********
        ** Accessors
        *********/
        /// <summary>Whether the current console supports color codes.</summary>
        internal static readonly bool ConsoleSupportsColor = Monitor.GetConsoleSupportsColor();

        /// <summary>Whether to show trace messages in the console.</summary>
        internal bool ShowTraceInConsole { get; set; }

        /// <summary>Whether to write anything to the console. This should be disabled if no console is available.</summary>
        internal bool WriteToConsole { get; set; } = true;


        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance.</summary>
        /// <param name="source">The name of the module which logs messages using this instance.</param>
        /// <param name="logFile">The log file to which to write messages.</param>
        public Monitor(string source, LogFileManager logFile)
        {
            // validate
            if (string.IsNullOrWhiteSpace(source))
                throw new ArgumentException("The log source cannot be empty.");
            if (logFile == null)
                throw new ArgumentNullException(nameof(logFile), "The log file manager cannot be null.");

            // initialise
            this.Source = source;
            this.LogFile = logFile;
        }

        /// <summary>Log a message for the player or developer.</summary>
        /// <param name="message">The message to log.</param>
        /// <param name="level">The log severity level.</param>
        public void Log(string message, LogLevel level = LogLevel.Debug)
        {
            this.LogImpl(this.Source, message, Monitor.Colors[level], level);
        }

        /// <summary>Immediately exit the game without saving. This should only be invoked when an irrecoverable fatal error happens that risks save corruption or game-breaking bugs.</summary>
        /// <param name="reason">The reason for the shutdown.</param>
        public void ExitGameImmediately(string reason)
        {
            Program.ExitGameImmediately(this.Source, reason);
            Program.gamePtr.Exit();
        }

        /// <summary>Log a fatal error message.</summary>
        /// <param name="message">The message to log.</param>
        internal void LogFatal(string message)
        {
            Console.BackgroundColor = ConsoleColor.Red;
            this.LogImpl(this.Source, message, ConsoleColor.White, LogLevel.Error);
        }

        /// <summary>Log a message for the player or developer, using the specified console color.</summary>
        /// <param name="source">The name of the mod logging the message.</param>
        /// <param name="message">The message to log.</param>
        /// <param name="color">The console color.</param>
        /// <param name="level">The log level.</param>
        [Obsolete("This method is provided for backwards compatibility and otherwise should not be used. Use " + nameof(Monitor) + "." + nameof(Monitor.Log) + " instead.")]
        internal void LegacyLog(string source, string message, ConsoleColor color, LogLevel level = LogLevel.Debug)
        {
            this.LogImpl(source, message, color, level);
        }


        /*********
        ** Private methods
        *********/
        /// <summary>Write a message line to the log.</summary>
        /// <param name="source">The name of the mod logging the message.</param>
        /// <param name="message">The message to log.</param>
        /// <param name="color">The console color.</param>
        /// <param name="level">The log level.</param>
        private void LogImpl(string source, string message, ConsoleColor color, LogLevel level)
        {
            // generate message
            string levelStr = level.ToString().ToUpper().PadRight(Monitor.MaxLevelLength);
            message = $"[{DateTime.Now:HH:mm:ss} {levelStr} {source}] {message}";

            // log
            if (this.WriteToConsole && (this.ShowTraceInConsole || level != LogLevel.Trace))
            {
                if (Monitor.ConsoleSupportsColor)
                {
                    Console.ForegroundColor = color;
                    Console.WriteLine(message);
                    Console.ResetColor();
                }
                else
                    Console.WriteLine(message);
            }
            this.LogFile.WriteLine(message);
        }

        /// <summary>Test whether the current console supports color formatting.</summary>
        private static bool GetConsoleSupportsColor()
        {
            try
            {
                Console.ForegroundColor = Console.ForegroundColor;
                return true;
            }
            catch (Exception)
            {
                return false; // Mono bug
            }
        }
    }
}