7using System.Collections.Generic;
8using System.Diagnostics;
11using System.Reflection;
12using System.Runtime.ExceptionServices;
14using System.Text.RegularExpressions;
15using System.Threading;
16using Terraria.Localization;
17using Terraria.ModLoader.Core;
18using Microsoft.Xna.Framework;
19using Terraria.ModLoader.UI;
25 public static readonly
string LogDir = Path.Combine(Program.SavePath,
"Logs");
27 public static string LogPath {
get;
private set; }
29 internal static ILog
Terraria {
get; } = LogManager.GetLogger(
"Terraria");
30 internal static ILog tML {
get; } = LogManager.GetLogger(
"tML");
33 internal const string side =
"client";
35 internal const string side =
"server";
39 internal static void Init() {
40 if (Program.LaunchParameters.ContainsKey(
"-build"))
44 Utils.TryCreatingDirectory(
LogDir);
48 tML.InfoFormat(
"Starting {0} {1} {2} ({3})",
ModLoader.
versionedName, ReLogic.OS.Platform.Current.Type, side, DateTime.Now.ToString(
"d"));
50 tML.InfoFormat(
"Executable: {0}", Assembly.GetEntryAssembly().Location);
51 tML.InfoFormat(
"Working Directory: {0}", Path.GetFullPath(Directory.GetCurrentDirectory()));
52 tML.InfoFormat(
"Launch Parameters: {0}",
string.Join(
" ", Program.LaunchParameters.Select(p => (p.Key +
" " + p.Value).Trim())));
54 if (ModCompile.DeveloperMode)
55 tML.Info(
"Developer mode enabled");
60 AppDomain.CurrentDomain.UnhandledException += (s, args) => tML.Error(
"Unhandled Exception", args.ExceptionObject as
Exception);
63 AssemblyResolving.Init();
65 LogArchiver.ArchiveLogs();
69 var layout =
new PatternLayout {
70 ConversionPattern =
"[%d{HH:mm:ss}] [%t/%level] [%logger]: %m%n"
72 layout.ActivateOptions();
74 var appenders =
new List<IAppender>();
76 appenders.Add(
new ConsoleAppender {
77 Name =
"ConsoleAppender",
81 appenders.Add(
new DebugAppender {
82 Name =
"DebugAppender",
86 var fileAppender =
new FileAppender {
87 Name =
"FileAppender",
90 Encoding = Encoding.UTF8,
93 fileAppender.ActivateOptions();
94 appenders.Add(fileAppender);
96 BasicConfigurator.Configure(appenders.ToArray());
100 var pattern =
new Regex($
"{baseName}(\\d*)\\.log$");
101 var existingLogs = Directory.GetFiles(
LogDir).Where(s => pattern.IsMatch(Path.GetFileName(s))).ToList();
103 if (!existingLogs.All(
CanOpen)) {
104 int n = existingLogs.Select(s => {
105 var tok = pattern.Match(Path.GetFileName(s)).Groups[1].Value;
106 return tok.Length == 0 ? 1 :
int.Parse(tok);
108 return $
"{baseName}{n + 1}.log";
111 foreach (var existingLog
in existingLogs.OrderBy(File.GetCreationTime)) {
114 while (File.Exists(existingLog + oldExt))
115 oldExt = $
".old{++n}";
118 File.Move(existingLog, existingLog + oldExt);
121 initWarnings.Add($
"Move failed during log initialization: {existingLog} -> {Path.GetFileName(existingLog)}{oldExt}\n{e}");
125 return $
"{baseName}.log";
128 private static bool CanOpen(
string fileName) {
130 using (
new FileStream(fileName, FileMode.Append)) ;
140 tML.Warn(
"First-chance exception reporting is not implemented on Mono");
146 internal static void ResetPastExceptions() =>
pastExceptions.Clear();
154 "System.Console.set_OutputEncoding",
155 "Terraria.ModLoader.Core.ModCompile",
156 "Delegate.CreateDelegateNoSecurityCheck",
157 "MethodBase.GetMethodBody",
158 "Terraria.Net.Sockets.TcpSocket.Terraria.Net.Sockets.ISocket.AsyncSend",
159 "System.Diagnostics.Process.Kill",
160 "Terraria.ModLoader.Core.AssemblyManager.CecilAssemblyResolver.Resolve",
161 "Terraria.ModLoader.Engine.TMLContentManager.OpenStream"
167 "A blocking operation was interrupted by a call to WSACancelBlockingCall",
168 "The request was aborted: The request was canceled.",
169 "Object name: 'System.Net.Sockets.Socket'.",
170 "Object name: 'System.Net.Sockets.NetworkStream'",
171 "This operation cannot be performed on a completed asynchronous result object.",
172 "Object name: 'SslStream'.",
173 "Unable to load DLL 'Microsoft.DiaSymReader.Native.x86.dll'"
177 "at Terraria.Lighting.doColors_Mode",
178 "System.Threading.CancellationToken.Throw",
186 private static ThreadLocal<bool>
handlerActive =
new ThreadLocal<bool>(() =>
false);
192 bool oom = args.Exception is OutOfMemoryException;
203 handlerActive.Value =
true;
207 args.Exception is ThreadAbortException ||
209 ignoreMessages.Any(str => args.Exception.Message?.Contains(str) ??
false) ||
214 var stackTrace =
new StackTrace(
true);
216 var traceString = stackTrace.ToString();
221 traceString = traceString.Substring(traceString.IndexOf(
'\n'));
222 var exString = args.Exception.GetType() +
": " + args.Exception.Message + traceString;
229 var msg = args.Exception.Message +
" " + Language.GetTextValue(
"tModLoader.RuntimeErrorSeeLogsForFullTrace", Path.GetFileName(
LogPath));
231 if (ModCompile.activelyModding)
234 Console.ForegroundColor = ConsoleColor.DarkMagenta;
238 tML.Warn(Language.GetTextValue(
"tModLoader.RuntimeErrorSilentlyCaughtException") +
'\n' + exString);
241 string error = Language.GetTextValue(
"tModLoader.OutOfMemory");
243 Interface.MessageBoxShow(error);
248 tML.Warn(
"FirstChanceExceptionHandler exception", e);
251 handlerActive.Value =
false;
260 float soundVolume = Main.soundVolume;
261 Main.soundVolume = 0f;
262 Main.NewText(msg, color);
263 Main.soundVolume = soundVolume;
267 internal static void LogStatusChange(
string oldStatusText,
string newStatusText) {
269 var oldBase =
statusRegex.Match(oldStatusText).Groups[1].Value;
270 var newBase =
statusRegex.Match(newStatusText).Groups[1].Value;
271 if (newBase != oldBase && newBase.Length > 0)
272 LogManager.GetLogger(
"StatusText").Info(newBase);
275 internal static void ServerConsoleLine(
string msg) => ServerConsoleLine(msg, Level.Info);
276 internal static void ServerConsoleLine(
string msg, Level level,
Exception ex =
null, ILog log =
null) {
277 if (level == Level.Warn)
278 Console.ForegroundColor = ConsoleColor.Yellow;
279 else if (level == Level.Error)
280 Console.ForegroundColor = ConsoleColor.Red;
285 (log ??
Terraria).Logger.Log(
null, level, msg, ex);
288 internal static readonly FieldInfo f_fileName =
289 typeof(StackFrame).GetField(
"strFileName", BindingFlags.Instance | BindingFlags.NonPublic) ??
290 typeof(StackFrame).GetField(
"fileName", BindingFlags.Instance | BindingFlags.NonPublic);
298 foreach (var frame
in frames) {
299 string filename = frame.GetFileName();
300 var assembly = frame.GetMethod()?.DeclaringType?.Assembly;
301 if (filename ==
null || assembly ==
null)
305 if (AssemblyManager.GetAssemblyOwner(assembly, out var modName))
312 int idx = filename.LastIndexOf(trim, StringComparison.InvariantCultureIgnoreCase);
314 filename = filename.Substring(idx);
315 f_fileName.SetValue(frame, filename);
322 Type.GetType(
"System.AppContextSwitches").GetField(
"_ignorePortablePDBsInStackTraces", BindingFlags.Static | BindingFlags.NonPublic).SetValue(
null, -1);
static readonly Version Version
static readonly Framework Framework
static List< string > ignoreMessages
static HashSet< string > ignoreSources
static List< string > initWarnings
static void PrettifyStackTraceSources(StackFrame[] frames)
static List< string > ignoreContents
static HashSet< string > pastExceptions
static void FirstChanceExceptionHandler(object sender, FirstChanceExceptionEventArgs args)
static void ConfigureAppenders()
static void LogFirstChanceExceptions()
static readonly string LogArchiveDir
static void IgnoreExceptionContents(string source)
static readonly Assembly TerrariaAssembly
static string GetNewLogFile(string baseName)
static List< string > ignoreThrowingMethods
static ThreadLocal< bool > handlerActive
static void EnablePortablePDBTraces()
static Exception previousException
static void IgnoreExceptionSource(string source)
static bool CanOpen(string fileName)
static readonly string LogDir
static void AddChatMessage(string msg, Color color)
This serves as the central class which loads mods. It contains many static fields and methods related...
static readonly string versionedName
@ Environment
Sandstorm, Hell, Above surface during Eclipse, Space
@ Console
Command can be used in server console during MP.