From b930588556d81170d00ea526fb359fe9384bef7a Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Sat, 13 Jan 2018 18:33:33 +0100 Subject: [PATCH 01/28] Introduced serilog in Vocaluxe --- Vocaluxe/Base/CGraphics.cs | 75 ++-- Vocaluxe/Base/CLog.cs | 386 ++++++++++-------- Vocaluxe/Base/CMain.cs | 30 +- Vocaluxe/Base/CSettings.cs | 8 +- Vocaluxe/Base/CSongs.cs | 128 +++--- Vocaluxe/Base/Fonts/CFonts.cs | 16 +- Vocaluxe/Base/ThemeSystem/CSkin.cs | 2 +- Vocaluxe/Base/ThemeSystem/CTheme.cs | 2 +- Vocaluxe/Lib/Draw/CDirect3D.cs | 4 +- Vocaluxe/Lib/Sound/CPortAudioHandle.cs | 4 +- .../Lib/Video/Acinerella/CDecoderThread.cs | 2 +- Vocaluxe/Program.cs | 310 ++++++++------ Vocaluxe/Properties/AssemblyInfo.cs | 2 +- Vocaluxe/Screens/CScreenLoad.cs | 20 +- Vocaluxe/Vocaluxe.csproj | 29 ++ Vocaluxe/packages.config | 7 + VocaluxeLib/IBasic.cs | 10 +- VocaluxeLib/Songs/CSongLoader.cs | 126 +++--- VocaluxeTests/PartyModeTests.cs | 2 +- 19 files changed, 665 insertions(+), 498 deletions(-) diff --git a/Vocaluxe/Base/CGraphics.cs b/Vocaluxe/Base/CGraphics.cs index 97ca9131..0c63169e 100644 --- a/Vocaluxe/Base/CGraphics.cs +++ b/Vocaluxe/Base/CGraphics.cs @@ -20,6 +20,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Windows.Forms; +using Serilog.Events; +using SerilogTimings; using Vocaluxe.Base.Fonts; using Vocaluxe.Base.ThemeSystem; using Vocaluxe.Screens; @@ -61,36 +63,35 @@ namespace Vocaluxe.Base public static void Init() { // Add Screens, must be the same order as in EScreens! - CLog.StartBenchmark("Build Screen List"); + CLog.LogInformation("Build Screen List"); + using (Operation.At(LogEventLevel.Information).Time("Build Screen List")) + { + _Screens.Add(new CScreenTest()); + _Screens.Add(new CScreenLoad()); + _Screens.Add(new CScreenMain()); + _Screens.Add(new CScreenSong()); + _Screens.Add(new CScreenOptions()); + _Screens.Add(new CScreenSing()); + _Screens.Add(new CScreenProfiles()); + _Screens.Add(new CScreenScore()); + _Screens.Add(new CScreenHighscore()); + _Screens.Add(new CScreenOptionsGame()); + _Screens.Add(new CScreenOptionsSound()); + _Screens.Add(new CScreenOptionsRecord()); + _Screens.Add(new CScreenOptionsVideo()); + _Screens.Add(new CScreenOptionsVideoAdjustments()); + _Screens.Add(new CScreenOptionsLyrics()); + _Screens.Add(new CScreenOptionsTheme()); + _Screens.Add(new CScreenNames()); + _Screens.Add(new CScreenCredits()); + _Screens.Add(new CScreenParty()); - _Screens.Add(new CScreenTest()); - _Screens.Add(new CScreenLoad()); - _Screens.Add(new CScreenMain()); - _Screens.Add(new CScreenSong()); - _Screens.Add(new CScreenOptions()); - _Screens.Add(new CScreenSing()); - _Screens.Add(new CScreenProfiles()); - _Screens.Add(new CScreenScore()); - _Screens.Add(new CScreenHighscore()); - _Screens.Add(new CScreenOptionsGame()); - _Screens.Add(new CScreenOptionsSound()); - _Screens.Add(new CScreenOptionsRecord()); - _Screens.Add(new CScreenOptionsVideo()); - _Screens.Add(new CScreenOptionsVideoAdjustments()); - _Screens.Add(new CScreenOptionsLyrics()); - _Screens.Add(new CScreenOptionsTheme()); - _Screens.Add(new CScreenNames()); - _Screens.Add(new CScreenCredits()); - _Screens.Add(new CScreenParty()); - - Debug.Assert(_Screens.Count == (int)EScreen.CountEntry, "Screen list and screens enum do not match"); - - _PopupScreens.Add(new CPopupScreenPlayerControl()); - _PopupScreens.Add(new CPopupScreenVolumeControl()); - _PopupScreens.Add(new CPopupScreenServerQR()); - - CLog.StopBenchmark("Build Screen List"); + Debug.Assert(_Screens.Count == (int)EScreen.CountEntry, "Screen list and screens enum do not match"); + _PopupScreens.Add(new CPopupScreenPlayerControl()); + _PopupScreens.Add(new CPopupScreenVolumeControl()); + _PopupScreens.Add(new CPopupScreenServerQR()); + } CurrentScreen = _Screens[(int)EScreen.Load]; NextScreen = null; _CurrentPopupScreen = EPopupScreens.NoPopup; @@ -99,9 +100,11 @@ namespace Vocaluxe.Base GlobalAlpha = 1f; ZOffset = 0f; - CLog.StartBenchmark("Load Theme"); - LoadTheme(); - CLog.StopBenchmark("Load Theme"); + CLog.LogInformation("Load Theme"); + using (Operation.At(LogEventLevel.Information).Time("Load Theme")) + { + LoadTheme(); + } } public static void Close() @@ -118,10 +121,12 @@ namespace Vocaluxe.Base for (int i = 0; i < _Screens.Count; i++) { - CLog.StartBenchmark("Load Theme " + Enum.GetNames(typeof(EScreen))[i]); - _Screens[i].Init(); - _Screens[i].LoadTheme(CThemes.GetThemeScreensPath(_Screens[i].PartyModeID)); - CLog.StopBenchmark("Load Theme " + Enum.GetNames(typeof(EScreen))[i]); + CLog.LogInformation("Load Theme " + Enum.GetNames(typeof(EScreen))[i]); + using (Operation.At(LogEventLevel.Information).Time("Load Theme " + Enum.GetNames(typeof(EScreen))[i])) + { + _Screens[i].Init(); + _Screens[i].LoadTheme(CThemes.GetThemeScreensPath(_Screens[i].PartyModeID)); + } } foreach (IMenu popup in _PopupScreens) diff --git a/Vocaluxe/Base/CLog.cs b/Vocaluxe/Base/CLog.cs index f91b9d00..7b8af970 100644 --- a/Vocaluxe/Base/CLog.cs +++ b/Vocaluxe/Base/CLog.cs @@ -16,210 +16,262 @@ #endregion using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using System.Text; +using System.Text.RegularExpressions; using System.Windows.Forms; +using Serilog; +using Serilog.Configuration; +using Serilog.Core; +using Serilog.Context; +using Serilog.Events; +using Serilog.Formatting.Compact; namespace Vocaluxe.Base { - class CLogFile : IDisposable - { - private readonly string _LogFileName; - private readonly string _LogName; - private StreamWriter _LogFile; - private readonly Object _FileMutex = new Object(); - - public CLogFile(string fileName, string logName) - { - _LogName = logName; - _LogFileName = fileName; - } - - private void _Open() - { - lock (_FileMutex) - { - if (_LogFile != null) - return; - _LogFile = new StreamWriter(Path.Combine(CSettings.DataFolder, _LogFileName), false, Encoding.UTF8); - - _LogFile.WriteLine(_LogName + " " + CSettings.GetFullVersionText() + " " + DateTime.Now); - _LogFile.WriteLine("----------------------------------------"); - _LogFile.WriteLine(); - } - } - - public void Close() - { - lock (_FileMutex) - { - if (_LogFile == null) - return; - try - { - _LogFile.Flush(); - _LogFile.Close(); - } - catch (Exception) {} - } - } - - public virtual void Add(string text) - { - if (_LogFile == null) - _Open(); - - // ReSharper disable PossibleNullReferenceException - _LogFile.WriteLine(text); - // ReSharper restore PossibleNullReferenceException - _LogFile.Flush(); - } - - public void Dispose() - { - Close(); - GC.SuppressFinalize(this); - } - } - - class CErrorLogFile : CLogFile - { - private int _NumErrors; - private readonly Object _WriteMutex = new Object(); - - public CErrorLogFile(string fileName, string logName) : base(fileName, logName) {} - - public override void Add(string errorText) - { - lock (_WriteMutex) - { - _NumErrors++; - base.Add(_NumErrors + ") " + errorText); - } - } - } - static class CLog { - private const int _MaxBenchmarks = 10; - private static int _BenchmarksRunning; private static bool _Initialized; - private static CLogFile _ErrorLog; - private static CLogFile _PerformanceLog; - private static CLogFile _BenchmarkLog; - private static CLogFile _DebugLog; - private static CLogFile _SongInfoLog; + private static readonly StringBuilder _MainLogStringBuilder = new StringBuilder(); + private static readonly StringWriter _MainLogStringWriter = new StringWriter(_MainLogStringBuilder); - private static Stopwatch[] _BenchmarkTimer; - private static readonly double _NanosecPerTick = (1000.0 * 1000.0 * 1000.0) / Stopwatch.Frequency; + private static Logger _MainLog; + private static Logger _SongInfoLog; + private static string _LogFolder; - public static void Init() + private const string _MainLogTemplate = "[{TimeStampFromStart}] [{Level}] {Message}{NewLine}{Properties}{NewLine}{Exception}"; + private const string _SongInfoLogTemplate = "{Message}{NewLine}Additional info:{Properties}{NewLine}{Exception}"; + + public static void Init(string logFolder) { - _ErrorLog = new CErrorLogFile(CSettings.FileNameErrorLog, "Error-Log"); - _PerformanceLog = new CLogFile(CSettings.FileNamePerformanceLog, "Performance-Log"); - _BenchmarkLog = new CLogFile(CSettings.FileNameBenchmarkLog, "Benchmark-Log"); - _DebugLog = new CLogFile(CSettings.FileNameDebugLog, "Debug-Log"); - _SongInfoLog = new CLogFile(CSettings.FileNameSongInfoLog, "Song-Information-Log"); + _LogFolder = logFolder; - _BenchmarkTimer = new Stopwatch[_MaxBenchmarks]; - for (int i = 0; i < _BenchmarkTimer.Length; i++) - _BenchmarkTimer[i] = new Stopwatch(); - _BenchmarksRunning = 0; + _RollLogs(Path.Combine(_LogFolder, CSettings.FileNameMainLog), 2); + _RollLogs(Path.Combine(_LogFolder, CSettings.FileNameSongInfoLog), 2); + + _MainLog = new LoggerConfiguration() + .Enrich.WithThreadId() + .Enrich.FromLogContext() + .Enrich.WithTimeStampFromStart() + .WriteTo.TextWriter(_MainLogStringWriter, outputTemplate: _MainLogTemplate) + // Json can be activated by adding "new CompactJsonFormatter()" as first argument + .WriteTo.File(Path.Combine(_LogFolder, CSettings.FileNameMainLog), flushToDiskInterval: TimeSpan.FromSeconds(30), outputTemplate: _MainLogTemplate) +#if DEBUG + .WriteTo.Console(outputTemplate: _MainLogTemplate) +#endif + .CreateLogger(); + + Log.Logger = _MainLog; + + _SongInfoLog = new LoggerConfiguration() + .WriteTo.File(Path.Combine(_LogFolder, CSettings.FileNameSongInfoLog), flushToDiskInterval: TimeSpan.FromSeconds(60), outputTemplate: _SongInfoLogTemplate) +#if DEBUG + .WriteTo.Console(outputTemplate: "[SongInfo] " + _SongInfoLogTemplate) +#endif + .CreateLogger(); + _Initialized = true; } + public static void Close() { if (_Initialized) { - _ErrorLog.Dispose(); - _ErrorLog = null; - _PerformanceLog.Dispose(); - _PerformanceLog = null; - _BenchmarkLog.Dispose(); - _BenchmarkLog = null; - _DebugLog.Dispose(); - _DebugLog = null; + + _MainLog.Dispose(); + _MainLog = null; _SongInfoLog.Dispose(); _SongInfoLog = null; _Initialized = false; } } - #region LogError - public static void LogError(string errorText, bool show = false, bool exit = false, Exception e = null) + public static string MainLogEntries { - if (show) - MessageBox.Show(errorText, CSettings.ProgramName, MessageBoxButtons.OK, MessageBoxIcon.Error); - if (e != null) - errorText += ": " + e; - _ErrorLog.Add(errorText); - if (exit) - Environment.Exit(Environment.ExitCode); - } - #endregion LogError - - public static void LogDebug(string text) - { - _DebugLog.Add(String.Format("{0:HH:mm:ss.ffff}", DateTime.Now) + ":" + text); -#if DEBUG - Console.WriteLine(text); -#endif - } - - public static void LogSongInfo(string text) - { - _SongInfoLog.Add(text); - } - - public static void LogPerformance(string text) - { - _PerformanceLog.Add(text); - _PerformanceLog.Add("-------------------------------"); - } - - #region LogBenchmark - public static void StartBenchmark(string text) - { - if (_BenchmarksRunning < _MaxBenchmarks) - { - string space = String.Empty; - for (int i = 0; i < _BenchmarksRunning; i++) - space += " "; - _BenchmarkLog.Add(space + "Start " + text); - - _BenchmarkTimer[_BenchmarksRunning].Restart(); + get { + _MainLogStringWriter.Flush(); + return _MainLogStringBuilder.ToString(); } - else - LogError("Tried to start to many benchmarks at once"); //Log and ignore - _BenchmarksRunning++; //Inc even if not started to correct right Stop() call } - public static void StopBenchmark(string text) + public static void LogError(Exception exception, string errorText, bool show = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", + [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) { - if (_BenchmarksRunning > 0 && _BenchmarksRunning <= _MaxBenchmarks) + using (LogContext.PushProperty("CallingContext", new { callerMethodeName = callerMethodeName, callerFilePath = callerFilePath, callerLineNumer = callerLineNumer })) { - _BenchmarksRunning--; - _BenchmarkTimer[_BenchmarksRunning].Stop(); - - string space = String.Empty; - for (int i = 0; i < _BenchmarksRunning; i++) - space += " "; - - float ms; - if (Stopwatch.IsHighResolution && _NanosecPerTick > 0) - ms = (float)((_NanosecPerTick * _BenchmarkTimer[_BenchmarksRunning].ElapsedTicks) / (1000.0 * 1000.0)); + if (exit) + { + _MainLog.Fatal(exception, errorText, propertyValues); + } else - ms = _BenchmarkTimer[_BenchmarksRunning].ElapsedMilliseconds; + { + _MainLog.Error(exception, errorText, propertyValues); + } + } - _BenchmarkLog.Add(space + "Stop " + text + ", Elapsed Time: " + ms.ToString("0.000") + "ms"); - _BenchmarkLog.Add(String.Empty); + if (show) + { + //Todo: Logsend assistent + MessageBox.Show(errorText, CSettings.ProgramName, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + + if (exit) + { + // Flush the logging piplines before exiting + _MainLog.Dispose(); + _SongInfoLog.Dispose(); + // Close the Programm + Environment.Exit(Environment.ExitCode); } - else - _BenchmarksRunning--; } - #endregion LogBenchmark + + public static void LogError(string errorText, bool show = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", + [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) + { + // ReSharper disable ExplicitCallerInfoArgument + LogError(null, errorText, show, exit, callerMethodeName, callerFilePath, callerLineNumer, propertyValues); + } + + public static void LogWarning(string text, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName = callerMethodeName, callerFilePath = callerFilePath, callerLineNumer = callerLineNumer })) + { + _MainLog.Warning(text, propertyValues); + } + } + + public static void LogInformation(string text, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName = callerMethodeName, callerFilePath = callerFilePath, callerLineNumer = callerLineNumer })) + { + _MainLog.Information(text, propertyValues); + } + } + + public static void LogDebug(string text, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName = callerMethodeName, callerFilePath = callerFilePath, callerLineNumer = callerLineNumer })) + { + _MainLog.Debug(text, propertyValues); + } + } + + public static void LogSongError(Exception exception, string text, params object[] propertyValues) + { + _SongInfoLog.Error(exception, text, propertyValues); + } + public static void LogSongError(string text, params object[] propertyValues) + { + _SongInfoLog.Error(text, propertyValues); + } + + public static void LogSongWaring(string text, params object[] propertyValues) + { + _SongInfoLog.Warning(text, propertyValues); + } + + #region Log rolling + + private static void _RollLogs(string mainLogFile, int numLogsToKeep) + { + _DeleteOldLogs(mainLogFile, numLogsToKeep); + + string currentSourceFile = _GenerateRollLogFileName(mainLogFile, numLogsToKeep); + + + for (var i = numLogsToKeep; i > 1; i--) + { + string currentTargetFile = currentSourceFile; + currentSourceFile = _GenerateRollLogFileName(mainLogFile, i - 1); + + if (File.Exists(currentSourceFile)) + { + try + { + File.Move(currentSourceFile, currentTargetFile); + } + catch (Exception) + { + // Cant log anything here as the log isn't initialized yet + } + } + } + + if (File.Exists(mainLogFile)) + { + try { + if(numLogsToKeep > 0) + File.Move(mainLogFile, currentSourceFile); + else + File.Delete(mainLogFile); + } + catch (Exception) + { + // Cant log anything here as the log isn't initialized yet + } + } + } + + private static void _DeleteOldLogs(string mainLogFile, int numLogsToKeep) + { + Regex r = new Regex($"{Path.GetDirectoryName(mainLogFile)}\\\\{Path.GetFileNameWithoutExtension(mainLogFile)}_([0-9]+){Path.GetExtension(mainLogFile)}", RegexOptions.IgnoreCase); + IEnumerable logFiles = Directory.EnumerateFiles($"{Path.GetDirectoryName(mainLogFile)}", $"{Path.GetFileNameWithoutExtension(mainLogFile)}*{Path.GetExtension(mainLogFile)}"); + foreach (string file in logFiles) + { + Match m = r.Match(file); + if (m.Success) + { + int logNum = -1; + Int32.TryParse(m.Groups[1].Value, out logNum); + + if (logNum < 0 || logNum > numLogsToKeep - 1) + { + try + { + File.Delete(file); + } + catch (Exception) + { + // Cant log anything here as the log isn't initialized yet + } + + } + } + } + } + + private static string _GenerateRollLogFileName(string mainLogFile, int number) + { + return $"{Path.GetDirectoryName(mainLogFile)}\\{Path.GetFileNameWithoutExtension(mainLogFile)}_{number}{Path.GetExtension(mainLogFile)}"; + } + + #endregion + + #region CTimeStampFromStartEnricher + + // ReSharper disable once InconsistentNaming + private static LoggerConfiguration WithTimeStampFromStart(this LoggerEnrichmentConfiguration enrich) + { + return enrich.With(new CTimeStampFromStartEnricher()); + } + + private class CTimeStampFromStartEnricher : ILogEventEnricher + { + private readonly DateTimeOffset _Start = DateTimeOffset.Now; + + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory lepf) + { + logEvent.AddPropertyIfAbsent( + lepf.CreateProperty("TimeStampFromStart", logEvent.Timestamp - _Start)); + } + } + + #endregion } } \ No newline at end of file diff --git a/Vocaluxe/Base/CMain.cs b/Vocaluxe/Base/CMain.cs index 61bfcf69..b4256fa0 100644 --- a/Vocaluxe/Base/CMain.cs +++ b/Vocaluxe/Base/CMain.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; +using System.Runtime.CompilerServices; using Vocaluxe.Base.Fonts; using Vocaluxe.Base.ThemeSystem; using VocaluxeLib; @@ -507,21 +508,38 @@ namespace Vocaluxe.Base } } + // ReSharper disable ExplicitCallerInfoArgument class CBlog : ILog { - public void LogError(string errorText, bool showMsg = false, bool exit = false) + public void LogError(Exception exeption, string errorText, bool showMsg = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) { - CLog.LogError(errorText, showMsg, exit); + CLog.LogError(exeption, errorText, showMsg, exit, callerMethodeName, callerFilePath, callerLineNumer, propertyValues); } - public void LogDebug(string text) + public void LogError(string errorText, bool showMsg = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) { - CLog.LogDebug(text); + CLog.LogError(null, errorText, showMsg, exit, callerMethodeName, callerFilePath, callerLineNumer, propertyValues); } - public void LogSongInfo(string text) + public void LogDebug(string text, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) { - CLog.LogSongInfo(text); + + CLog.LogDebug(text, callerMethodeName, callerFilePath, callerLineNumer, propertyValues); + } + + public void LogSongWarning(string text, params object[] propertyValues) + { + CLog.LogSongWaring(text, propertyValues); + } + + public void LogSongError(Exception exception, string text, params object[] propertyValues) + { + CLog.LogSongError(exception, text, propertyValues); + } + + public void LogSongError(string text, params object[] propertyValues) + { + CLog.LogSongError(text, propertyValues); } } diff --git a/Vocaluxe/Base/CSettings.cs b/Vocaluxe/Base/CSettings.cs index 8bc61828..5a6a8998 100644 --- a/Vocaluxe/Base/CSettings.cs +++ b/Vocaluxe/Base/CSettings.cs @@ -111,11 +111,8 @@ namespace Vocaluxe.Base public const string FileNameOldHighscoreDB = "Ultrastar.db"; public const string FileNameCoverDB = "CoverDB.sqlite"; public const string FileNameCreditsRessourcesDB = "CreditsRessourcesDB.sqlite"; - - public const string FileNamePerformanceLog = "Performance.log"; - public const string FileNameErrorLog = "Error.log"; - public const string FileNameBenchmarkLog = "Benchmark.log"; - public const string FileNameDebugLog = "Debug.log"; + + public const string FileNameMainLog = "Vocaluxe.log"; public const string FileNameSongInfoLog = "SongInformation.log"; public const string FileNameRequiredSkinElements = "RequiredSkinElements.xml"; @@ -135,6 +132,7 @@ namespace Vocaluxe.Base public const string FolderNameLanguages = "Languages"; public const string FolderNameScreenshots = "Screenshots"; public const string FolderNameBackgroundMusic = "BackgroundMusic"; + public const string FolderNameLogs = "Logs"; public const string FolderNamePartyModes = "PartyModes"; public const string FolderNamePartyModeCode = "Code"; diff --git a/Vocaluxe/Base/CSongs.cs b/Vocaluxe/Base/CSongs.cs index ce0fbb66..5429b65b 100644 --- a/Vocaluxe/Base/CSongs.cs +++ b/Vocaluxe/Base/CSongs.cs @@ -22,6 +22,8 @@ using System.Linq; using System.Threading; using System.IO; using System.Threading.Tasks; +using Serilog.Events; +using SerilogTimings; using VocaluxeLib; using VocaluxeLib.Songs; @@ -333,54 +335,65 @@ namespace Vocaluxe.Base public static void LoadSongs() { - CLog.StartBenchmark("Load Songs"); - SongsLoaded = false; - _Songs.Clear(); - - CLog.StartBenchmark("List Songs"); - var files = new List(); - foreach (string p in CConfig.SongFolders) + CLog.LogInformation("Load Songs"); + using (Operation.At(LogEventLevel.Information).Time("Load Songs")) { - if (Directory.Exists(p)) + SongsLoaded = false; + _Songs.Clear(); + + var files = new List(); + + CLog.LogInformation("List Songs"); + using (Operation.At(LogEventLevel.Information).Time("List Songs")) { - string path = p; - files.AddRange(CHelper.ListFiles(path, "*.txt", true, true)); - files.AddRange(CHelper.ListFiles(path, "*.txd", true, true)); + + foreach (string p in CConfig.SongFolders) + { + if (Directory.Exists(p)) + { + string path = p; + files.AddRange(CHelper.ListFiles(path, "*.txt", true, true)); + files.AddRange(CHelper.ListFiles(path, "*.txd", true, true)); + } + } + } + + CLog.LogInformation("Read TXTs"); + using (Operation.At(LogEventLevel.Information).Time("Read TXTs")) + { + foreach (string file in files) + { + CSong song = CSong.LoadSong(file); + if (song == null) + continue; + song.ID = _Songs.Count; + if (song.LoadNotes()) + _Songs.Add(song); + } + } + + CLog.LogInformation("Sort Songs"); + using (Operation.At(LogEventLevel.Information).Time("Sorted Songs")) + { + Sorter.SongSorting = CConfig.Config.Game.SongSorting; + Sorter.IgnoreArticles = CConfig.Config.Game.IgnoreArticles; + Categorizer.Tabs = CConfig.Config.Game.Tabs; + Categorizer.ObjectChanged += _HandleCategoriesChanged; + } + + Category = -1; + SongsLoaded = true; + + switch (CConfig.Config.Theme.CoverLoading) + { + case ECoverLoading.TR_CONFIG_COVERLOADING_ATSTART: + _LoadCovers(); + break; + case ECoverLoading.TR_CONFIG_COVERLOADING_DYNAMIC: + _LoadCoversAsync(); + break; } } - CLog.StopBenchmark("List Songs"); - - CLog.StartBenchmark("Read TXTs"); - foreach (string file in files) - { - CSong song = CSong.LoadSong(file); - if (song == null) - continue; - song.ID = _Songs.Count; - if (song.LoadNotes()) - _Songs.Add(song); - } - CLog.StopBenchmark("Read TXTs"); - - CLog.StartBenchmark("Sort Songs"); - Sorter.SongSorting = CConfig.Config.Game.SongSorting; - Sorter.IgnoreArticles = CConfig.Config.Game.IgnoreArticles; - Categorizer.Tabs = CConfig.Config.Game.Tabs; - Categorizer.ObjectChanged += _HandleCategoriesChanged; - CLog.StopBenchmark("Sort Songs"); - Category = -1; - SongsLoaded = true; - - switch (CConfig.Config.Theme.CoverLoading) - { - case ECoverLoading.TR_CONFIG_COVERLOADING_ATSTART: - _LoadCovers(); - break; - case ECoverLoading.TR_CONFIG_COVERLOADING_DYNAMIC: - _LoadCoversAsync(); - break; - } - CLog.StopBenchmark("Load Songs"); } private static void _LoadCoversAsync() @@ -396,25 +409,28 @@ namespace Vocaluxe.Base private static void _LoadCovers() { - CLog.StartBenchmark("Load Covers"); - int songCount = _Songs.Count; - AutoResetEvent ev = new AutoResetEvent(songCount == 0); - - NumSongsWithCoverLoaded = 0; - foreach (CSong song in _Songs) + CLog.LogInformation("Load Covers"); + using (Operation.At(LogEventLevel.Information).Time("Loaded Covers")) { - CSong tmp = song; - Task.Factory.StartNew(() => + int songCount = _Songs.Count; + AutoResetEvent ev = new AutoResetEvent(songCount == 0); + + NumSongsWithCoverLoaded = 0; + foreach (CSong song in _Songs) + { + CSong tmp = song; + Task.Factory.StartNew(() => { tmp.LoadSmallCover(); if (Interlocked.Increment(ref _NumSongsWithCoverLoaded) >= songCount) ev.Set(); }); + } + + ev.WaitOne(); + _CoverLoaded = true; + CDataBase.CommitCovers(); } - ev.WaitOne(); - _CoverLoaded = true; - CDataBase.CommitCovers(); - CLog.StopBenchmark("Load Covers"); } } } \ No newline at end of file diff --git a/Vocaluxe/Base/Fonts/CFonts.cs b/Vocaluxe/Base/Fonts/CFonts.cs index 5e242e2c..ae9d1af9 100644 --- a/Vocaluxe/Base/Fonts/CFonts.cs +++ b/Vocaluxe/Base/Fonts/CFonts.cs @@ -21,6 +21,8 @@ using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; +using Serilog.Events; +using SerilogTimings; using VocaluxeLib; using VocaluxeLib.Menu; using VocaluxeLib.Xml; @@ -217,7 +219,7 @@ namespace Vocaluxe.Base.Fonts } } else - CLog.LogError("Empty fontName requested", false, false, new Exception()); + CLog.LogError("Empty fontName requested"); if (_FontFamilies.Count == 0) CLog.LogError("No fonts found!", true, true); @@ -265,7 +267,7 @@ namespace Vocaluxe.Base.Fonts } catch (Exception e) { - CLog.LogError("Error loading default Fonts", false, false, e); + CLog.LogError(e, "Error loading default Fonts"); return false; } @@ -287,9 +289,13 @@ namespace Vocaluxe.Base.Fonts return false; } } - CLog.StartBenchmark("BuildGlyphs"); - _BuildGlyphs(); - CLog.StopBenchmark("BuildGlyphs"); + + CLog.LogInformation("Build Glyphs"); + using (Operation.At(LogEventLevel.Information).Time("Build Glyphs")) + { + _BuildGlyphs(); + } + return true; } diff --git a/Vocaluxe/Base/ThemeSystem/CSkin.cs b/Vocaluxe/Base/ThemeSystem/CSkin.cs index 6e04b079..379ed66a 100644 --- a/Vocaluxe/Base/ThemeSystem/CSkin.cs +++ b/Vocaluxe/Base/ThemeSystem/CSkin.cs @@ -147,7 +147,7 @@ namespace Vocaluxe.Base.ThemeSystem } catch (Exception e) { - CLog.LogError("Can't load skin file \"" + Path.Combine(_Folder, _FileName) + "\". Invalid file!", false, false, e); + CLog.LogError(e, "Can't load skin file \"" + Path.Combine(_Folder, _FileName) + "\". Invalid file!"); return false; } diff --git a/Vocaluxe/Base/ThemeSystem/CTheme.cs b/Vocaluxe/Base/ThemeSystem/CTheme.cs index ef37438e..6014da02 100644 --- a/Vocaluxe/Base/ThemeSystem/CTheme.cs +++ b/Vocaluxe/Base/ThemeSystem/CTheme.cs @@ -80,7 +80,7 @@ namespace Vocaluxe.Base.ThemeSystem } catch (Exception e) { - CLog.LogError("Can't load theme \"" + _FileName + "\". Invalid file!", false, false, e); + CLog.LogError(e, "Can't load theme \"" + _FileName + "\". Invalid file!"); return false; } diff --git a/Vocaluxe/Lib/Draw/CDirect3D.cs b/Vocaluxe/Lib/Draw/CDirect3D.cs index 9960e86f..11dd3c22 100644 --- a/Vocaluxe/Lib/Draw/CDirect3D.cs +++ b/Vocaluxe/Lib/Draw/CDirect3D.cs @@ -94,7 +94,7 @@ namespace Vocaluxe.Lib.Draw } catch (Direct3DX9NotFoundException e) { - CLog.LogError("No DirectX runtimes were found, please download and install them from http://www.microsoft.com/download/en/details.aspx?id=8109", true, true, e); + CLog.LogError(e, "No DirectX runtimes were found, please download and install them from http://www.microsoft.com/download/en/details.aspx?id=8109"); } _Form.KeyDown += _OnKeyDown; @@ -177,7 +177,7 @@ namespace Vocaluxe.Lib.Draw } catch (Exception e) { - CLog.LogError("Error during D3D device creation.", false, false, e); + CLog.LogError(e, "Error during D3D device creation."); } finally { diff --git a/Vocaluxe/Lib/Sound/CPortAudioHandle.cs b/Vocaluxe/Lib/Sound/CPortAudioHandle.cs index 81c4d9b0..355b4b45 100644 --- a/Vocaluxe/Lib/Sound/CPortAudioHandle.cs +++ b/Vocaluxe/Lib/Sound/CPortAudioHandle.cs @@ -86,7 +86,7 @@ namespace Vocaluxe.Lib.Sound } catch (Exception ex) { - CLog.LogError(errorText: $"Error disposing PortAudio: {ex.Message}", e: ex); + CLog.LogError(ex, "Error disposing PortAudio"); } } _Disposed = true; @@ -179,7 +179,7 @@ namespace Vocaluxe.Lib.Sound } catch (Exception ex) { - CLog.LogError(errorText:$"Error closing stream: {ex.Message}", e:ex); + CLog.LogError(ex, "Error closing stream:"); } _Streams.Remove(stream); } diff --git a/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs b/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs index 3d7229d0..64385abe 100644 --- a/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs +++ b/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs @@ -437,7 +437,7 @@ namespace Vocaluxe.Lib.Video.Acinerella } catch (Exception e) { - CLog.LogError("Couldn't copy the frame to the managed environment.", false, false, e); + CLog.LogError(e, "Couldn't copy the frame to the managed environment."); return false; } diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index 136508fd..590a0127 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -24,6 +24,8 @@ using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Threading; using System.Windows.Forms; +using Serilog.Events; +using SerilogTimings; using Vocaluxe.Base; using Vocaluxe.Base.Fonts; using Vocaluxe.Base.Server; @@ -96,170 +98,210 @@ namespace Vocaluxe Directory.CreateDirectory(CSettings.DataFolder); // Init Log - CLog.Init(); + CLog.Init(CSettings.FolderNameLogs); if (!CProgrammHelper.CheckRequirements()) return; CProgrammHelper.Init(); - CLog.StartBenchmark("Init Program"); - CMain.Init(); - Application.DoEvents(); + CLog.LogInformation("Init Program"); + using (Operation.At(LogEventLevel.Information).Time("Init Program")) + { + CMain.Init(); + Application.DoEvents(); - // Init Language - CLog.StartBenchmark("Init Language"); - if (!CLanguage.Init()) - throw new CLoadingException("Language"); - CLog.StopBenchmark("Init Language"); + // Init Language + CLog.LogInformation("Init Language"); + using (Operation.At(LogEventLevel.Information).Time("Init Language")) + { + if (!CLanguage.Init()) + throw new CLoadingException("Language"); + } - Application.DoEvents(); + Application.DoEvents(); - // load config - CLog.StartBenchmark("Init Config"); - CConfig.LoadCommandLineParams(args); - CConfig.UseCommandLineParamsBefore(); - CConfig.Init(); - CConfig.UseCommandLineParamsAfter(); - CLog.StopBenchmark("Init Config"); + // load config + CLog.LogInformation("Init Config"); + using (Operation.At(LogEventLevel.Information).Time("Init Config")) + { + CConfig.LoadCommandLineParams(args); + CConfig.UseCommandLineParamsBefore(); + CConfig.Init(); + CConfig.UseCommandLineParamsAfter(); + } - // Create folders - CSettings.CreateFolders(); + // Create folders + CSettings.CreateFolders(); - _SplashScreen = new CSplashScreen(); - Application.DoEvents(); + _SplashScreen = new CSplashScreen(); + Application.DoEvents(); - // Init Draw - CLog.StartBenchmark("Init Draw"); - if (!CDraw.Init()) - throw new CLoadingException("drawing"); - CLog.StopBenchmark("Init Draw"); + // Init Draw + CLog.LogInformation("Init Draw"); + using (Operation.At(LogEventLevel.Information).Time("Init Draw")) + { + if (!CDraw.Init()) + throw new CLoadingException("drawing"); + } - Application.DoEvents(); + Application.DoEvents(); - // Init Playback - CLog.StartBenchmark("Init Playback"); - if (!CSound.Init()) - throw new CLoadingException("playback"); - CLog.StopBenchmark("Init Playback"); + // Init Playback + CLog.LogInformation("Init Playback"); + using (Operation.At(LogEventLevel.Information).Time("Init Playback")) + { + if (!CSound.Init()) + throw new CLoadingException("playback"); + } - Application.DoEvents(); + Application.DoEvents(); - // Init Record - CLog.StartBenchmark("Init Record"); - if (!CRecord.Init()) - throw new CLoadingException("record"); - CLog.StopBenchmark("Init Record"); + // Init Record + CLog.LogInformation("Init Record"); + using (Operation.At(LogEventLevel.Information).Time("Init Record")) + { + if (!CRecord.Init()) + throw new CLoadingException("record"); + } - Application.DoEvents(); + Application.DoEvents(); - // Init VideoDecoder - CLog.StartBenchmark("Init Videodecoder"); - if (!CVideo.Init()) - throw new CLoadingException("video"); - CLog.StopBenchmark("Init Videodecoder"); + // Init VideoDecoder + CLog.LogInformation("Init Videodecoder"); + using (Operation.At(LogEventLevel.Information).Time("Init Videodecoder")) + { + if (!CVideo.Init()) + throw new CLoadingException("video"); + } - Application.DoEvents(); + Application.DoEvents(); - // Init Database - CLog.StartBenchmark("Init Database"); - if (!CDataBase.Init()) - throw new CLoadingException("database"); - CLog.StopBenchmark("Init Database"); + // Init Database + CLog.LogInformation("Init Database"); + using (Operation.At(LogEventLevel.Information).Time("Init Database")) + { + if (!CDataBase.Init()) + throw new CLoadingException("database"); + } - Application.DoEvents(); + Application.DoEvents(); - //Init Webcam - CLog.StartBenchmark("Init Webcam"); - if (!CWebcam.Init()) - throw new CLoadingException("webcam"); - CLog.StopBenchmark("Init Webcam"); + //Init Webcam + CLog.LogInformation("Init Webcam"); + using (Operation.At(LogEventLevel.Information).Time("Init Webcam")) + { + if (!CWebcam.Init()) + throw new CLoadingException("webcam"); + } - Application.DoEvents(); + Application.DoEvents(); - // Init Background Music - CLog.StartBenchmark("Init Background Music"); - CBackgroundMusic.Init(); - CLog.StopBenchmark("Init Background Music"); + // Init Background Music + CLog.LogInformation("Init Background Music"); + using (Operation.At(LogEventLevel.Information).Time("Init Background Music")) + { + CBackgroundMusic.Init(); + } - Application.DoEvents(); + Application.DoEvents(); - // Init Profiles - CLog.StartBenchmark("Init Profiles"); - CProfiles.Init(); - CLog.StopBenchmark("Init Profiles"); + // Init Profiles + CLog.LogInformation("Init Profiles"); + using (Operation.At(LogEventLevel.Information).Time("Init Profiles")) + { + CProfiles.Init(); + } - Application.DoEvents(); + Application.DoEvents(); - // Init Fonts - CLog.StartBenchmark("Init Fonts"); - if (!CFonts.Init()) - throw new CLoadingException("fonts"); - CLog.StopBenchmark("Init Fonts"); + // Init Fonts + CLog.LogInformation("Init Fonts"); + using (Operation.At(LogEventLevel.Information).Time("Init Fonts")) + { + if (!CFonts.Init()) + throw new CLoadingException("fonts"); + } - Application.DoEvents(); + Application.DoEvents(); - // Theme System - CLog.StartBenchmark("Init Theme"); - if (!CThemes.Init()) - throw new CLoadingException("theme"); - CLog.StopBenchmark("Init Theme"); + // Theme System + CLog.LogInformation("Init Theme"); + using (Operation.At(LogEventLevel.Information).Time("Init Theme")) + { + if (!CThemes.Init()) + throw new CLoadingException("theme"); + } - CLog.StartBenchmark("Load Theme"); - CThemes.Load(); - CLog.StopBenchmark("Load Theme"); + CLog.LogInformation("Load Theme"); + using (Operation.At(LogEventLevel.Information).Time("Load Theme")) + { + CThemes.Load(); + } - Application.DoEvents(); + Application.DoEvents(); - // Load Cover - CLog.StartBenchmark("Init Cover"); - if (!CCover.Init()) - throw new CLoadingException("covertheme"); - CLog.StopBenchmark("Init Cover"); + // Load Cover + CLog.LogInformation("Init Cover"); + using (Operation.At(LogEventLevel.Information).Time("Init Cover")) + { + if (!CCover.Init()) + throw new CLoadingException("covertheme"); + } - Application.DoEvents(); + Application.DoEvents(); - // Init Screens - CLog.StartBenchmark("Init Screens"); - CGraphics.Init(); - CLog.StopBenchmark("Init Screens"); + // Init Screens + CLog.LogInformation("Init Screens"); + using (Operation.At(LogEventLevel.Information).Time("Init Screens")) + { + CGraphics.Init(); + } - Application.DoEvents(); + Application.DoEvents(); - // Init Server - CLog.StartBenchmark("Init Server"); - CVocaluxeServer.Init(); - CLog.StopBenchmark("Init Server"); + // Init Server + CLog.LogInformation("Init Server"); + using (Operation.At(LogEventLevel.Information).Time("Init Server")) + { + CVocaluxeServer.Init(); + } - Application.DoEvents(); + Application.DoEvents(); - // Init Input - CLog.StartBenchmark("Init Input"); - CController.Init(); - CController.Connect(); - CLog.StopBenchmark("Init Input"); + // Init Input + CLog.LogInformation("Init Input"); + using (Operation.At(LogEventLevel.Information).Time("Init Input")) + { + CController.Init(); + CController.Connect(); + } - Application.DoEvents(); + Application.DoEvents(); - // Init Game; - CLog.StartBenchmark("Init Game"); - CGame.Init(); - CProfiles.Update(); - CConfig.UsePlayers(); - CLog.StopBenchmark("Init Game"); + // Init Game; + CLog.LogInformation("Init Game"); + using (Operation.At(LogEventLevel.Information).Time("Init Game")) + { + CGame.Init(); + CProfiles.Update(); + CConfig.UsePlayers(); + } - Application.DoEvents(); + Application.DoEvents(); - // Init Party Modes; - CLog.StartBenchmark("Init Party Modes"); - if (!CParty.Init()) - throw new CLoadingException("Party Modes"); - CLog.StopBenchmark("Init Party Modes"); + // Init Party Modes; + CLog.LogInformation("Init Party Modes"); + using (Operation.At(LogEventLevel.Information).Time("Init Party Modes")) + { + if (!CParty.Init()) + throw new CLoadingException("Party Modes"); + } - Application.DoEvents(); - //Only reasonable point to call GC.Collect() because initialization may cause lots of garbage - //Rely on GC doing its job afterwards and call Dispose methods where appropriate - GC.Collect(); - CLog.StopBenchmark("Init Program"); + Application.DoEvents(); + //Only reasonable point to call GC.Collect() because initialization may cause lots of garbage + //Rely on GC doing its job afterwards and call Dispose methods where appropriate + GC.Collect(); + } } catch (Exception e) { @@ -288,7 +330,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CController)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CController)"); } try @@ -297,7 +339,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CVocaluxeServer)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CVocaluxeServer)"); } try @@ -306,7 +348,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CGraphics)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CGraphics)"); } try @@ -315,7 +357,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CThemes)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CThemes)"); } try @@ -324,7 +366,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CCover)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CCover)"); } try @@ -333,7 +375,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CFonts)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CFonts)"); } try @@ -342,7 +384,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CBackgroundMusic)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CBackgroundMusic)"); } try @@ -351,7 +393,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CWebcam)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CWebcam)"); } try @@ -360,7 +402,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CDataBase)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CDataBase)"); } try @@ -369,7 +411,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CVideo)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CVideo)"); } try @@ -378,7 +420,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CRecord)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CRecord)"); } try @@ -387,7 +429,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CSound)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CSound)"); } try @@ -396,7 +438,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error during shutdown! (CDraw)", false, false, e); + CLog.LogError(e, "Error during shutdown! (CDraw)"); } GC.Collect(); // Do a GC run here before we close logs to have finalizers run diff --git a/Vocaluxe/Properties/AssemblyInfo.cs b/Vocaluxe/Properties/AssemblyInfo.cs index 5ab36484..856b5d45 100644 --- a/Vocaluxe/Properties/AssemblyInfo.cs +++ b/Vocaluxe/Properties/AssemblyInfo.cs @@ -27,7 +27,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Vocaluxe")] -[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Vocaluxe/Screens/CScreenLoad.cs b/Vocaluxe/Screens/CScreenLoad.cs index 641bad44..61b9e39e 100644 --- a/Vocaluxe/Screens/CScreenLoad.cs +++ b/Vocaluxe/Screens/CScreenLoad.cs @@ -15,8 +15,11 @@ // along with Vocaluxe. If not, see . #endregion +using System; using System.Threading; using System.Windows.Forms; +using Serilog.Events; +using SerilogTimings; using Vocaluxe.Base; using VocaluxeLib; using VocaluxeLib.Menu; @@ -42,6 +45,8 @@ namespace Vocaluxe.Screens private bool _IntroOutPlayed; private CVideoPlayer[] _Intros; + private IDisposable _TimerLoadSongsFull; + public override void Init() { base.Init(); @@ -123,7 +128,9 @@ namespace Vocaluxe.Screens videoPlayer.PreLoad(); } - CLog.StartBenchmark("Load Songs Full"); + CLog.LogInformation("Load Songs Full"); + _TimerLoadSongsFull = Operation.At(LogEventLevel.Information).Time("Loaded Songs Full"); + _SongLoaderThread = new Thread(CSongs.LoadSongs) {Name = "SongLoader", IsBackground = true}; _SongLoaderThread.Start(); CBackgroundMusic.OwnSongsAvailable = false; @@ -176,12 +183,15 @@ namespace Vocaluxe.Screens foreach (CVideoPlayer videoPlayer in _Intros) videoPlayer.Close(); - CLog.StopBenchmark("Load Songs Full"); + // Stop the song full load timer + _TimerLoadSongsFull.Dispose(); //Init Playlists - CLog.StartBenchmark("Init Playlists"); - CPlaylists.Init(); - CLog.StopBenchmark("Init Playlists"); + CLog.LogInformation("Init Playlists"); + using (Operation.At(LogEventLevel.Information).Time("Init Playlists")) + { + CPlaylists.Init(); + } } private void _CheckStartIntroVideos() diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index e21a0817..4b64c235 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -158,6 +158,35 @@ true + + + ..\packages\Serilog.2.5.0\lib\net46\Serilog.dll + True + + + ..\packages\Serilog.Enrichers.Thread.3.0.0\lib\net45\Serilog.Enrichers.Thread.dll + True + + + ..\packages\Serilog.Formatting.Compact.1.0.0\lib\net45\Serilog.Formatting.Compact.dll + True + + + ..\packages\Serilog.Sinks.Console.3.1.1\lib\net45\Serilog.Sinks.Console.dll + True + + + ..\packages\Serilog.Sinks.File.3.1.0\lib\net45\Serilog.Sinks.File.dll + True + + + ..\packages\Serilog.Sinks.TextWriter.2.1.0\lib\net45\Serilog.Sinks.TextWriter.dll + True + + + ..\packages\SerilogTimings.2.2.0\lib\net45\SerilogTimings.dll + True + diff --git a/Vocaluxe/packages.config b/Vocaluxe/packages.config index 437d7321..01998c23 100644 --- a/Vocaluxe/packages.config +++ b/Vocaluxe/packages.config @@ -1,5 +1,12 @@  + + + + + + + diff --git a/VocaluxeLib/IBasic.cs b/VocaluxeLib/IBasic.cs index fd779e59..d730db8b 100644 --- a/VocaluxeLib/IBasic.cs +++ b/VocaluxeLib/IBasic.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; +using System.Runtime.CompilerServices; using VocaluxeLib.Draw; using VocaluxeLib.Game; using VocaluxeLib.Menu; @@ -167,9 +168,12 @@ namespace VocaluxeLib public interface ILog { - void LogError(string errorText, bool showMsg = false, bool exit = false); - void LogDebug(string text); - void LogSongInfo(string text); + void LogError(Exception exeption, string errorText, bool showMsg = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues); + void LogError(string errorText, bool showMsg = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues); + void LogDebug(string text, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues); + void LogSongWarning(string text, params object[] propertyValues); + void LogSongError(Exception exception, string text, params object[] propertyValues); + void LogSongError(string text, params object[] propertyValues); } public interface IFonts diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index b2374e1d..17a7bc7a 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -69,30 +69,7 @@ namespace VocaluxeLib.Songs _Song.FileName = Path.GetFileName(filePath); return true; } - - /// - /// Logs a given message with file name and line# - /// - /// Message - /// True prepends "Error: "; False prepends "Warning: " - /// Adds line# - private void _LogMsg(string msg, bool error, bool withLineNr) - { - msg = (error ? "Error: " : "Warning: ") + msg; - if (withLineNr) - msg += " in line #" + _LineNr; - CBase.Log.LogSongInfo(msg + " (" + Path.Combine(_Song.Folder, _Song.FileName) + ")"); - } - - private void _LogError(string msg, bool withLineNr = true) - { - _LogMsg(msg, true, withLineNr); - } - - private void _LogWarning(string msg, bool withLineNr = true) - { - _LogMsg(msg, false, withLineNr); - } + public bool ReadHeader(bool useSetEncoding = false) { @@ -144,7 +121,8 @@ namespace VocaluxeLib.Songs if (value == "") { _Song.UnknownTags.Add(line); - _LogWarning("Empty value skipped"); + CBase.Log.LogSongWarning("[{SongFileName}] Empty value skipped", _Song.FileName); + continue; } @@ -157,7 +135,7 @@ namespace VocaluxeLib.Songs { if (useSetEncoding) { - _LogWarning("Duplicate encoding ignored"); + CBase.Log.LogSongWarning("[{SongFileName}] Duplicate encoding ignored", _Song.FileName); continue; } sr.Dispose(); @@ -203,7 +181,7 @@ namespace VocaluxeLib.Songs } else { - _LogError("Can't find audio file: " + Path.Combine(_Song.Folder, value)); + CBase.Log.LogSongError("[{SongFileName}] Can't find audio file: {AudioFile}", _Song.FileName, Path.Combine(_Song.Folder, value)); return false; } break; @@ -214,19 +192,19 @@ namespace VocaluxeLib.Songs headerFlags |= EHeaderFlags.BPM; } else - _LogWarning("Invalid BPM value"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid BPM value: {Value}", _Song.FileName, value); break; case "EDITION": if (value.Length > 1) _Song.Editions.Add(value); else - _LogWarning("Invalid edition"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid edition: {Value}", _Song.FileName, value); break; case "GENRE": if (value.Length > 1) _Song.Genres.Add(value); else - _LogWarning("Invalid genre"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid genre: {Value}", _Song.FileName, value); break; case "ALBUM": _Song.Album = value; @@ -236,13 +214,13 @@ namespace VocaluxeLib.Songs if (value.Length == 4 && int.TryParse(value, out num) && num > 0) _Song.Year = value; else - _LogWarning("Invalid year"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid year: {Value}", _Song.FileName, value); break; case "LANGUAGE": if (value.Length > 1) _Song.Languages.Add(_UnifyLanguage(value)); else - _LogWarning("Invalid language"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid language: {Value}", _Song.FileName, value); break; case "COMMENT": if (!String.IsNullOrEmpty(_Song._Comment)) @@ -253,49 +231,49 @@ namespace VocaluxeLib.Songs if (CHelper.TryParse(value, out _Song.Gap)) _Song.Gap /= 1000f; else - _LogWarning("Invalid gap"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid gap: {Value}", _Song.FileName, value); break; case "COVER": if (File.Exists(Path.Combine(_Song.Folder, value))) _Song.CoverFileName = value; else - _LogWarning("Can't find cover file: " + Path.Combine(_Song.Folder, value)); + CBase.Log.LogSongWarning("[{SongFileName}] Can't find cover file: {MissingFile}", _Song.FileName, Path.Combine(_Song.Folder, value)); break; case "BACKGROUND": if (File.Exists(Path.Combine(_Song.Folder, value))) _Song.BackgroundFileNames.Add(value); else - _LogWarning("Can't find background file: " + Path.Combine(_Song.Folder, value)); + CBase.Log.LogSongWarning("[{SongFileName}] Can't find background file: {MissingFile}", _Song.FileName, Path.Combine(_Song.Folder, value)); break; case "VIDEO": if (File.Exists(Path.Combine(_Song.Folder, value))) _Song.VideoFileName = value; else - _LogWarning("Can't find video file: " + Path.Combine(_Song.Folder, value)); + CBase.Log.LogSongWarning("[{SongFileName}] Can't find video file: {MissingFile}", _Song.FileName, Path.Combine(_Song.Folder, value)); break; case "VIDEOGAP": if (!CHelper.TryParse(value, out _Song.VideoGap)) - _LogWarning("Invalid videogap"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid videogap: {Value}", _Song.FileName, value); break; case "VIDEOASPECT": if (!CHelper.TryParse(value, out _Song.VideoAspect, true)) - _LogWarning("Invalid videoaspect"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid videoaspect: {Value}", _Song.FileName, value); break; case "START": if (!CHelper.TryParse(value, out _Song.Start)) - _LogWarning("Invalid start"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid start: {Value}", _Song.FileName, value); break; case "END": if (CHelper.TryParse(value, out _Song.Finish)) _Song.Finish /= 1000f; else - _LogWarning("Invalid end"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid end: {Value}", _Song.FileName, value); break; case "PREVIEWSTART": if (CHelper.TryParse(value, out _Song.Preview.StartTime) && _Song.Preview.StartTime >= 0f) _Song.Preview.Source = EDataSource.Tag; else - _LogWarning("Invalid previewstart"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid previewstart: {Value}", _Song.FileName, value); break; case "PREVIEW": if (CHelper.TryParse(value, out _Song.Preview.StartTime) && _Song.Preview.StartTime >= 0f) @@ -305,19 +283,19 @@ namespace VocaluxeLib.Songs _Song.Preview.Source = EDataSource.Tag; } else - _LogWarning("Invalid previewstart"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid previewstart: {Value}", _Song.FileName, value); break; case "MEDLEYSTARTBEAT": if (int.TryParse(value, out _Song.Medley.StartBeat)) headerFlags |= EHeaderFlags.MedleyStartBeat; else - _LogWarning("Invalid medleystartbeat"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid medleystartbeat: {Value}", _Song.FileName, value); break; case "MEDLEYENDBEAT": if (int.TryParse(value, out _Song.Medley.EndBeat)) headerFlags |= EHeaderFlags.MedleyEndBeat; else - _LogWarning("Invalid medleyendbeat"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid medleyendbeat: {Value}", _Song.FileName, value); break; case "ENDSHORT": if ((headerFlags & EHeaderFlags.BPM) != 0) @@ -329,7 +307,7 @@ namespace VocaluxeLib.Songs _Song.ShortEnd.Source = EDataSource.Tag; } else - _LogWarning("Invalid shortendbeat"); + CBase.Log.LogSongWarning("[{SongFileName}] Invalid shortendbeat: {Value}", _Song.FileName, value); } break; case "CALCMEDLEY": @@ -364,7 +342,7 @@ namespace VocaluxeLib.Songs else { _Song.UnknownTags.Add(line); - _LogWarning("Unknown tag: #" + identifier); + CBase.Log.LogSongWarning("[{SongFileName}] Unknown tag: #{Identifier}", _Song.FileName, identifier); } break; @@ -374,31 +352,32 @@ namespace VocaluxeLib.Songs if (sr.EndOfStream) { //No other data then header - _LogError("Lyrics/Notes missing", false); + CBase.Log.LogSongError("[{SongFileName}] Lyrics/Notes missing", _Song.FileName); + return false; } if ((headerFlags & EHeaderFlags.Title) == 0) { - _LogError("Title tag missing", false); + CBase.Log.LogSongError("[{SongFileName}] Title tag missing", _Song.FileName); return false; } if ((headerFlags & EHeaderFlags.Artist) == 0) { - _LogError("Artist tag missing", false); + CBase.Log.LogSongError("[{SongFileName}] Artist tag missing", _Song.FileName); return false; } if ((headerFlags & EHeaderFlags.MP3) == 0) { - _LogError("MP3 tag missing", false); + CBase.Log.LogSongError("[{SongFileName}] MP3 tag missing", _Song.FileName); return false; } if ((headerFlags & EHeaderFlags.BPM) == 0) { - _LogError("BPM tag missing", false); + CBase.Log.LogSongError("[{SongFileName}] BPM tag missing", _Song.FileName); return false; } @@ -407,7 +386,7 @@ namespace VocaluxeLib.Songs { if (_Song.Medley.StartBeat > _Song.Medley.EndBeat) { - _LogError("MedleyStartBeat is bigger than MedleyEndBeat in file", false); + CBase.Log.LogSongError("[{SongFileName}] MedleyStartBeat is bigger than MedleyEndBeat in file: {StartBeat} > {EndBeat}", _Song.FileName, _Song.Medley.StartBeat > _Song.Medley.EndBeat); headerFlags = headerFlags - EHeaderFlags.MedleyStartBeat - EHeaderFlags.MedleyEndBeat; } } @@ -433,7 +412,7 @@ namespace VocaluxeLib.Songs { if (sr != null) sr.Dispose(); - _LogError("Error reading txt header" + e.Message, false); + CBase.Log.LogSongError(e, "[{SongFileName}] Error reading txt header with Message: {ExceptionMessage}", e.Message, _Song.FileName); return false; } _Song.Encoding = sr.CurrentEncoding; @@ -530,7 +509,7 @@ namespace VocaluxeLib.Songs if (!File.Exists(filePath)) { - _LogError("The file does not exist", false); + CBase.Log.LogSongError("[{SongFileName}] The file songfile does not exist", _Song.FileName); return false; } @@ -579,7 +558,7 @@ namespace VocaluxeLib.Songs if (!int.TryParse(line, out player)) { - _LogError("Wrong or missing number after \"P\""); + CBase.Log.LogSongError("[{SongFileName}] Wrong or missing number after \"P\" (in line {LineNr})", _Song.FileName, _LineNr); return false; } currentBeat = 0; @@ -593,25 +572,25 @@ namespace VocaluxeLib.Songs { if (noteData.Length == 3) { - _LogWarning("Ignored note without text"); + CBase.Log.LogSongWarning("[{SongFileName}] Ignored note without text (in line {LineNr})", _Song.FileName, _LineNr); changesMade.NoTextNoteCt++; continue; } - _LogError("Invalid note found"); + CBase.Log.LogSongError("[{SongFileName}] Invalid note found (in line {LineNr}): {noteData}", _Song.FileName, _LineNr, noteData); sr.Dispose(); return false; } int tone; if (!int.TryParse(noteData[0], out beat) || !int.TryParse(noteData[1], out length) || !int.TryParse(noteData[2], out tone)) { - _LogError("Invalid note found (non-numeric values)"); + CBase.Log.LogSongError("[{SongFileName}] Invalid note found (non-numeric values) (in line {LineNr}): {noteData}", _Song.FileName, _LineNr, noteData); sr.Dispose(); return false; } string text = noteData[3].TrimMultipleWs(); if (text == "") { - _LogWarning("Ignored note without text"); + CBase.Log.LogSongWarning("[{SongFileName}] Ignored note without text (in line {LineNr})", _Song.FileName, _LineNr); changesMade.NoTextNoteCt++; continue; } @@ -622,12 +601,12 @@ namespace VocaluxeLib.Songs changesMade.ZeroLengthNoteCt++; if (_CurrentReadMode == ENoteReadMode.Normal && changesMade.ZeroLengthNoteCt > _MaxZeroNoteCt && changesMade.OverlapNoteCt <= _MaxOverlapNoteCt) { - _LogWarning("Found more than " + _MaxZeroNoteCt + " note with length < 1. Trying alternative read mode."); + CBase.Log.LogSongWarning("[{SongFileName}] Found more than {MaxZeroNoteCt} notes with length < 1. Trying alternative read mode.", _Song.FileName, _MaxZeroNoteCt); _CurrentReadMode = ENoteReadMode.ZeroBased; sr.Dispose(); return ReadNotes(true); } - _LogWarning("Ignored note with length < 1"); + CBase.Log.LogSongWarning("[{SongFileName}] Ignored note with length < 1 (in line {LineNr})", _Song.FileName, _LineNr); } else { @@ -656,13 +635,13 @@ namespace VocaluxeLib.Songs changesMade.OverlapNoteCt++; if (changesMade.OverlapNoteCt > _MaxOverlapNoteCt && _CurrentReadMode == ENoteReadMode.ZeroBased) { - _LogWarning("Found more than " + _MaxOverlapNoteCt + " overlapping notes. Using standard mode."); + CBase.Log.LogSongWarning("[{SongFileName}] Found more than {MaxOverlapNoteCt} overlapping notes. Using standard mode.", _Song.FileName, _MaxOverlapNoteCt); _CurrentReadMode = ENoteReadMode.OneBased; sr.Dispose(); return ReadNotes(true); } } - _LogWarning("Ignored note for player " + (curPlayer + 1) + " because it overlaps with other note"); + CBase.Log.LogSongWarning("[{SongFileName}] Ignored note for player {CurrentPlayerNumber} because it overlaps with other note (in line {LineNr})", _Song.FileName, (curPlayer + 1), _LineNr); } } } @@ -671,13 +650,13 @@ namespace VocaluxeLib.Songs string[] lineBreakData = line.Split(splitChars); if (lineBreakData.Length < 1) { - _LogError("Invalid line break found (No beat)"); + CBase.Log.LogSongError("[{SongFileName}] Invalid line break found (No beat) (in line {LineNr}): {LineBreakData}", _Song.FileName, _LineNr, lineBreakData); sr.Dispose(); return false; } if (!int.TryParse(lineBreakData[0], out beat)) { - _LogError("Invalid line break found (Non-numeric value)"); + CBase.Log.LogSongError("[{SongFileName}] Invalid line break found (Non-numeric value) (in line {LineNr}): {LineBreakData}", _Song.FileName, _LineNr, lineBreakData); sr.Dispose(); return false; } @@ -687,7 +666,7 @@ namespace VocaluxeLib.Songs beat += currentBeat; if (lineBreakData.Length < 2 || !int.TryParse(lineBreakData[1], out length)) { - _LogWarning("Missing line break length"); + CBase.Log.LogSongWarning("[{SongFileName}] Missing line break length (in line {LineNr}):{LineBreakData}", _Song.FileName, _LineNr, lineBreakData); changesMade.NoLengthBreakCt++; currentBeat = beat; } @@ -697,7 +676,8 @@ namespace VocaluxeLib.Songs if (lastNote != null && beat <= lastNote.EndBeat) { - _LogWarning("Line break is before previous note end. Adjusted."); + CBase.Log.LogSongWarning("[{SongFileName}] Line break is before previous note end. Adjusted. (in line {LineNr})", _Song.FileName, _LineNr); + changesMade.AjustedBreakCt++; if (_Song.Relative) currentBeat += lastNote.EndBeat - beat + 1; @@ -706,7 +686,7 @@ namespace VocaluxeLib.Songs if (beat < 1) { - _LogWarning("Ignored line break because position is < 1"); + CBase.Log.LogSongWarning("[{SongFileName}] Ignored line break because position is < 1 (in line {LineNr})", _Song.FileName, _LineNr); changesMade.InvalidPosBreakCt++; } else @@ -714,12 +694,12 @@ namespace VocaluxeLib.Songs foreach (int curPlayer in player.GetSetBits()) { if (!_NewSentence(curPlayer, beat)) - _LogWarning("Ignored line break for player " + (curPlayer + 1) + " (Overlapping or duplicate)"); + CBase.Log.LogSongWarning("[{SongFileName}] Ignored line break for player {CurPlayerNr} (Overlapping or duplicate) (in line {LineNr})", _Song.FileName, (curPlayer + 1) , _LineNr); } } break; default: - _LogError("Unexpected or missing character (" + tag + ")"); + CBase.Log.LogSongError("[{SongFileName}] Unexpected or missing character ({Tag})", _Song.FileName, tag); return false; } } @@ -729,13 +709,13 @@ namespace VocaluxeLib.Songs CVoice voice = _Song.Notes.GetVoice(i); int emptyLines = voice.RemoveEmptyLines(); if (emptyLines > 0) - _LogWarning("Removed " + emptyLines + " empty lines from P" + ". This often indicates a problem with the line breaks in the file", false); + CBase.Log.LogSongWarning("[{SongFileName}] Removed {NumEmptyLines} empty lines from P .This often indicates a problem with the line breaks in the file", _Song.FileName, emptyLines); voice.UpdateTimings(); } } catch (Exception e) { - _LogError("An unhandled exception occured (" + e.Message + ")"); + CBase.Log.LogSongError(e, "[{SongFileName}] An unhandled exception occured: {ExceptionMessage}", _Song.FileName, e.Message); if (sr != null) sr.Dispose(); return false; @@ -752,7 +732,7 @@ namespace VocaluxeLib.Songs } catch (Exception e) { - _LogError("An unhandled exception occured (" + e.Message + ")", false); + CBase.Log.LogSongError(e, "[{SongFileName}] An unhandled exception occured: {ExceptionMessage}", _Song.FileName, e.Message); return false; } diff --git a/VocaluxeTests/PartyModeTests.cs b/VocaluxeTests/PartyModeTests.cs index c0363123..4108e8df 100644 --- a/VocaluxeTests/PartyModeTests.cs +++ b/VocaluxeTests/PartyModeTests.cs @@ -46,7 +46,7 @@ namespace VocaluxeTests public void TestRoundGeneration() { CBase.Game = new CBGame(); - CLog.Init(); + CLog.Init(CSettings.FolderNameLogs); CBase.Log = new CBlog(); for (int i = 0; i < 10; i++) { From c907ec0f44ce62ce11a8b3da0f4a56bf9a499026 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Tue, 16 Jan 2018 11:57:15 +0100 Subject: [PATCH 02/28] Refactor logging --- Vocaluxe/Base/CConfig.cs | 3 +- Vocaluxe/Base/CCover.cs | 5 +- Vocaluxe/Base/CDataBase.cs | 7 +- Vocaluxe/Base/CDraw.cs | 9 +- Vocaluxe/Base/CGraphics.cs | 7 +- Vocaluxe/Base/CLanguage.cs | 3 +- Vocaluxe/Base/CLog.cs | 277 ----- Vocaluxe/Base/CMain.cs | 45 +- Vocaluxe/Base/CParty.cs | 19 +- Vocaluxe/Base/CPlaylists.cs | 5 +- Vocaluxe/Base/CProfiles.cs | 3 +- Vocaluxe/Base/CSettings.cs | 5 +- Vocaluxe/Base/CSongs.cs | 11 +- Vocaluxe/Base/Fonts/CFontStyle.cs | 3 +- Vocaluxe/Base/Fonts/CFonts.cs | 13 +- Vocaluxe/Base/ThemeSystem/CBaseSkin.cs | 3 +- Vocaluxe/Base/ThemeSystem/CBaseTheme.cs | 3 +- Vocaluxe/Base/ThemeSystem/CPartySkin.cs | 9 +- Vocaluxe/Base/ThemeSystem/CPartyTheme.cs | 3 +- Vocaluxe/Base/ThemeSystem/CSkin.cs | 9 +- Vocaluxe/Base/ThemeSystem/CTheme.cs | 3 +- Vocaluxe/Base/ThemeSystem/CThemes.cs | 17 +- Vocaluxe/CProgrammHelper.cs | 12 +- Vocaluxe/CSplashScreen.cs | 9 +- Vocaluxe/Lib/Database/CCoverDB.cs | 5 +- Vocaluxe/Lib/Database/CHighscoreDB.cs | 19 +- Vocaluxe/Lib/Database/CResourceDB.cs | 9 +- Vocaluxe/Lib/Draw/CDirect3D.cs | 60 +- Vocaluxe/Lib/Input/CHIDAPI.cs | 13 +- Vocaluxe/Lib/Input/WiiMote/WiiMoteLib.cs | 13 +- Vocaluxe/Lib/Playlist/CPlaylistFile.cs | 5 +- Vocaluxe/Lib/Sound/CPortAudioHandle.cs | 17 +- .../Lib/Sound/Playback/CAudioStreamBase.cs | 3 +- .../Playback/Decoder/CAudioDecoderFFmpeg.cs | 9 +- .../GstreamerSharp/CGstreamerSharpAudio.cs | 3 +- .../CGstreamerSharpAudioStream.cs | 15 +- .../Sound/Playback/OpenAL/COpenAlStream.cs | 7 +- .../Playback/PortAudio/CPortAudioStream.cs | 9 +- .../Record/PitchTracker/CPitchTrackerTest.cs | 11 +- .../Record/PortAudio/CPortAudioRecord.cs | 5 +- .../Lib/Video/Acinerella/CDecoderThread.cs | 19 +- Vocaluxe/Program.cs | 77 +- Vocaluxe/Screens/CScreenCredits.cs | 3 +- Vocaluxe/Screens/CScreenLoad.cs | 5 +- Vocaluxe/Screens/CScreenOptionsVideo.cs | 3 +- Vocaluxe/Screens/CScreenSing.cs | 3 +- Vocaluxe/Vocaluxe.csproj | 5 +- Vocaluxe/packages.config | 4 +- VocaluxeLib/CBasic.cs | 4 +- VocaluxeLib/CHelper.cs | 5 +- VocaluxeLib/IBasic.cs | 10 - VocaluxeLib/Log/CLog.cs | 79 ++ VocaluxeLib/Log/CLogFunctions.cs | 959 ++++++++++++++++++ VocaluxeLib/Log/CLogFunctions.tt | 139 +++ .../Enricher/CTimeStampFromStartEnricher.cs | 28 + VocaluxeLib/Log/Rolling/LogRoller.cs | 84 ++ VocaluxeLib/Log/Serilog/SilentLogger.cs | 355 +++++++ VocaluxeLib/Menu/CMenu.cs | 15 +- VocaluxeLib/Menu/COrderedDictionaryLite.cs | 3 +- VocaluxeLib/Profile/CProfile.cs | 5 +- VocaluxeLib/Songs/CSong.cs | 3 +- VocaluxeLib/Songs/CSongLoader.cs | 103 +- VocaluxeLib/Songs/CSongWriter.cs | 5 +- VocaluxeLib/VocaluxeLib.csproj | 46 + VocaluxeLib/Xml/CXmlDeserializer.cs | 3 +- VocaluxeLib/packages.config | 9 + VocaluxeTests/PartyModeTests.cs | 6 +- 67 files changed, 2039 insertions(+), 627 deletions(-) delete mode 100644 Vocaluxe/Base/CLog.cs create mode 100644 VocaluxeLib/Log/CLog.cs create mode 100644 VocaluxeLib/Log/CLogFunctions.cs create mode 100644 VocaluxeLib/Log/CLogFunctions.tt create mode 100644 VocaluxeLib/Log/Enricher/CTimeStampFromStartEnricher.cs create mode 100644 VocaluxeLib/Log/Rolling/LogRoller.cs create mode 100644 VocaluxeLib/Log/Serilog/SilentLogger.cs create mode 100644 VocaluxeLib/packages.config diff --git a/Vocaluxe/Base/CConfig.cs b/Vocaluxe/Base/CConfig.cs index a552974b..5d4fb05a 100644 --- a/Vocaluxe/Base/CConfig.cs +++ b/Vocaluxe/Base/CConfig.cs @@ -27,6 +27,7 @@ using System.Xml.Serialization; using Vocaluxe.Lib.Sound.Record; using Vocaluxe.Lib.Webcam; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Profile; using VocaluxeLib.Xml; @@ -376,7 +377,7 @@ namespace Vocaluxe.Base { Config = xml.Deserialize(_FileConfig); if (_XmlErrorsOccured) - CLog.LogError("There were some warnings or errors loading the config file. Some values might have been reset to their defaults."); + CLog.Error("There were some warnings or errors loading the config file. Some values might have been reset to their defaults."); } else Config = xml.DeserializeString(""); diff --git a/Vocaluxe/Base/CCover.cs b/Vocaluxe/Base/CCover.cs index 0a155740..6dea5e2f 100644 --- a/Vocaluxe/Base/CCover.cs +++ b/Vocaluxe/Base/CCover.cs @@ -25,6 +25,7 @@ using System.Threading.Tasks; using System.Linq; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Log; using VocaluxeLib.Songs; using VocaluxeLib.Xml; @@ -185,7 +186,7 @@ namespace Vocaluxe.Base } catch (CXmlException e) { - CLog.LogError("Error loading cover theme " + file + ": " + e); + CLog.Error("Error loading cover theme " + file + ": " + e); continue; } @@ -218,7 +219,7 @@ namespace Vocaluxe.Base NoCover = _Covers[_NoCoverNameAlt]; else { - CBase.Log.LogError("Covertheme \"" + coverTheme.Info.Name + "\" does not include a cover file named \"" + _NoCoverName + "\" and cannot be used!", true, true); + CLog.Fatal("Covertheme \"{ThemeName}\" does not include a cover file named \"{MissingFileName}\" and cannot be used!", CLog.Params(coverTheme.Info.Name, _NoCoverName)); _UnloadCovers(); // Remove current theme and recursively try the other themes _CoverThemes.Remove(coverTheme); diff --git a/Vocaluxe/Base/CDataBase.cs b/Vocaluxe/Base/CDataBase.cs index 8887982f..d159679f 100644 --- a/Vocaluxe/Base/CDataBase.cs +++ b/Vocaluxe/Base/CDataBase.cs @@ -21,6 +21,7 @@ using System.IO; using Vocaluxe.Lib.Database; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Log; namespace Vocaluxe.Base { @@ -38,17 +39,17 @@ namespace Vocaluxe.Base if (!_HighscoreDB.Init()) { - CLog.LogError("Error initializing Highscore-DB", true, true); + CLog.Fatal("Error initializing Highscore-DB"); return false; } if (!_CoverDB.Init()) { - CLog.LogError("Error initializing Cover-DB", true, true); + CLog.Fatal("Error initializing Cover-DB"); return false; } if (!_ResourceDB.Init()) { - CLog.LogError("Error initializing Credits-DB", true, true); + CLog.Fatal("Error initializing Credits-DB"); return false; } return true; diff --git a/Vocaluxe/Base/CDraw.cs b/Vocaluxe/Base/CDraw.cs index 57f61ff2..08969aac 100644 --- a/Vocaluxe/Base/CDraw.cs +++ b/Vocaluxe/Base/CDraw.cs @@ -20,6 +20,7 @@ using System.Drawing; using Vocaluxe.Lib.Draw; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Log; namespace Vocaluxe.Base { @@ -48,8 +49,7 @@ namespace Vocaluxe.Base } catch (Exception e) { - CLog.LogError(e.Message + " - Error in initializing of OpenGL. Please check whether" + - " your graphic card drivers are up to date.", true, true); + CLog.Fatal(e, "Error in initializing of OpenGL. Please check whether your graphic card drivers are up to date."); return false; } break; @@ -62,9 +62,8 @@ namespace Vocaluxe.Base } catch (Exception e) { - CLog.LogError(e.Message + " - Error in initializing of Direct3D. Please check if " + - "your DirectX redistributables and graphic card drivers are up to date. You can " + - "download the DirectX runtimes at http://www.microsoft.com/download/en/details.aspx?id=8109", true, true); + CLog.Fatal(e, "Error in initializing of Direct3D. Please check if your DirectX redistributables and graphic card drivers are up to date. You can " + + "download the DirectX runtimes at http://www.microsoft.com/download/en/details.aspx?id=8109"); return false; } break; diff --git a/Vocaluxe/Base/CGraphics.cs b/Vocaluxe/Base/CGraphics.cs index 0c63169e..495090a8 100644 --- a/Vocaluxe/Base/CGraphics.cs +++ b/Vocaluxe/Base/CGraphics.cs @@ -26,6 +26,7 @@ using Vocaluxe.Base.Fonts; using Vocaluxe.Base.ThemeSystem; using Vocaluxe.Screens; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Menu; namespace Vocaluxe.Base @@ -63,7 +64,7 @@ namespace Vocaluxe.Base public static void Init() { // Add Screens, must be the same order as in EScreens! - CLog.LogInformation("Build Screen List"); + CLog.Information("Build Screen List"); using (Operation.At(LogEventLevel.Information).Time("Build Screen List")) { _Screens.Add(new CScreenTest()); @@ -100,7 +101,7 @@ namespace Vocaluxe.Base GlobalAlpha = 1f; ZOffset = 0f; - CLog.LogInformation("Load Theme"); + CLog.Information("Load Theme"); using (Operation.At(LogEventLevel.Information).Time("Load Theme")) { LoadTheme(); @@ -121,7 +122,7 @@ namespace Vocaluxe.Base for (int i = 0; i < _Screens.Count; i++) { - CLog.LogInformation("Load Theme " + Enum.GetNames(typeof(EScreen))[i]); + CLog.Information("Load Theme " + Enum.GetNames(typeof(EScreen))[i]); using (Operation.At(LogEventLevel.Information).Time("Load Theme " + Enum.GetNames(typeof(EScreen))[i])) { _Screens[i].Init(); diff --git a/Vocaluxe/Base/CLanguage.cs b/Vocaluxe/Base/CLanguage.cs index d6b1cbe4..a7f6f1ec 100644 --- a/Vocaluxe/Base/CLanguage.cs +++ b/Vocaluxe/Base/CLanguage.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Xml; namespace Vocaluxe.Base @@ -170,7 +171,7 @@ namespace Vocaluxe.Base } catch (Exception e) { - CLog.LogError("Error reading language file " + filePath + ": " + e.Message); + CLog.Error("Error reading language file " + filePath + ": " + e.Message); texts = null; return false; } diff --git a/Vocaluxe/Base/CLog.cs b/Vocaluxe/Base/CLog.cs deleted file mode 100644 index 7b8af970..00000000 --- a/Vocaluxe/Base/CLog.cs +++ /dev/null @@ -1,277 +0,0 @@ -#region license -// This file is part of Vocaluxe. -// -// Vocaluxe is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Vocaluxe is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Vocaluxe. If not, see . -#endregion - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.RegularExpressions; -using System.Windows.Forms; -using Serilog; -using Serilog.Configuration; -using Serilog.Core; -using Serilog.Context; -using Serilog.Events; -using Serilog.Formatting.Compact; - -namespace Vocaluxe.Base -{ - static class CLog - { - private static bool _Initialized; - - private static readonly StringBuilder _MainLogStringBuilder = new StringBuilder(); - private static readonly StringWriter _MainLogStringWriter = new StringWriter(_MainLogStringBuilder); - - private static Logger _MainLog; - private static Logger _SongInfoLog; - private static string _LogFolder; - - private const string _MainLogTemplate = "[{TimeStampFromStart}] [{Level}] {Message}{NewLine}{Properties}{NewLine}{Exception}"; - private const string _SongInfoLogTemplate = "{Message}{NewLine}Additional info:{Properties}{NewLine}{Exception}"; - - public static void Init(string logFolder) - { - _LogFolder = logFolder; - - _RollLogs(Path.Combine(_LogFolder, CSettings.FileNameMainLog), 2); - _RollLogs(Path.Combine(_LogFolder, CSettings.FileNameSongInfoLog), 2); - - _MainLog = new LoggerConfiguration() - .Enrich.WithThreadId() - .Enrich.FromLogContext() - .Enrich.WithTimeStampFromStart() - .WriteTo.TextWriter(_MainLogStringWriter, outputTemplate: _MainLogTemplate) - // Json can be activated by adding "new CompactJsonFormatter()" as first argument - .WriteTo.File(Path.Combine(_LogFolder, CSettings.FileNameMainLog), flushToDiskInterval: TimeSpan.FromSeconds(30), outputTemplate: _MainLogTemplate) -#if DEBUG - .WriteTo.Console(outputTemplate: _MainLogTemplate) -#endif - .CreateLogger(); - - Log.Logger = _MainLog; - - _SongInfoLog = new LoggerConfiguration() - .WriteTo.File(Path.Combine(_LogFolder, CSettings.FileNameSongInfoLog), flushToDiskInterval: TimeSpan.FromSeconds(60), outputTemplate: _SongInfoLogTemplate) -#if DEBUG - .WriteTo.Console(outputTemplate: "[SongInfo] " + _SongInfoLogTemplate) -#endif - .CreateLogger(); - - _Initialized = true; - } - - - public static void Close() - { - if (_Initialized) - { - - _MainLog.Dispose(); - _MainLog = null; - _SongInfoLog.Dispose(); - _SongInfoLog = null; - _Initialized = false; - } - } - - public static string MainLogEntries - { - get { - _MainLogStringWriter.Flush(); - return _MainLogStringBuilder.ToString(); - } - } - - public static void LogError(Exception exception, string errorText, bool show = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", - [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) - { - using (LogContext.PushProperty("CallingContext", new { callerMethodeName = callerMethodeName, callerFilePath = callerFilePath, callerLineNumer = callerLineNumer })) - { - if (exit) - { - _MainLog.Fatal(exception, errorText, propertyValues); - } - else - { - _MainLog.Error(exception, errorText, propertyValues); - } - } - - if (show) - { - //Todo: Logsend assistent - MessageBox.Show(errorText, CSettings.ProgramName, MessageBoxButtons.OK, MessageBoxIcon.Error); - } - - - if (exit) - { - // Flush the logging piplines before exiting - _MainLog.Dispose(); - _SongInfoLog.Dispose(); - // Close the Programm - Environment.Exit(Environment.ExitCode); - } - } - - public static void LogError(string errorText, bool show = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", - [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) - { - // ReSharper disable ExplicitCallerInfoArgument - LogError(null, errorText, show, exit, callerMethodeName, callerFilePath, callerLineNumer, propertyValues); - } - - public static void LogWarning(string text, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) - { - using (LogContext.PushProperty("CallingContext", new { callerMethodeName = callerMethodeName, callerFilePath = callerFilePath, callerLineNumer = callerLineNumer })) - { - _MainLog.Warning(text, propertyValues); - } - } - - public static void LogInformation(string text, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) - { - using (LogContext.PushProperty("CallingContext", new { callerMethodeName = callerMethodeName, callerFilePath = callerFilePath, callerLineNumer = callerLineNumer })) - { - _MainLog.Information(text, propertyValues); - } - } - - public static void LogDebug(string text, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) - { - using (LogContext.PushProperty("CallingContext", new { callerMethodeName = callerMethodeName, callerFilePath = callerFilePath, callerLineNumer = callerLineNumer })) - { - _MainLog.Debug(text, propertyValues); - } - } - - public static void LogSongError(Exception exception, string text, params object[] propertyValues) - { - _SongInfoLog.Error(exception, text, propertyValues); - } - public static void LogSongError(string text, params object[] propertyValues) - { - _SongInfoLog.Error(text, propertyValues); - } - - public static void LogSongWaring(string text, params object[] propertyValues) - { - _SongInfoLog.Warning(text, propertyValues); - } - - #region Log rolling - - private static void _RollLogs(string mainLogFile, int numLogsToKeep) - { - _DeleteOldLogs(mainLogFile, numLogsToKeep); - - string currentSourceFile = _GenerateRollLogFileName(mainLogFile, numLogsToKeep); - - - for (var i = numLogsToKeep; i > 1; i--) - { - string currentTargetFile = currentSourceFile; - currentSourceFile = _GenerateRollLogFileName(mainLogFile, i - 1); - - if (File.Exists(currentSourceFile)) - { - try - { - File.Move(currentSourceFile, currentTargetFile); - } - catch (Exception) - { - // Cant log anything here as the log isn't initialized yet - } - } - } - - if (File.Exists(mainLogFile)) - { - try { - if(numLogsToKeep > 0) - File.Move(mainLogFile, currentSourceFile); - else - File.Delete(mainLogFile); - } - catch (Exception) - { - // Cant log anything here as the log isn't initialized yet - } - } - } - - private static void _DeleteOldLogs(string mainLogFile, int numLogsToKeep) - { - Regex r = new Regex($"{Path.GetDirectoryName(mainLogFile)}\\\\{Path.GetFileNameWithoutExtension(mainLogFile)}_([0-9]+){Path.GetExtension(mainLogFile)}", RegexOptions.IgnoreCase); - IEnumerable logFiles = Directory.EnumerateFiles($"{Path.GetDirectoryName(mainLogFile)}", $"{Path.GetFileNameWithoutExtension(mainLogFile)}*{Path.GetExtension(mainLogFile)}"); - foreach (string file in logFiles) - { - Match m = r.Match(file); - if (m.Success) - { - int logNum = -1; - Int32.TryParse(m.Groups[1].Value, out logNum); - - if (logNum < 0 || logNum > numLogsToKeep - 1) - { - try - { - File.Delete(file); - } - catch (Exception) - { - // Cant log anything here as the log isn't initialized yet - } - - } - } - } - } - - private static string _GenerateRollLogFileName(string mainLogFile, int number) - { - return $"{Path.GetDirectoryName(mainLogFile)}\\{Path.GetFileNameWithoutExtension(mainLogFile)}_{number}{Path.GetExtension(mainLogFile)}"; - } - - #endregion - - #region CTimeStampFromStartEnricher - - // ReSharper disable once InconsistentNaming - private static LoggerConfiguration WithTimeStampFromStart(this LoggerEnrichmentConfiguration enrich) - { - return enrich.With(new CTimeStampFromStartEnricher()); - } - - private class CTimeStampFromStartEnricher : ILogEventEnricher - { - private readonly DateTimeOffset _Start = DateTimeOffset.Now; - - public void Enrich(LogEvent logEvent, ILogEventPropertyFactory lepf) - { - logEvent.AddPropertyIfAbsent( - lepf.CreateProperty("TimeStampFromStart", logEvent.Timestamp - _Start)); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/Vocaluxe/Base/CMain.cs b/Vocaluxe/Base/CMain.cs index b4256fa0..0689ccba 100644 --- a/Vocaluxe/Base/CMain.cs +++ b/Vocaluxe/Base/CMain.cs @@ -25,6 +25,7 @@ using Vocaluxe.Base.ThemeSystem; using VocaluxeLib; using VocaluxeLib.Game; using VocaluxeLib.Draw; +using VocaluxeLib.Log; using VocaluxeLib.Menu; using VocaluxeLib.Songs; using VocaluxeLib.Profile; @@ -39,7 +40,6 @@ namespace Vocaluxe.Base private static readonly IBackgroundMusic _BackgroundMusic = new CBbackgroundMusic(); private static readonly IDrawing _Draw = new CBdraw(); private static readonly IGraphics _Graphics = new CBGraphics(); - private static readonly ILog _Log = new CBlog(); private static readonly IFonts _Fonts = new CBfonts(); private static readonly ILanguage _Language = new CBlanguage(); private static readonly IGame _Game = new CBGame(); @@ -55,7 +55,7 @@ namespace Vocaluxe.Base public static void Init() { - CBase.Assign(_Config, _Settings, _Themes, _Log, _BackgroundMusic, _Draw, _Graphics, _Fonts, _Language, + CBase.Assign(_Config, _Settings, _Themes, _BackgroundMusic, _Draw, _Graphics, _Fonts, _Language, _Game, _Profiles, _Record, _Songs, _Video, _Sound, _Cover, _DataBase, _Controller, _Playlist); } } @@ -508,41 +508,6 @@ namespace Vocaluxe.Base } } - // ReSharper disable ExplicitCallerInfoArgument - class CBlog : ILog - { - public void LogError(Exception exeption, string errorText, bool showMsg = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) - { - CLog.LogError(exeption, errorText, showMsg, exit, callerMethodeName, callerFilePath, callerLineNumer, propertyValues); - } - - public void LogError(string errorText, bool showMsg = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) - { - CLog.LogError(null, errorText, showMsg, exit, callerMethodeName, callerFilePath, callerLineNumer, propertyValues); - } - - public void LogDebug(string text, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues) - { - - CLog.LogDebug(text, callerMethodeName, callerFilePath, callerLineNumer, propertyValues); - } - - public void LogSongWarning(string text, params object[] propertyValues) - { - CLog.LogSongWaring(text, propertyValues); - } - - public void LogSongError(Exception exception, string text, params object[] propertyValues) - { - CLog.LogSongError(exception, text, propertyValues); - } - - public void LogSongError(string text, params object[] propertyValues) - { - CLog.LogSongError(text, propertyValues); - } - } - class CBfonts : IFonts { public RectangleF GetTextBounds(CText text) @@ -866,7 +831,7 @@ namespace Vocaluxe.Base } catch (NotSupportedException e) { - CLog.LogError($"Clould not close the background video: {e.Message}"); + CLog.Error($"Clould not close the background video: {e.Message}"); } } @@ -883,7 +848,7 @@ namespace Vocaluxe.Base } catch (NotSupportedException e) { - CLog.LogError($"Clould not pause the background video: {e.Message}"); + CLog.Error($"Clould not pause the background video: {e.Message}"); } } @@ -895,7 +860,7 @@ namespace Vocaluxe.Base } catch (NotSupportedException e) { - CLog.LogError($"Clould not resume the background video: {e.Message}"); + CLog.Error($"Clould not resume the background video: {e.Message}"); } } } diff --git a/Vocaluxe/Base/CParty.cs b/Vocaluxe/Base/CParty.cs index f8126737..6687bffa 100644 --- a/Vocaluxe/Base/CParty.cs +++ b/Vocaluxe/Base/CParty.cs @@ -23,6 +23,7 @@ using System.IO; using System.Reflection; using Vocaluxe.Base.ThemeSystem; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Menu; using VocaluxeLib.PartyModes; using VocaluxeLib.Xml; @@ -74,7 +75,7 @@ namespace Vocaluxe.Base _CurrentPartyMode = pm; else { - CLog.LogError("Could not init PartyMode \"" + _PartyModes[value].Info.Name + "\"! Removing...", true); + CLog.Error("Could not init PartyMode \"" + _PartyModes[value].Info.Name + "\"! Removing...", true); _PartyModes.Remove(value); } } @@ -212,7 +213,7 @@ namespace Vocaluxe.Base catch (Exception e) { pm = new SPartyMode(); - CLog.LogError("Error loading PartyMode file " + filePath + ": " + e.Message); + CLog.Error("Error loading PartyMode file " + filePath + ": " + e.Message); return false; } @@ -230,7 +231,7 @@ namespace Vocaluxe.Base BindingFlags.Public | BindingFlags.Instance, null, new object[] {_NextID++}, null, null); if (instance == null) { - CLog.LogError("Error creating Instance of PartyMode file: " + filePath); + CLog.Error("Error creating Instance of PartyMode file: " + filePath); return false; } @@ -240,13 +241,13 @@ namespace Vocaluxe.Base } catch (Exception e) { - CLog.LogError("Error casting PartyMode file: " + filePath + "; " + e.Message); + CLog.Error("Error casting PartyMode file: " + filePath + "; " + e.Message); return false; } if (!CLanguage.LoadPartyLanguageFiles(pm.PartyMode.ID, Path.Combine(pathToPm, CSettings.FolderNamePartyModeLanguages))) { - CLog.LogError("Error loading language files for PartyMode: " + filePath); + CLog.Error("Error loading language files for PartyMode: " + filePath); return false; } @@ -299,14 +300,14 @@ namespace Vocaluxe.Base } catch (Exception e) { - CLog.LogError("Error Compiling Source (" + CHelper.ListStrings(files) + "): " + e.Message); + CLog.Error("Error Compiling Source (" + CHelper.ListStrings(files) + "): " + e.Message); return null; } if (compileResult.Errors.Count > 0) { foreach (CompilerError e in compileResult.Errors) - CLog.LogError("Error Compiling Source (" + CHelper.ListStrings(files) + "): " + e.ErrorText + " in '" + e.FileName + "' (" + e.Line + ")"); + CLog.Error("Error Compiling Source (" + CHelper.ListStrings(files) + "): " + e.ErrorText + " in '" + e.FileName + "' (" + e.Line + ")"); return null; } return compileResult.CompiledAssembly; @@ -321,7 +322,7 @@ namespace Vocaluxe.Base object instance = assembly.CreateInstance(typeof(IPartyMode).Namespace + "." + partyModeName + "." + screenName); if (instance == null) { - CLog.LogError("Error creating Instance of PartyScreen: " + screenName); + CLog.Error("Error creating Instance of PartyScreen: " + screenName); return null; } @@ -332,7 +333,7 @@ namespace Vocaluxe.Base } catch (Exception e) { - CLog.LogError("Error casting PartyScreen: " + screenName + "; " + e.Message); + CLog.Error("Error casting PartyScreen: " + screenName + "; " + e.Message); return null; } return screen; diff --git a/Vocaluxe/Base/CPlaylists.cs b/Vocaluxe/Base/CPlaylists.cs index 881d3ccb..8c47b962 100644 --- a/Vocaluxe/Base/CPlaylists.cs +++ b/Vocaluxe/Base/CPlaylists.cs @@ -23,6 +23,7 @@ using System.Linq; using System.Text; using Vocaluxe.Lib.Playlist; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Menu; using VocaluxeLib.Songs; @@ -120,7 +121,7 @@ namespace Vocaluxe.Base } catch (Exception) { - CLog.LogError("Can't delete Playlist File " + _Playlists[playlistID].File + ".xml"); + CLog.Error("Can't delete Playlist File " + _Playlists[playlistID].File + ".xml"); } } _Playlists.Remove(pl); @@ -256,7 +257,7 @@ namespace Vocaluxe.Base if (plSong != null) pl.AddSong(plSong.ID); else - CLog.LogError("Can't find song '" + title + "' from '" + artist + "' in playlist file: " + file); + CLog.Error("Can't find song '" + title + "' from '" + artist + "' in playlist file: " + file); } } } diff --git a/Vocaluxe/Base/CProfiles.cs b/Vocaluxe/Base/CProfiles.cs index 9e454ffe..12440e5f 100644 --- a/Vocaluxe/Base/CProfiles.cs +++ b/Vocaluxe/Base/CProfiles.cs @@ -22,6 +22,7 @@ using System.IO; using System.Text.RegularExpressions; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Log; using VocaluxeLib.Profile; namespace Vocaluxe.Base @@ -625,7 +626,7 @@ namespace Vocaluxe.Base } catch (Exception) { - CLog.LogError("Can't delete Profile File " + _Profiles[profileID].FilePath); + CLog.Error("Can't delete Profile File " + _Profiles[profileID].FilePath); } _ProfilesChanged = true; } diff --git a/Vocaluxe/Base/CSettings.cs b/Vocaluxe/Base/CSettings.cs index 5a6a8998..abe324ed 100644 --- a/Vocaluxe/Base/CSettings.cs +++ b/Vocaluxe/Base/CSettings.cs @@ -23,6 +23,7 @@ using System.Reflection; using System.Text; using System.Windows.Forms; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Xml; namespace Vocaluxe.Base @@ -113,7 +114,7 @@ namespace Vocaluxe.Base public const string FileNameCreditsRessourcesDB = "CreditsRessourcesDB.sqlite"; public const string FileNameMainLog = "Vocaluxe.log"; - public const string FileNameSongInfoLog = "SongInformation.log"; + public const string FileNameSongLog = "Song.log"; public const string FileNameRequiredSkinElements = "RequiredSkinElements.xml"; @@ -239,7 +240,7 @@ namespace Vocaluxe.Base } catch (Exception e) { - CLog.LogError("Cannot create directory \"" + path + "\": " + e.Message); + CLog.Error("Cannot create directory \"" + path + "\": " + e.Message); } } } diff --git a/Vocaluxe/Base/CSongs.cs b/Vocaluxe/Base/CSongs.cs index 5429b65b..e2a6e3f8 100644 --- a/Vocaluxe/Base/CSongs.cs +++ b/Vocaluxe/Base/CSongs.cs @@ -25,6 +25,7 @@ using System.Threading.Tasks; using Serilog.Events; using SerilogTimings; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Songs; namespace Vocaluxe.Base @@ -335,7 +336,7 @@ namespace Vocaluxe.Base public static void LoadSongs() { - CLog.LogInformation("Load Songs"); + CLog.Information("Load Songs"); using (Operation.At(LogEventLevel.Information).Time("Load Songs")) { SongsLoaded = false; @@ -343,7 +344,7 @@ namespace Vocaluxe.Base var files = new List(); - CLog.LogInformation("List Songs"); + CLog.Information("List Songs"); using (Operation.At(LogEventLevel.Information).Time("List Songs")) { @@ -358,7 +359,7 @@ namespace Vocaluxe.Base } } - CLog.LogInformation("Read TXTs"); + CLog.Information("Read TXTs"); using (Operation.At(LogEventLevel.Information).Time("Read TXTs")) { foreach (string file in files) @@ -372,7 +373,7 @@ namespace Vocaluxe.Base } } - CLog.LogInformation("Sort Songs"); + CLog.Information("Sort Songs"); using (Operation.At(LogEventLevel.Information).Time("Sorted Songs")) { Sorter.SongSorting = CConfig.Config.Game.SongSorting; @@ -409,7 +410,7 @@ namespace Vocaluxe.Base private static void _LoadCovers() { - CLog.LogInformation("Load Covers"); + CLog.Information("Load Covers"); using (Operation.At(LogEventLevel.Information).Time("Loaded Covers")) { int songCount = _Songs.Count; diff --git a/Vocaluxe/Base/Fonts/CFontStyle.cs b/Vocaluxe/Base/Fonts/CFontStyle.cs index 0958632b..448f4b13 100644 --- a/Vocaluxe/Base/Fonts/CFontStyle.cs +++ b/Vocaluxe/Base/Fonts/CFontStyle.cs @@ -21,6 +21,7 @@ using System.Drawing; using System.Drawing.Text; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Log; namespace Vocaluxe.Base.Fonts { @@ -93,7 +94,7 @@ namespace Vocaluxe.Base.Fonts } catch (Exception e) { - CLog.LogError("Error opening font file " + _FilePath + ": " + e.Message); + CLog.Error("Error opening font file " + _FilePath + ": " + e.Message); } } return new Font(_Family, height, _GetSystemFontStyle(), GraphicsUnit.Pixel); diff --git a/Vocaluxe/Base/Fonts/CFonts.cs b/Vocaluxe/Base/Fonts/CFonts.cs index ae9d1af9..a91faec2 100644 --- a/Vocaluxe/Base/Fonts/CFonts.cs +++ b/Vocaluxe/Base/Fonts/CFonts.cs @@ -24,6 +24,7 @@ using System.Linq; using Serilog.Events; using SerilogTimings; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Menu; using VocaluxeLib.Xml; @@ -215,14 +216,14 @@ namespace Vocaluxe.Base.Fonts if (!_LoggedMissingFonts.Contains(fontName)) { _LoggedMissingFonts.Add(fontName); - CLog.LogError("Font \"" + fontName + "\" not found!"); + CLog.Error("Font \"" + fontName + "\" not found!"); } } else - CLog.LogError("Empty fontName requested"); + CLog.Error("Empty fontName requested"); if (_FontFamilies.Count == 0) - CLog.LogError("No fonts found!", true, true); + CLog.Fatal("No fonts found!"); return 0; } @@ -267,7 +268,7 @@ namespace Vocaluxe.Base.Fonts } catch (Exception e) { - CLog.LogError(e, "Error loading default Fonts"); + CLog.Error(e, "Error loading default Fonts"); return false; } @@ -285,12 +286,12 @@ namespace Vocaluxe.Base.Fonts { if (!_LoadFont(fontFamily, fontFolder, themeName, partyModeId)) { - CLog.LogError("Error loading fonts for Theme " + themeName + "(" + partyModeId + ")"); + CLog.Error("Error loading fonts for Theme " + themeName + "(" + partyModeId + ")"); return false; } } - CLog.LogInformation("Build Glyphs"); + CLog.Information("Build Glyphs"); using (Operation.At(LogEventLevel.Information).Time("Build Glyphs")) { _BuildGlyphs(); diff --git a/Vocaluxe/Base/ThemeSystem/CBaseSkin.cs b/Vocaluxe/Base/ThemeSystem/CBaseSkin.cs index 91d36113..273a7aa2 100644 --- a/Vocaluxe/Base/ThemeSystem/CBaseSkin.cs +++ b/Vocaluxe/Base/ThemeSystem/CBaseSkin.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using VocaluxeLib.Log; namespace Vocaluxe.Base.ThemeSystem { @@ -52,7 +53,7 @@ namespace Vocaluxe.Base.ThemeSystem msg += Environment.NewLine + "Videos: " + String.Join(", ", missingVideos); if (missingColors.Count > 0) msg += Environment.NewLine + "Colors: " + String.Join(", ", missingColors); - CLog.LogError(msg); + CLog.Error(msg); return false; } } diff --git a/Vocaluxe/Base/ThemeSystem/CBaseTheme.cs b/Vocaluxe/Base/ThemeSystem/CBaseTheme.cs index cc4c5f3c..f0cd3950 100644 --- a/Vocaluxe/Base/ThemeSystem/CBaseTheme.cs +++ b/Vocaluxe/Base/ThemeSystem/CBaseTheme.cs @@ -19,6 +19,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using Vocaluxe.Base.Fonts; +using VocaluxeLib.Log; namespace Vocaluxe.Base.ThemeSystem { @@ -64,7 +65,7 @@ namespace Vocaluxe.Base.ThemeSystem if (skin.Load()) break; skin.Unload(); - CLog.LogError("Failed to load skin " + skin + "! Removing...", true); + CLog.Error("Failed to load skin " + skin + "! Removing...", true); _Skins.Remove(skin.Name); skin = _Skins.Values.FirstOrDefault(); } diff --git a/Vocaluxe/Base/ThemeSystem/CPartySkin.cs b/Vocaluxe/Base/ThemeSystem/CPartySkin.cs index 16484bfc..b09c93bb 100644 --- a/Vocaluxe/Base/ThemeSystem/CPartySkin.cs +++ b/Vocaluxe/Base/ThemeSystem/CPartySkin.cs @@ -17,6 +17,7 @@ using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Log; namespace Vocaluxe.Base.ThemeSystem { @@ -35,7 +36,7 @@ namespace Vocaluxe.Base.ThemeSystem { if (!_Data.Colors.ContainsKey("Player" + i)) continue; - CLog.LogDebug("Party themes cannot contain player colors. They will be ignored!"); + CLog.Debug("Party themes cannot contain player colors. They will be ignored!"); break; } return true; @@ -44,21 +45,21 @@ namespace Vocaluxe.Base.ThemeSystem public override bool GetColor(string name, out SColorF color) { if (_Parent.Name == "Default" && !_Required.Colors.Contains(name)) - CLog.LogDebug("Non-Default color: " + name); + CLog.Debug("Non-Default color: " + name); return base.GetColor(name, out color) || _BaseSkin.GetColor(name, out color); } public override CVideoStream GetVideo(string name, bool loop) { if (_Parent.Name == "Default" && !_Required.Videos.Contains(name)) - CLog.LogDebug("Non-Default color: " + name); + CLog.Debug("Non-Default color: " + name); return base.GetVideo(name, loop) ?? _BaseSkin.GetVideo(name, loop); } public override CTextureRef GetTexture(string name) { if (_Parent.Name == "Default" && !_Required.Textures.Contains(name)) - CLog.LogDebug("Non-Default color: " + name); + CLog.Debug("Non-Default color: " + name); return base.GetTexture(name) ?? _BaseSkin.GetTexture(name); } } diff --git a/Vocaluxe/Base/ThemeSystem/CPartyTheme.cs b/Vocaluxe/Base/ThemeSystem/CPartyTheme.cs index f91a09d0..b6167fb6 100644 --- a/Vocaluxe/Base/ThemeSystem/CPartyTheme.cs +++ b/Vocaluxe/Base/ThemeSystem/CPartyTheme.cs @@ -19,6 +19,7 @@ using System; using System.IO; using System.Linq; using Vocaluxe.Base.Fonts; +using VocaluxeLib.Log; namespace Vocaluxe.Base.ThemeSystem { @@ -73,7 +74,7 @@ namespace Vocaluxe.Base.ThemeSystem if (skin.Load()) break; skin.Unload(); - CLog.LogError("Failed to load skin " + skin + "! Removing...", true); + CLog.Error("Failed to load skin " + skin + "! Removing...", true); _Skins.Remove(skin.Name); } if (skin == null) diff --git a/Vocaluxe/Base/ThemeSystem/CSkin.cs b/Vocaluxe/Base/ThemeSystem/CSkin.cs index 379ed66a..398d1d35 100644 --- a/Vocaluxe/Base/ThemeSystem/CSkin.cs +++ b/Vocaluxe/Base/ThemeSystem/CSkin.cs @@ -24,6 +24,7 @@ using VocaluxeLib; using VocaluxeLib.Draw; using VocaluxeLib.Xml; using System.Linq; +using VocaluxeLib.Log; namespace Vocaluxe.Base.ThemeSystem { @@ -99,7 +100,7 @@ namespace Vocaluxe.Base.ThemeSystem } catch (CXmlException e) { - CLog.LogError("Error reading required elements: " + e); + CLog.Error("Error reading required elements: " + e); return false; } for (int i = 1; i <= CSettings.MaxNumPlayer; i++) @@ -147,7 +148,7 @@ namespace Vocaluxe.Base.ThemeSystem } catch (Exception e) { - CLog.LogError(e, "Can't load skin file \"" + Path.Combine(_Folder, _FileName) + "\". Invalid file!"); + CLog.Error(e, "Can't load skin file \"" + Path.Combine(_Folder, _FileName) + "\". Invalid file!"); return false; } @@ -166,7 +167,7 @@ namespace Vocaluxe.Base.ThemeSystem CTextureRef texture = CDraw.AddTexture(Path.Combine(_Folder, kvp.Value)); if (texture == null) { - CLog.LogError("Error on loading texture \"" + kvp.Key + "\": " + kvp.Value, true); + CLog.Error("Error on loading texture \"" + kvp.Key + "\": " + kvp.Value, true); return false; } _Textures.Add(kvp.Key, texture); @@ -178,7 +179,7 @@ namespace Vocaluxe.Base.ThemeSystem CVideoSkinElement sk = new CVideoSkinElement {FileName = kvp.Value}; if (!File.Exists(Path.Combine(_Folder, sk.FileName))) { - CLog.LogError("Video \"" + kvp.Key + "\": (" + sk.FileName + ") not found!"); + CLog.Error("Video \"" + kvp.Key + "\": (" + sk.FileName + ") not found!"); continue; } _Videos.Add(kvp.Key, sk); diff --git a/Vocaluxe/Base/ThemeSystem/CTheme.cs b/Vocaluxe/Base/ThemeSystem/CTheme.cs index 6014da02..12ea5eaf 100644 --- a/Vocaluxe/Base/ThemeSystem/CTheme.cs +++ b/Vocaluxe/Base/ThemeSystem/CTheme.cs @@ -21,6 +21,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Xml; namespace Vocaluxe.Base.ThemeSystem @@ -80,7 +81,7 @@ namespace Vocaluxe.Base.ThemeSystem } catch (Exception e) { - CLog.LogError(e, "Can't load theme \"" + _FileName + "\". Invalid file!"); + CLog.Error(e, "Can't load theme \"" + _FileName + "\". Invalid file!"); return false; } diff --git a/Vocaluxe/Base/ThemeSystem/CThemes.cs b/Vocaluxe/Base/ThemeSystem/CThemes.cs index fabfa85f..8ca65c47 100644 --- a/Vocaluxe/Base/ThemeSystem/CThemes.cs +++ b/Vocaluxe/Base/ThemeSystem/CThemes.cs @@ -22,6 +22,7 @@ using System.Linq; using VocaluxeLib; using VocaluxeLib.Draw; using System.Diagnostics; +using VocaluxeLib.Log; namespace Vocaluxe.Base.ThemeSystem { @@ -75,13 +76,13 @@ namespace Vocaluxe.Base.ThemeSystem if (theme.Load()) break; theme.Unload(); - CLog.LogError("Failed to load theme " + theme + "! Removing...", true); + CLog.Error("Failed to load theme {ThemeName}! Removing...", CLog.Params(theme.Name, theme), true); _Themes.Remove(theme); theme = _Themes.FirstOrDefault(th => th is CBaseTheme); } CurrentThemes.Add(-1, theme); if (theme == null) - CLog.LogError("No themes found! Cannot continue!", true, true); + CLog.Fatal("No themes found! Cannot continue!"); else { CConfig.Config.Theme.Theme = theme.Name; @@ -104,7 +105,7 @@ namespace Vocaluxe.Base.ThemeSystem return true; } theme.Unload(); - CLog.LogError("Failed to load theme " + theme + " for partymode! Removing...", true); + CLog.Error("Failed to load theme " + theme + " for partymode! Removing...", true); _Themes.Remove(theme); } theme = _Themes.First(th => th.PartyModeID == partyModeID && th.Name == CSettings.DefaultName); @@ -113,7 +114,7 @@ namespace Vocaluxe.Base.ThemeSystem CurrentThemes.Add(partyModeID, theme); return true; } - CLog.LogError("Failed to load default theme for partymode! Unloading partymode!", true); + CLog.Error("Failed to load default theme for partymode! Unloading partymode!", true); foreach (CPartyTheme th in _Themes.Where(th => th.PartyModeID == partyModeID)) th.Unload(); _Themes.RemoveAll(th => th.PartyModeID == partyModeID); @@ -149,12 +150,12 @@ namespace Vocaluxe.Base.ThemeSystem } if (newThemes.Count == 0) { - CLog.LogError("No valid themes found in " + path); + CLog.Error("No valid themes found in " + path); return false; } if (partyModeID >= 0 && newThemes.Count(th => th.Name == CSettings.DefaultName) == 0) { - CLog.LogError("Partymode misses default theme in " + path); + CLog.Error("Partymode misses default theme in " + path); return false; } @@ -170,7 +171,7 @@ namespace Vocaluxe.Base.ThemeSystem private static void _LogMissingElement(int partyModeID, string elType, string elName) { - CLog.LogError("Skin " + CurrentThemes[partyModeID].CurrentSkin + " is missing the " + elType + " \"" + elName + "\"! Expect visual problems!"); + CLog.Error("Skin " + CurrentThemes[partyModeID].CurrentSkin + " is missing the " + elType + " \"" + elName + "\"! Expect visual problems!"); } public static CTextureRef GetSkinTexture(string textureName, int partyModeID) @@ -212,7 +213,7 @@ namespace Vocaluxe.Base.ThemeSystem { SColorF color; if (!GetColor("Player" + playerNr, -1, out color)) - CLog.LogError("Invalid color requested: Color for player " + playerNr + ". Expect visual problems!", true); + CLog.Error("Invalid color requested: Color for player " + playerNr + ". Expect visual problems!", true); return color; } } diff --git a/Vocaluxe/CProgrammHelper.cs b/Vocaluxe/CProgrammHelper.cs index 7a3dd995..52e7d055 100644 --- a/Vocaluxe/CProgrammHelper.cs +++ b/Vocaluxe/CProgrammHelper.cs @@ -21,6 +21,7 @@ using System.Reflection; using System.Security; using Microsoft.Win32; using Vocaluxe.Base; +using VocaluxeLib.Log; namespace Vocaluxe { @@ -118,14 +119,13 @@ namespace Vocaluxe #if WIN if (!_IsVC2010Installed()) { - CLog.LogError( - "VC++ 2010 Redistributables are missing. Please install them first. \r\nDownload(x86): https://www.microsoft.com/de-de/download/details.aspx?id=5555 \r\nDownload(x64): https://www.microsoft.com/de-de/download/details.aspx?id=14632", - true, true); + CLog.Fatal( + "VC++ 2010 Redistributables are missing. Please install them first. \r\nDownload(x86): https://www.microsoft.com/de-de/download/details.aspx?id=5555 \r\nDownload(x64): https://www.microsoft.com/de-de/download/details.aspx?id=14632"); return false; } /*if (!_IsVC2012Installed()) { - CLog.LogError( + CLog.Error( "VC++ 2012 Redistributables are missing. Please install them first.\r\nDownload: http://www.microsoft.com/de-de/download/details.aspx?id=30679", true, true); return false; @@ -134,12 +134,12 @@ namespace Vocaluxe bool vc2008Installed = _IsVC2008Installed(); if (!vc2008Installed) { - CLog.LogError( + CLog.Error( "VC++ 2008 Redistributables are missing. Portaudio might not be working.\r\nDownload: http://www.microsoft.com/de-de/download/details.aspx?id=29"); } if (!vc2008Installed && !_IsVC2010Installed()) { - CLog.LogError( + CLog.Error( "VC++ 2010 and 2008 Redistributables are missing. Please install them first. VC++ 2008 is preferred as Portaudio doesn't work with VC++ 2010.\r\nDownload(2008): http://www.microsoft.com/de-de/download/details.aspx?id=29 \r\nDownload(2010): http://www.microsoft.com/de-de/download/details.aspx?id=5555", true, true); return false; diff --git a/Vocaluxe/CSplashScreen.cs b/Vocaluxe/CSplashScreen.cs index bba2749c..30f1f9b2 100644 --- a/Vocaluxe/CSplashScreen.cs +++ b/Vocaluxe/CSplashScreen.cs @@ -20,6 +20,7 @@ using System.Drawing; using System.IO; using System.Windows.Forms; using Vocaluxe.Base; +using VocaluxeLib.Log; namespace Vocaluxe { @@ -39,11 +40,11 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error loading logo: " + e.Message); + CLog.Error("Error loading logo: " + e.Message); } } else - CLog.LogError("Can't find " + path); + CLog.Error("Can't find " + path); path = Path.Combine(CSettings.ProgramFolder, CSettings.FileNameIcon); if (File.Exists(path)) @@ -54,11 +55,11 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Error loading icon: " + e.Message); + CLog.Error("Error loading icon: " + e.Message); } } else - CLog.LogError("Can't find " + path); + CLog.Error("Can't find " + path); SetStyle(ControlStyles.SupportsTransparentBackColor, true); BackColor = Color.Transparent; diff --git a/Vocaluxe/Lib/Database/CCoverDB.cs b/Vocaluxe/Lib/Database/CCoverDB.cs index 7c902fbe..f4729f5f 100644 --- a/Vocaluxe/Lib/Database/CCoverDB.cs +++ b/Vocaluxe/Lib/Database/CCoverDB.cs @@ -24,6 +24,7 @@ using System.Runtime.InteropServices; using Vocaluxe.Base; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Log; #if WIN using System.Data.SQLite; @@ -74,7 +75,7 @@ namespace Vocaluxe.Lib.Database return false; if (!File.Exists(coverPath)) { - CLog.LogError("Can't find File: " + coverPath); + CLog.Error("Can't find File: " + coverPath); return false; } @@ -230,7 +231,7 @@ namespace Vocaluxe.Lib.Database } catch (Exception e) { - CLog.LogError("Error creating Cover DB " + e); + CLog.Error("Error creating Cover DB " + e); return false; } return true; diff --git a/Vocaluxe/Lib/Database/CHighscoreDB.cs b/Vocaluxe/Lib/Database/CHighscoreDB.cs index c6683788..5f6bf106 100644 --- a/Vocaluxe/Lib/Database/CHighscoreDB.cs +++ b/Vocaluxe/Lib/Database/CHighscoreDB.cs @@ -23,6 +23,7 @@ using System.Text; using Community.CsharpSqlite; using Vocaluxe.Base; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Songs; #if WIN using System.Data.SQLite; @@ -58,17 +59,17 @@ namespace Vocaluxe.Lib.Database { if (!_CreateOrConvert(oldDBFilePath)) { - CLog.LogError("Cannot init Highscore DB: Error opening database: " + oldDBFilePath, true, true); + CLog.Fatal("Cannot init Highscore DB: Error opening old database: {DBFile}" + CLog.Params(oldDBFilePath)); return false; } if (!_CreateOrConvert(_FilePath)) { - CLog.LogError("Cannot init Highscore DB: Error opening database: " + _FilePath, true, true); + CLog.Fatal("Cannot init Highscore DB: Error opening database: {DBFile}", CLog.Params(_FilePath)); return false; } if (!_ImportData(oldDBFilePath)) { - CLog.LogError("Cannot init Highscore DB: Error importing data", true, true); + CLog.Fatal("Cannot init Highscore DB: Error importing data"); return false; } } @@ -77,7 +78,7 @@ namespace Vocaluxe.Lib.Database File.Copy(oldDBFilePath, _FilePath); if (!_CreateOrConvert(_FilePath)) { - CLog.LogError("Cannot init Highscore DB: Error opening database: " + _FilePath, true, true); + CLog.Fatal("Cannot init Highscore DB: Error opening database: {DBFile}" + CLog.Params(_FilePath)); return false; } } @@ -85,7 +86,7 @@ namespace Vocaluxe.Lib.Database } else if (!_CreateOrConvert(_FilePath)) { - CLog.LogError("Cannot init Highscore DB: Error opening database: " + _FilePath, true, true); + CLog.Fatal("Cannot init Highscore DB: Error opening database: {DBFile}", CLog.Params(_FilePath)); return false; } return true; @@ -827,14 +828,14 @@ namespace Vocaluxe.Lib.Database int res = Sqlite3.sqlite3_open(filePath, out oldDB); if (res != Sqlite3.SQLITE_OK) - CLog.LogError("Error opening Database: " + filePath + " (" + Sqlite3.sqlite3_errmsg(oldDB) + ")"); + CLog.Error("Error opening Database: " + filePath + " (" + Sqlite3.sqlite3_errmsg(oldDB) + ")"); else { var stmt = new Sqlite3.Vdbe(); res = Sqlite3.sqlite3_prepare_v2(oldDB, "SELECT id, Artist, Title FROM Songs", -1, ref stmt, 0); if (res != Sqlite3.SQLITE_OK) - CLog.LogError("Error query Database: " + filePath + " (" + Sqlite3.sqlite3_errmsg(oldDB) + ")"); + CLog.Error("Error query Database: " + filePath + " (" + Sqlite3.sqlite3_errmsg(oldDB) + ")"); else { //Sqlite3.sqlite3_step(Stmt); @@ -866,7 +867,7 @@ namespace Vocaluxe.Lib.Database res = Sqlite3.sqlite3_prepare_v2(oldDB, "SELECT id, PlayerName, Date FROM Scores", -1, ref stmt, 0); if (res != Sqlite3.SQLITE_OK) - CLog.LogError("Error query Database: " + filePath + " (" + Sqlite3.sqlite3_errmsg(oldDB) + ")"); + CLog.Error("Error query Database: " + filePath + " (" + Sqlite3.sqlite3_errmsg(oldDB) + ")"); else { //Sqlite3.sqlite3_step(Stmt); @@ -1022,7 +1023,7 @@ namespace Vocaluxe.Lib.Database } catch (Exception e) { - CLog.LogError("Error on import high score data. Can't open source database \"" + sourceDBPath + "\" (" + e.Message + ")"); + CLog.Error("Error on import high score data. Can't open source database \"" + sourceDBPath + "\" (" + e.Message + ")"); return false; } #endregion open db diff --git a/Vocaluxe/Lib/Database/CResourceDB.cs b/Vocaluxe/Lib/Database/CResourceDB.cs index 64b0a181..1aca8250 100644 --- a/Vocaluxe/Lib/Database/CResourceDB.cs +++ b/Vocaluxe/Lib/Database/CResourceDB.cs @@ -23,6 +23,7 @@ using System.IO; using System.Runtime.InteropServices; using Vocaluxe.Base; using VocaluxeLib.Draw; +using VocaluxeLib.Log; #if WIN using System.Data.SQLite; @@ -53,7 +54,7 @@ namespace Vocaluxe.Lib.Database return false; if (_Version < 0) { - CLog.LogError("Can't find Ressource-DB!"); + CLog.Error("Can't find Ressource-DB!"); return false; } @@ -62,7 +63,7 @@ namespace Vocaluxe.Lib.Database #if DEBUG return _CreateDB() && _UpdateV1(); #else - CLog.LogError("Upgrading Ressource-DB not possible"); + CLog.Error("Upgrading Ressource-DB not possible"); return false; #endif } @@ -161,7 +162,7 @@ namespace Vocaluxe.Lib.Database } catch (Exception) { - CLog.LogError("Error loading image: " + imagePath); + CLog.Error("Error loading image: " + imagePath); return false; } try @@ -247,7 +248,7 @@ namespace Vocaluxe.Lib.Database } catch (Exception e) { - CLog.LogError("Error creating Ressource DB " + e); + CLog.Error("Error creating Ressource DB " + e); return false; } return true; diff --git a/Vocaluxe/Lib/Draw/CDirect3D.cs b/Vocaluxe/Lib/Draw/CDirect3D.cs index 11dd3c22..728e1217 100644 --- a/Vocaluxe/Lib/Draw/CDirect3D.cs +++ b/Vocaluxe/Lib/Draw/CDirect3D.cs @@ -28,6 +28,7 @@ using SlimDX.Windows; using Vocaluxe.Base; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Draw { @@ -94,7 +95,7 @@ namespace Vocaluxe.Lib.Draw } catch (Direct3DX9NotFoundException e) { - CLog.LogError(e, "No DirectX runtimes were found, please download and install them from http://www.microsoft.com/download/en/details.aspx?id=8109"); + CLog.Error(e, "No DirectX runtimes were found, please download and install them from http://www.microsoft.com/download/en/details.aspx?id=8109"); } _Form.KeyDown += _OnKeyDown; @@ -149,7 +150,7 @@ namespace Vocaluxe.Lib.Draw !_D3D.CheckDeviceMultisampleType(_D3D.Adapters.DefaultAdapter.Adapter, DeviceType.Hardware, _D3D.Adapters.DefaultAdapter.CurrentDisplayMode.Format, false, msType, out quality)) { - CLog.LogError("[Direct3D] This AAMode is not supported by this device or driver, fallback to no AA"); + CLog.Error("[Direct3D] This AAMode is not supported by this device or driver, fallback to no AA"); msType = MultisampleType.None; quality = 1; } @@ -177,15 +178,14 @@ namespace Vocaluxe.Lib.Draw } catch (Exception e) { - CLog.LogError(e, "Error during D3D device creation."); + CLog.Error(e, "Error during D3D device creation."); } finally { if (_Device == null || _Device.Disposed) { - CLog.LogError( - "Something went wrong during device creating, please check if your DirectX redistributables and grafic card drivers are up to date. You can download the DirectX runtimes at http://www.microsoft.com/download/en/details.aspx?id=8109", - true, true); + CLog.Fatal( + "Something went wrong during device creating, please check if your DirectX redistributables and grafic card drivers are up to date. You can download the DirectX runtimes at http://www.microsoft.com/download/en/details.aspx?id=8109"); } } } @@ -278,40 +278,40 @@ namespace Vocaluxe.Lib.Draw VertexFormat.Position | VertexFormat.Texture1 | VertexFormat.Diffuse, Pool.Default); if (_Device.SetStreamSource(0, _VertexBuffer, 0, Marshal.SizeOf(typeof(STexturedColoredVertex))).IsFailure) - CLog.LogError("Failed to set stream source"); + CLog.Error("Failed to set stream source"); _Device.VertexDeclaration = STexturedColoredVertex.GetDeclaration(_Device); if (_Device.SetRenderState(RenderState.CullMode, Cull.None).IsFailure) - CLog.LogError("Failed to set cull mode"); + CLog.Error("Failed to set cull mode"); if (_Device.SetRenderState(RenderState.AlphaBlendEnable, true).IsFailure) - CLog.LogError("Failed to enable alpha blending"); + CLog.Error("Failed to enable alpha blending"); if (_Device.SetRenderState(RenderState.Lighting, false).IsFailure) - CLog.LogError("Failed to disable lighting"); + CLog.Error("Failed to disable lighting"); if (_Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha).IsFailure) - CLog.LogError("Failed to set destination blend"); + CLog.Error("Failed to set destination blend"); if (_Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha).IsFailure) - CLog.LogError("Failed to set source blend"); + CLog.Error("Failed to set source blend"); if (_PresentParameters.Multisample != MultisampleType.None) { if (_Device.SetRenderState(RenderState.MultisampleAntialias, true).IsFailure) - CLog.LogError("Failed to set antialiasing"); + CLog.Error("Failed to set antialiasing"); } if (_Device.SetSamplerState(0, SamplerState.MinFilter, TextureFilter.Linear).IsFailure) - CLog.LogError("Failed to set min filter"); + CLog.Error("Failed to set min filter"); if (_Device.SetSamplerState(0, SamplerState.MagFilter, TextureFilter.Linear).IsFailure) - CLog.LogError("Failed to set mag filter"); + CLog.Error("Failed to set mag filter"); if (_Device.SetSamplerState(0, SamplerState.MipFilter, TextureFilter.Linear).IsFailure) - CLog.LogError("Failed to set mip filter"); + CLog.Error("Failed to set mip filter"); if (_Device.SetSamplerState(0, SamplerState.AddressU, TextureAddress.Clamp).IsFailure) - CLog.LogError("Failed to set clamping on u"); + CLog.Error("Failed to set clamping on u"); if (_Device.SetSamplerState(0, SamplerState.AddressV, TextureAddress.Clamp).IsFailure) - CLog.LogError("Failed to set claming on v"); + CLog.Error("Failed to set claming on v"); if (_Device.SetTextureStageState(0, TextureStage.AlphaArg1, TextureArgument.Texture).IsFailure) - CLog.LogError("Failed to set alpha argument 1"); + CLog.Error("Failed to set alpha argument 1"); if (_Device.SetTextureStageState(0, TextureStage.AlphaArg2, TextureArgument.Diffuse).IsFailure) - CLog.LogError("Failed to set alpha argument 2"); + CLog.Error("Failed to set alpha argument 2"); if (_Device.SetTextureStageState(0, TextureStage.AlphaOperation, TextureOperation.Modulate).IsFailure) - CLog.LogError("Failed to set alpha operation"); + CLog.Error("Failed to set alpha operation"); var indices = new Int16[6]; indices[0] = 0; @@ -332,14 +332,14 @@ namespace Vocaluxe.Lib.Draw protected override void _OnBeforeDraw() { if (_Device.BeginScene().IsFailure) - CLog.LogError("Failed to begin scene"); + CLog.Error("Failed to begin scene"); } protected override void _OnAfterDraw() { _RenderVertexBuffer(); if (_Device.EndScene().IsFailure) - CLog.LogError("Failed to end scene"); + CLog.Error("Failed to end scene"); try { //Now push the frame to the Viewport @@ -370,7 +370,7 @@ namespace Vocaluxe.Lib.Draw _VertexBuffer.Dispose(); _IndexBuffer.Dispose(); if (_Device.Reset(_PresentParameters).IsFailure) - CLog.LogError("Failed to reset the device"); + CLog.Error("Failed to reset the device"); } protected override void _AdjustNewBorders() @@ -384,9 +384,9 @@ namespace Vocaluxe.Lib.Draw CSettings.ZNear, CSettings.ZFar); if (_Device.SetTransform(TransformState.Projection, projection).IsFailure) - CLog.LogError("Failed to set orthogonal matrix"); + CLog.Error("Failed to set orthogonal matrix"); if (_Device.SetTransform(TransformState.World, translate).IsFailure) - CLog.LogError("Failed to set translation matrix"); + CLog.Error("Failed to set translation matrix"); } /// @@ -491,13 +491,13 @@ namespace Vocaluxe.Lib.Draw { //Apply rotation if (_Device.SetTransform(TransformState.World, _VerticesRotationMatrices.Dequeue()).IsFailure) - CLog.LogError("Failed to set world transformation"); + CLog.Error("Failed to set world transformation"); //Apply texture if (_Device.SetTexture(0, _VerticesTextures.Dequeue()).IsFailure) - CLog.LogError("Failed to set texture"); + CLog.Error("Failed to set texture"); //Draw 2 triangles from vertexbuffer if (_Device.DrawIndexedPrimitives(PrimitiveType.TriangleList, i, 0, 4, 0, 2).IsFailure) - CLog.LogError("Failed to draw quad"); + CLog.Error("Failed to draw quad"); } //Clear the queues for the next frame _Vertices.Clear(); @@ -512,7 +512,7 @@ namespace Vocaluxe.Lib.Draw protected override void _ClearScreen() { if (_Device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0).IsFailure) - CLog.LogError("Failed to clear the backbuffer"); + CLog.Error("Failed to clear the backbuffer"); } /// diff --git a/Vocaluxe/Lib/Input/CHIDAPI.cs b/Vocaluxe/Lib/Input/CHIDAPI.cs index 73a92ce1..86fa686e 100644 --- a/Vocaluxe/Lib/Input/CHIDAPI.cs +++ b/Vocaluxe/Lib/Input/CHIDAPI.cs @@ -18,6 +18,7 @@ using System; using System.Runtime.InteropServices; using Vocaluxe.Base; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Input { @@ -73,7 +74,7 @@ namespace Vocaluxe.Lib.Input } catch (Exception e) { - CLog.LogError("Error CHIDAPI.Init(): " + e); + CLog.Error("Error CHIDAPI.Init(): " + e); return false; } @@ -92,7 +93,7 @@ namespace Vocaluxe.Lib.Input } catch (Exception e) { - CLog.LogError("Error CHIDAPI.Exit(): " + e); + CLog.Error("Error CHIDAPI.Exit(): " + e); return false; } @@ -129,7 +130,7 @@ namespace Vocaluxe.Lib.Input } catch (Exception e) { - CLog.LogError("Error CHIDAPI.Open(): " + e); + CLog.Error("Error CHIDAPI.Open(): " + e); return false; } @@ -169,7 +170,7 @@ namespace Vocaluxe.Lib.Input catch (Exception e) { bytesRead = -1; - CLog.LogError("Error CHIDAPI.ReadTimeout(): " + e); + CLog.Error("Error CHIDAPI.ReadTimeout(): " + e); } if (bytesRead != -1) @@ -198,7 +199,7 @@ namespace Vocaluxe.Lib.Input catch (Exception e) { result = -1; - CLog.LogError("Error CHIDAPI.Read(): " + e); + CLog.Error("Error CHIDAPI.Read(): " + e); } if (result != -1) @@ -245,7 +246,7 @@ namespace Vocaluxe.Lib.Input } catch (Exception e) { - CLog.LogError("Error CHIDAPI.Close(): " + e); + CLog.Error("Error CHIDAPI.Close(): " + e); return false; } return true; diff --git a/Vocaluxe/Lib/Input/WiiMote/WiiMoteLib.cs b/Vocaluxe/Lib/Input/WiiMote/WiiMoteLib.cs index f8eb987b..e6642331 100644 --- a/Vocaluxe/Lib/Input/WiiMote/WiiMoteLib.cs +++ b/Vocaluxe/Lib/Input/WiiMote/WiiMoteLib.cs @@ -20,6 +20,7 @@ using System.Drawing; using System.Threading; using Vocaluxe.Base; using VocaluxeLib; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Input.WiiMote { @@ -213,8 +214,8 @@ namespace Vocaluxe.Lib.Input.WiiMote { if (!CHIDApi.Init()) { - CLog.LogError("WiiMoteLib: Can't initialize HID API"); - CLog.LogError("Please install the Visual C++ Redistributable Packages 2008!"); + CLog.Error("WiiMoteLib: Can't initialize HID API"); + CLog.Error("Please install the Visual C++ Redistributable Packages 2008!"); _Error = true; } @@ -353,7 +354,7 @@ namespace Vocaluxe.Lib.Input.WiiMote } catch (Exception e) { - CLog.LogError("(WiiMoteLib) Error reading from device: " + e); + CLog.Error("(WiiMoteLib) Error reading from device: " + e); Connected = false; break; } @@ -414,7 +415,7 @@ namespace Vocaluxe.Lib.Input.WiiMote break; default: - //CLog.LogError("(WiiMoteLib) Unknown report type: " + type.ToString("x")); + //CLog.Error("(WiiMoteLib) Unknown report type: " + type.ToString("x")); return false; } @@ -519,13 +520,13 @@ namespace Vocaluxe.Lib.Input.WiiMote { if ((buff[3] & 0x08) != 0) { - CLog.LogError("Error reading data from WiiMote: Bytes do not exist"); + CLog.Error("Error reading data from WiiMote: Bytes do not exist"); Connected = false; } if ((buff[3] & 0x07) != 0) { - CLog.LogError("Error reading data from WiiMote: Attempt to read from write-only registers."); + CLog.Error("Error reading data from WiiMote: Attempt to read from write-only registers."); Connected = false; } diff --git a/Vocaluxe/Lib/Playlist/CPlaylistFile.cs b/Vocaluxe/Lib/Playlist/CPlaylistFile.cs index fdddf0dc..9c9c9ac4 100644 --- a/Vocaluxe/Lib/Playlist/CPlaylistFile.cs +++ b/Vocaluxe/Lib/Playlist/CPlaylistFile.cs @@ -21,6 +21,7 @@ using System.IO; using System.Linq; using Vocaluxe.Base; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Menu; using VocaluxeLib.Songs; using VocaluxeLib.Xml; @@ -77,7 +78,7 @@ namespace Vocaluxe.Lib.Playlist } catch (Exception e) { - CLog.LogError("Cannot load playlist from " + file + ": " + e.Message); + CLog.Error("Cannot load playlist from " + file + ": " + e.Message); return false; } Name = data.Info.Name; @@ -86,7 +87,7 @@ namespace Vocaluxe.Lib.Playlist { CSong plSong = CSongs.AllSongs.FirstOrDefault(song => song.Artist == songEntry.Artist && song.Title == songEntry.Title); if (plSong == null) - CLog.LogError("Can't find song '" + songEntry.Title + "' from '" + songEntry.Artist + "' in playlist file: " + File); + CLog.Error("Can't find song '" + songEntry.Title + "' from '" + songEntry.Artist + "' in playlist file: " + File); else { var playlistSong = new CPlaylistSong(plSong.ID, songEntry.GameMode); diff --git a/Vocaluxe/Lib/Sound/CPortAudioHandle.cs b/Vocaluxe/Lib/Sound/CPortAudioHandle.cs index 355b4b45..a8fe8e89 100644 --- a/Vocaluxe/Lib/Sound/CPortAudioHandle.cs +++ b/Vocaluxe/Lib/Sound/CPortAudioHandle.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using System.Diagnostics; using PortAudioSharp; using Vocaluxe.Base; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Sound { @@ -64,11 +65,11 @@ namespace Vocaluxe.Lib.Sound if (_Disposed) return; if (!disposing) - CLog.LogDebug("Did not close CPortAudioHandle"); + CLog.Debug("Did not close CPortAudioHandle"); //Make sure we do not leek any streams as we may keep PA open if (_Streams.Count > 0) { - CLog.LogDebug("Did not close " + _Streams.Count + "PortAudio-Stream(s)"); + CLog.Debug("Did not close " + _Streams.Count + "PortAudio-Stream(s)"); while (_Streams.Count > 0) CloseStream(_Streams[0]); } @@ -86,7 +87,7 @@ namespace Vocaluxe.Lib.Sound } catch (Exception ex) { - CLog.LogError(ex, "Error disposing PortAudio"); + CLog.Error(ex, "Error disposing PortAudio"); } } _Disposed = true; @@ -179,7 +180,7 @@ namespace Vocaluxe.Lib.Sound } catch (Exception ex) { - CLog.LogError(ex, "Error closing stream:"); + CLog.Error(ex, "Error closing stream:"); } _Streams.Remove(stream); } @@ -199,13 +200,13 @@ namespace Vocaluxe.Lib.Sound if (errorCode != PortAudio.PaError.paNoError) { - CLog.LogError(action + " error: " + PortAudio.Pa_GetErrorText(errorCode)); + CLog.Error(action + " error: " + PortAudio.Pa_GetErrorText(errorCode)); if (errorCode == PortAudio.PaError.paUnanticipatedHostError) { PortAudio.PaHostErrorInfo errorInfo = PortAudio.Pa_GetLastHostErrorInfo(); - CLog.LogError("- Host error API type: " + errorInfo.hostApiType); - CLog.LogError("- Host error code: " + errorInfo.errorCode); - CLog.LogError("- Host error text: " + errorInfo.errorText); + CLog.Error("- Host error API type: " + errorInfo.hostApiType); + CLog.Error("- Host error code: " + errorInfo.errorCode); + CLog.Error("- Host error text: " + errorInfo.errorText); } return true; } diff --git a/Vocaluxe/Lib/Sound/Playback/CAudioStreamBase.cs b/Vocaluxe/Lib/Sound/Playback/CAudioStreamBase.cs index 35e62978..bad098cf 100644 --- a/Vocaluxe/Lib/Sound/Playback/CAudioStreamBase.cs +++ b/Vocaluxe/Lib/Sound/Playback/CAudioStreamBase.cs @@ -17,6 +17,7 @@ using System; using VocaluxeLib; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Sound.Playback { @@ -71,7 +72,7 @@ namespace Vocaluxe.Lib.Sound.Playback protected virtual void _Dispose(bool disposing) { if (!disposing) - CBase.Log.LogDebug("Audio stream " + _Medium + " was not closed."); + CLog.Debug("Audio stream " + _Medium + " was not closed."); } public void Close() diff --git a/Vocaluxe/Lib/Sound/Playback/Decoder/CAudioDecoderFFmpeg.cs b/Vocaluxe/Lib/Sound/Playback/Decoder/CAudioDecoderFFmpeg.cs index 505b3604..7abe611b 100644 --- a/Vocaluxe/Lib/Sound/Playback/Decoder/CAudioDecoderFFmpeg.cs +++ b/Vocaluxe/Lib/Sound/Playback/Decoder/CAudioDecoderFFmpeg.cs @@ -19,6 +19,7 @@ using System; using System.Runtime.InteropServices; using Vocaluxe.Base; using Vocaluxe.Lib.Video.Acinerella; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Sound.Playback.Decoder { @@ -46,7 +47,7 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder } catch (Exception) { - CLog.LogError("Error opening audio file: " + _FileName); + CLog.Error("Error opening audio file: " + _FileName); Close(); return false; } @@ -68,7 +69,7 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder } catch (Exception) { - CLog.LogError("Error opening audio file (can't find decoder): " + _FileName); + CLog.Error("Error opening audio file (can't find decoder): " + _FileName); Close(); return false; } @@ -90,7 +91,7 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder if (_FormatInfo.BitDepth != 16) { - CLog.LogError("Unsupported BitDepth in file " + fileName); + CLog.Error("Unsupported BitDepth in file " + fileName); Close(); return false; } @@ -136,7 +137,7 @@ namespace Vocaluxe.Lib.Sound.Playback.Decoder } catch (Exception) { - CLog.LogError("Error seeking in file: " + _FileName); + CLog.Error("Error seeking in file: " + _FileName); Close(); } } diff --git a/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudio.cs b/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudio.cs index 6de16180..bd903d2d 100644 --- a/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudio.cs +++ b/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudio.cs @@ -19,6 +19,7 @@ using Gst; using System; using Vocaluxe.Base; using System.IO; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp { @@ -49,7 +50,7 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp if (gstreamerEnvVar == null || !Directory.Exists(gstreamerEnvVar)) { - CLog.LogError("Gstreamer not found! Make sure you installed it correctly and if it set the environment variable '" + varName + "'!", true); + CLog.Error("Gstreamer not found! Make sure you installed it correctly and if it set the environment variable '" + varName + "'!", true); return false; } else diff --git a/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudioStream.cs b/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudioStream.cs index 452056d0..f433c8c5 100644 --- a/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudioStream.cs +++ b/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudioStream.cs @@ -20,6 +20,7 @@ using GLib; using Gst; using Vocaluxe.Base; using VocaluxeLib; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp { @@ -79,7 +80,7 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp long position; if (!_Element.QueryPosition(Format.Time, out position)) - CLog.LogError("Could not query position"); + CLog.Error("Could not query position"); else _Position = ((float)position / Constants.SECOND); @@ -121,7 +122,7 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp if (convert == null || audiosink == null) { - CLog.LogError("Could not create pipeline"); + CLog.Error("Could not create pipeline"); if (convert != null) convert.Dispose(); if (audiosink != null) @@ -158,7 +159,7 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp if (pad == null) { - CLog.LogError("Could not create pads"); + CLog.Error("Could not create pads"); convert.Dispose(); audiosink.Dispose(); audioSinkBin.Dispose(); @@ -171,7 +172,7 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp if (!ghostpad.SetActive(true)) { - CLog.LogError("Could not link pads"); + CLog.Error("Could not link pads"); convert.Dispose(); audiosink.Dispose(); audioSinkBin.Dispose(); @@ -184,7 +185,7 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp } if (!audioSinkBin.AddPad(ghostpad)) { - CLog.LogError("Could not add pad"); + CLog.Error("Could not add pad"); convert.Dispose(); audiosink.Dispose(); audioSinkBin.Dispose(); @@ -199,7 +200,7 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp _Element = ElementFactory.Make("playbin", "playbin"); if (_Element == null) { - CLog.LogError("Could not create playbin"); + CLog.Error("Could not create playbin"); convert.Dispose(); audiosink.Dispose(); audioSinkBin.Dispose(); @@ -299,7 +300,7 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp GException error; string debug; msg.ParseError(out error, out debug); - CLog.LogError("Gstreamer error: message" + error.Message + ", code" + error.Code + " ,debug information" + debug); + CLog.Error("Gstreamer error: message" + error.Message + ", code" + error.Code + " ,debug information" + debug); return false; case MessageType.DurationChanged: _UpdateDuration(); diff --git a/Vocaluxe/Lib/Sound/Playback/OpenAL/COpenAlStream.cs b/Vocaluxe/Lib/Sound/Playback/OpenAL/COpenAlStream.cs index fd22e07c..38a0cb77 100644 --- a/Vocaluxe/Lib/Sound/Playback/OpenAL/COpenAlStream.cs +++ b/Vocaluxe/Lib/Sound/Playback/OpenAL/COpenAlStream.cs @@ -22,6 +22,7 @@ using System.Threading; using OpenTK.Audio; using Vocaluxe.Base; using Vocaluxe.Lib.Sound.Playback.Decoder; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Sound.Playback.OpenAL { @@ -178,7 +179,7 @@ namespace Vocaluxe.Lib.Sound.Playback.OpenAL if (!ok) { Dispose(); - CLog.LogError("Error Init OpenAL Playback"); + CLog.Error("Error Init OpenAL Playback"); return false; } @@ -187,14 +188,14 @@ namespace Vocaluxe.Lib.Sound.Playback.OpenAL if (!_Decoder.Open(_Medium)) { Dispose(); - CLog.LogError("Error opening audio file: " + _Medium); + CLog.Error("Error opening audio file: " + _Medium); return false; } _Format = _Decoder.GetFormatInfo(); if (_Format.SamplesPerSecond == 0) { Dispose(); - CLog.LogError("Error Init OpenAL Playback (samples=0)"); + CLog.Error("Error Init OpenAL Playback (samples=0)"); return false; } diff --git a/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioStream.cs b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioStream.cs index 2002aa9c..d8040976 100644 --- a/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioStream.cs +++ b/Vocaluxe/Lib/Sound/Playback/PortAudio/CPortAudioStream.cs @@ -22,6 +22,7 @@ using System.Runtime.InteropServices; using System.Threading; using Vocaluxe.Base; using Vocaluxe.Lib.Sound.Playback.Decoder; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Sound.Playback.PortAudio { @@ -160,7 +161,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio catch (Exception) { Dispose(); - CLog.LogError("Error Init PortAudio Playback"); + CLog.Error("Error Init PortAudio Playback"); return false; } @@ -168,7 +169,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio if (!_Decoder.Open(_Medium)) { Dispose(); - CLog.LogError("Error opening audio file: " + _Medium); + CLog.Error("Error opening audio file: " + _Medium); return false; } @@ -176,7 +177,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio if (format.SamplesPerSecond == 0) { Dispose(); - CLog.LogError("Error Init PortAudio Playback (samples=0)"); + CLog.Error("Error Init PortAudio Playback (samples=0)"); return false; } @@ -413,7 +414,7 @@ namespace Vocaluxe.Lib.Sound.Playback.PortAudio } catch (Exception e) { - CLog.LogError("Error PortAudio.StreamCallback: " + e.Message); + CLog.Error("Error PortAudio.StreamCallback: " + e.Message); } return PortAudioSharp.PortAudio.PaStreamCallbackResult.paContinue; diff --git a/Vocaluxe/Lib/Sound/Record/PitchTracker/CPitchTrackerTest.cs b/Vocaluxe/Lib/Sound/Record/PitchTracker/CPitchTrackerTest.cs index 7c727693..81b934c1 100644 --- a/Vocaluxe/Lib/Sound/Record/PitchTracker/CPitchTrackerTest.cs +++ b/Vocaluxe/Lib/Sound/Record/PitchTracker/CPitchTrackerTest.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Utils; namespace Vocaluxe.Lib.Sound.Record.PitchTracker @@ -71,7 +72,7 @@ namespace Vocaluxe.Lib.Sound.Record.PitchTracker _CurPassedCount[i] = 0; } msg += "of " + _CurTestCount; - CBase.Log.LogDebug(msg); + CLog.Debug(msg); _TestCount += _CurTestCount; _CurTestCount = 0; //Do a reset first as we actually have an impossible situation (drop by multiple octaves) @@ -107,7 +108,7 @@ namespace Vocaluxe.Lib.Sound.Record.PitchTracker _InitNext("Real song"); _TestSpeed(); - CBase.Log.LogDebug("Finished: "); + CLog.Debug("Finished: "); for (int i = 0; i < _Analyzers.Count; i++) { string msg = _Analyzers[i].GetType().Name + ":"; @@ -115,7 +116,7 @@ namespace Vocaluxe.Lib.Sound.Record.PitchTracker msg += " Passed=" + _PassedCount[i]; msg += " Total=" + _TestCount; msg += " Speed=" + _SamplesPerSec[i] / 1000 + "kSamples/s (=" + (_SamplesPerSec[i] / 44100) + "rec.s/s)"; - CBase.Log.LogDebug(msg); + CLog.Debug(msg); } } @@ -208,7 +209,7 @@ namespace Vocaluxe.Lib.Sound.Record.PitchTracker string msg = "Note " + _ToneToNote(tone) + "(" + distort + ") at buffer " + (i + 1) + "/" + (sampleCt / batchCt) + " detected as "; for (int j = 0; j < valids.Length; j++) msg += _ToneToNote(_Tones[j]) + (valids[j] ? "" : "(!)") + "; "; - CBase.Log.LogDebug(msg); + CLog.Debug(msg); /*CWavFile w = new CWavFile(); w.Create(tone + "-" + distort + ".wav", 1, 44100, 16); w.Write16BitSamples(data); @@ -401,7 +402,7 @@ namespace Vocaluxe.Lib.Sound.Record.PitchTracker string msg = "Note " + _ToneToNote(curNote) + " at " + time + "ms detected as "; for (int i = 0; i < _Tones.Length; i++) msg += _ToneToNote(_Tones[i]) + (valids[i] ? "" : "(!)") + "; "; - CBase.Log.LogDebug(msg); + CLog.Debug(msg); } } } diff --git a/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs b/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs index cfc8818c..dde1cbe9 100644 --- a/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs +++ b/Vocaluxe/Lib/Sound/Record/PortAudio/CPortAudioRecord.cs @@ -21,6 +21,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading; using Vocaluxe.Base; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Sound.Record.PortAudio { @@ -64,7 +65,7 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio catch (Exception e) { _Initialized = false; - CLog.LogError("Error initializing PortAudio: " + e.Message); + CLog.Error("Error initializing PortAudio: " + e.Message); Close(); return false; } @@ -207,7 +208,7 @@ namespace Vocaluxe.Lib.Sound.Record.PortAudio } catch (Exception e) { - CLog.LogError("Error on Stream Callback (rec): " + e); + CLog.Error("Error on Stream Callback (rec): " + e); } return PortAudioSharp.PortAudio.PaStreamCallbackResult.paContinue; diff --git a/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs b/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs index 64385abe..2e845f4d 100644 --- a/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs +++ b/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs @@ -21,6 +21,7 @@ using System.Runtime.InteropServices; using System.Threading; using Vocaluxe.Base; using VocaluxeLib.Draw; +using VocaluxeLib.Log; namespace Vocaluxe.Lib.Video.Acinerella { @@ -87,7 +88,7 @@ namespace Vocaluxe.Lib.Video.Acinerella _Free(); } catch (Exception) {} - CLog.LogError("Error opening video file: " + _FileName); + CLog.Error("Error opening video file: " + _FileName); _Instance = IntPtr.Zero; return false; } @@ -96,12 +97,12 @@ namespace Vocaluxe.Lib.Video.Acinerella { if (_Instance == IntPtr.Zero) { - CLog.LogError("Tried to start a video file that is not open: " + _FileName); + CLog.Error("Tried to start a video file that is not open: " + _FileName); return false; } if (_Thread != null) { - CLog.LogError("Tried to start a video file that is already started: " + _FileName); + CLog.Error("Tried to start a video file that is already started: " + _FileName); return false; } RequestTime = 0f; @@ -281,7 +282,7 @@ namespace Vocaluxe.Lib.Video.Acinerella } catch (Exception) { - CLog.LogError("Error opening video file (can't find decoder): " + _FileName); + CLog.Error("Error opening video file (can't find decoder): " + _FileName); return false; } @@ -325,7 +326,7 @@ namespace Vocaluxe.Lib.Video.Acinerella } catch (Exception e) { - CLog.LogError("Error seeking video file \"" + _FileName + "\": " + e.Message); + CLog.Error("Error seeking video file \"" + _FileName + "\": " + e.Message); } _LastDecodedTime = skipTime; @@ -369,7 +370,7 @@ namespace Vocaluxe.Lib.Video.Acinerella } catch (Exception) { - CLog.LogError("Error AcGetFrame " + _FileName); + CLog.Error("Error AcGetFrame " + _FileName); } } if (hasFrameDecoded) @@ -395,7 +396,7 @@ namespace Vocaluxe.Lib.Video.Acinerella } catch (Exception) { - CLog.LogError("Error AcSeek " + _FileName); + CLog.Error("Error AcSeek " + _FileName); } if (!hasFrameDecoded) @@ -419,7 +420,7 @@ namespace Vocaluxe.Lib.Video.Acinerella } catch (Exception) { - CLog.LogError("Error AcSkipFrame " + _FileName); + CLog.Error("Error AcSkipFrame " + _FileName); } return hasFrameDecoded; } @@ -437,7 +438,7 @@ namespace Vocaluxe.Lib.Video.Acinerella } catch (Exception e) { - CLog.LogError(e, "Couldn't copy the frame to the managed environment."); + CLog.Error(e, "Couldn't copy the frame to the managed environment."); return false; } diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index 590a0127..84ea160b 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -30,6 +30,7 @@ using Vocaluxe.Base; using Vocaluxe.Base.Fonts; using Vocaluxe.Base.Server; using Vocaluxe.Base.ThemeSystem; +using VocaluxeLib.Log; [assembly: InternalsVisibleTo("VocaluxeTests")] @@ -80,7 +81,7 @@ namespace Vocaluxe } catch {} MessageBox.Show("Unhandled error: " + e.Message + stackTrace); - CLog.LogError("Unhandled error: " + e.Message + stackTrace); + CLog.Error("Unhandled error: " + e.Message + stackTrace); } #else _Run(args); @@ -98,20 +99,20 @@ namespace Vocaluxe Directory.CreateDirectory(CSettings.DataFolder); // Init Log - CLog.Init(CSettings.FolderNameLogs); + CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog); if (!CProgrammHelper.CheckRequirements()) return; CProgrammHelper.Init(); - CLog.LogInformation("Init Program"); + CLog.Information("Init Program"); using (Operation.At(LogEventLevel.Information).Time("Init Program")) { CMain.Init(); Application.DoEvents(); // Init Language - CLog.LogInformation("Init Language"); + CLog.Information("Init Language"); using (Operation.At(LogEventLevel.Information).Time("Init Language")) { if (!CLanguage.Init()) @@ -121,7 +122,7 @@ namespace Vocaluxe Application.DoEvents(); // load config - CLog.LogInformation("Init Config"); + CLog.Information("Init Config"); using (Operation.At(LogEventLevel.Information).Time("Init Config")) { CConfig.LoadCommandLineParams(args); @@ -137,7 +138,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Draw - CLog.LogInformation("Init Draw"); + CLog.Information("Init Draw"); using (Operation.At(LogEventLevel.Information).Time("Init Draw")) { if (!CDraw.Init()) @@ -147,7 +148,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Playback - CLog.LogInformation("Init Playback"); + CLog.Information("Init Playback"); using (Operation.At(LogEventLevel.Information).Time("Init Playback")) { if (!CSound.Init()) @@ -157,7 +158,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Record - CLog.LogInformation("Init Record"); + CLog.Information("Init Record"); using (Operation.At(LogEventLevel.Information).Time("Init Record")) { if (!CRecord.Init()) @@ -167,7 +168,7 @@ namespace Vocaluxe Application.DoEvents(); // Init VideoDecoder - CLog.LogInformation("Init Videodecoder"); + CLog.Information("Init Videodecoder"); using (Operation.At(LogEventLevel.Information).Time("Init Videodecoder")) { if (!CVideo.Init()) @@ -177,7 +178,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Database - CLog.LogInformation("Init Database"); + CLog.Information("Init Database"); using (Operation.At(LogEventLevel.Information).Time("Init Database")) { if (!CDataBase.Init()) @@ -187,7 +188,7 @@ namespace Vocaluxe Application.DoEvents(); //Init Webcam - CLog.LogInformation("Init Webcam"); + CLog.Information("Init Webcam"); using (Operation.At(LogEventLevel.Information).Time("Init Webcam")) { if (!CWebcam.Init()) @@ -197,7 +198,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Background Music - CLog.LogInformation("Init Background Music"); + CLog.Information("Init Background Music"); using (Operation.At(LogEventLevel.Information).Time("Init Background Music")) { CBackgroundMusic.Init(); @@ -206,7 +207,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Profiles - CLog.LogInformation("Init Profiles"); + CLog.Information("Init Profiles"); using (Operation.At(LogEventLevel.Information).Time("Init Profiles")) { CProfiles.Init(); @@ -215,7 +216,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Fonts - CLog.LogInformation("Init Fonts"); + CLog.Information("Init Fonts"); using (Operation.At(LogEventLevel.Information).Time("Init Fonts")) { if (!CFonts.Init()) @@ -225,14 +226,14 @@ namespace Vocaluxe Application.DoEvents(); // Theme System - CLog.LogInformation("Init Theme"); + CLog.Information("Init Theme"); using (Operation.At(LogEventLevel.Information).Time("Init Theme")) { if (!CThemes.Init()) throw new CLoadingException("theme"); } - CLog.LogInformation("Load Theme"); + CLog.Information("Load Theme"); using (Operation.At(LogEventLevel.Information).Time("Load Theme")) { CThemes.Load(); @@ -241,7 +242,7 @@ namespace Vocaluxe Application.DoEvents(); // Load Cover - CLog.LogInformation("Init Cover"); + CLog.Information("Init Cover"); using (Operation.At(LogEventLevel.Information).Time("Init Cover")) { if (!CCover.Init()) @@ -251,7 +252,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Screens - CLog.LogInformation("Init Screens"); + CLog.Information("Init Screens"); using (Operation.At(LogEventLevel.Information).Time("Init Screens")) { CGraphics.Init(); @@ -260,7 +261,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Server - CLog.LogInformation("Init Server"); + CLog.Information("Init Server"); using (Operation.At(LogEventLevel.Information).Time("Init Server")) { CVocaluxeServer.Init(); @@ -269,7 +270,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Input - CLog.LogInformation("Init Input"); + CLog.Information("Init Input"); using (Operation.At(LogEventLevel.Information).Time("Init Input")) { CController.Init(); @@ -279,7 +280,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Game; - CLog.LogInformation("Init Game"); + CLog.Information("Init Game"); using (Operation.At(LogEventLevel.Information).Time("Init Game")) { CGame.Init(); @@ -290,7 +291,7 @@ namespace Vocaluxe Application.DoEvents(); // Init Party Modes; - CLog.LogInformation("Init Party Modes"); + CLog.Information("Init Party Modes"); using (Operation.At(LogEventLevel.Information).Time("Init Party Modes")) { if (!CParty.Init()) @@ -306,7 +307,7 @@ namespace Vocaluxe catch (Exception e) { MessageBox.Show("Error on start up: " + e.Message); - CLog.LogError("Error on start up: " + e); + CLog.Error("Error on start up: " + e); if (_SplashScreen != null) _SplashScreen.Close(); _CloseProgram(); @@ -330,7 +331,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CController)"); + CLog.Error(e, "Error during shutdown! (CController)"); } try @@ -339,7 +340,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CVocaluxeServer)"); + CLog.Error(e, "Error during shutdown! (CVocaluxeServer)"); } try @@ -348,7 +349,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CGraphics)"); + CLog.Error(e, "Error during shutdown! (CGraphics)"); } try @@ -357,7 +358,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CThemes)"); + CLog.Error(e, "Error during shutdown! (CThemes)"); } try @@ -366,7 +367,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CCover)"); + CLog.Error(e, "Error during shutdown! (CCover)"); } try @@ -375,7 +376,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CFonts)"); + CLog.Error(e, "Error during shutdown! (CFonts)"); } try @@ -384,7 +385,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CBackgroundMusic)"); + CLog.Error(e, "Error during shutdown! (CBackgroundMusic)"); } try @@ -393,7 +394,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CWebcam)"); + CLog.Error(e, "Error during shutdown! (CWebcam)"); } try @@ -402,7 +403,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CDataBase)"); + CLog.Error(e, "Error during shutdown! (CDataBase)"); } try @@ -411,7 +412,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CVideo)"); + CLog.Error(e, "Error during shutdown! (CVideo)"); } try @@ -420,7 +421,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CRecord)"); + CLog.Error(e, "Error during shutdown! (CRecord)"); } try @@ -429,7 +430,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CSound)"); + CLog.Error(e, "Error during shutdown! (CSound)"); } try @@ -438,7 +439,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError(e, "Error during shutdown! (CDraw)"); + CLog.Error(e, "Error during shutdown! (CDraw)"); } GC.Collect(); // Do a GC run here before we close logs to have finalizers run @@ -462,7 +463,7 @@ namespace Vocaluxe } catch {} MessageBox.Show("Unhandled exception: " + e.Message + stackTrace); - CLog.LogError("Unhandled exception: " + e.Message + stackTrace); + CLog.Error("Unhandled exception: " + e.Message + stackTrace); _CloseProgram(); } #endif @@ -498,7 +499,7 @@ namespace Vocaluxe } catch (Exception e) { - CLog.LogError("Cannot load assembly " + args.Name + " from " + path + ": " + e + "\r\nOuter Error: " + e1); + CLog.Error("Cannot load assembly " + args.Name + " from " + path + ": " + e + "\r\nOuter Error: " + e1); } } #if LINUX diff --git a/Vocaluxe/Screens/CScreenCredits.cs b/Vocaluxe/Screens/CScreenCredits.cs index 9ec2afed..824f25f4 100644 --- a/Vocaluxe/Screens/CScreenCredits.cs +++ b/Vocaluxe/Screens/CScreenCredits.cs @@ -23,6 +23,7 @@ using Vocaluxe.Base; using Vocaluxe.Base.Fonts; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Log; using VocaluxeLib.Menu; namespace Vocaluxe.Screens @@ -284,7 +285,7 @@ namespace Vocaluxe.Screens ressourceOK &= CDataBase.GetCreditsRessource("babene03.png", ref _TexNameBabene03); if (!ressourceOK) - CLog.LogError("Could not load all ressources!", true, true); + CLog.Fatal("Could not load all ressources!"); //Prepare Text int lastY = 280; diff --git a/Vocaluxe/Screens/CScreenLoad.cs b/Vocaluxe/Screens/CScreenLoad.cs index 61b9e39e..7ace4847 100644 --- a/Vocaluxe/Screens/CScreenLoad.cs +++ b/Vocaluxe/Screens/CScreenLoad.cs @@ -22,6 +22,7 @@ using Serilog.Events; using SerilogTimings; using Vocaluxe.Base; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.Menu; namespace Vocaluxe.Screens @@ -128,7 +129,7 @@ namespace Vocaluxe.Screens videoPlayer.PreLoad(); } - CLog.LogInformation("Load Songs Full"); + CLog.Information("Load Songs Full"); _TimerLoadSongsFull = Operation.At(LogEventLevel.Information).Time("Loaded Songs Full"); _SongLoaderThread = new Thread(CSongs.LoadSongs) {Name = "SongLoader", IsBackground = true}; @@ -187,7 +188,7 @@ namespace Vocaluxe.Screens _TimerLoadSongsFull.Dispose(); //Init Playlists - CLog.LogInformation("Init Playlists"); + CLog.Information("Init Playlists"); using (Operation.At(LogEventLevel.Information).Time("Init Playlists")) { CPlaylists.Init(); diff --git a/Vocaluxe/Screens/CScreenOptionsVideo.cs b/Vocaluxe/Screens/CScreenOptionsVideo.cs index 8c33e722..e06a755f 100644 --- a/Vocaluxe/Screens/CScreenOptionsVideo.cs +++ b/Vocaluxe/Screens/CScreenOptionsVideo.cs @@ -22,6 +22,7 @@ using Vocaluxe.Lib.Webcam; using VocaluxeLib; using VocaluxeLib.Menu; using VocaluxeLib.Draw; +using VocaluxeLib.Log; namespace Vocaluxe.Screens { @@ -205,7 +206,7 @@ namespace Vocaluxe.Screens } catch (Exception e) { - CLog.LogError("Error on listing webcam capabilities: " + e.Message); + CLog.Error("Error on listing webcam capabilities: " + e.Message); } _SelectSlides[_SelectSlideWebcamDevices].Visible = ssVisible; diff --git a/Vocaluxe/Screens/CScreenSing.cs b/Vocaluxe/Screens/CScreenSing.cs index d55e6bc3..e36dfbfd 100644 --- a/Vocaluxe/Screens/CScreenSing.cs +++ b/Vocaluxe/Screens/CScreenSing.cs @@ -27,6 +27,7 @@ using Vocaluxe.Base.Fonts; using Vocaluxe.Lib.Sound; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Log; using VocaluxeLib.Menu; using VocaluxeLib.Menu.SingNotes; using VocaluxeLib.Songs; @@ -1043,7 +1044,7 @@ namespace Vocaluxe.Screens if (song == null) { - CLog.LogError("Critical Error! ScreenSing.LoadNextSong() song is null!"); + CLog.Error("Critical Error! ScreenSing.LoadNextSong() song is null!"); return; } diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index 4b64c235..8b69d5f0 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -160,7 +160,7 @@ - ..\packages\Serilog.2.5.0\lib\net46\Serilog.dll + ..\packages\Serilog.2.6.0\lib\net46\Serilog.dll True @@ -176,7 +176,7 @@ True - ..\packages\Serilog.Sinks.File.3.1.0\lib\net45\Serilog.Sinks.File.dll + ..\packages\Serilog.Sinks.File.4.0.0\lib\net45\Serilog.Sinks.File.dll True @@ -249,7 +249,6 @@ - diff --git a/Vocaluxe/packages.config b/Vocaluxe/packages.config index 01998c23..1cb54eb4 100644 --- a/Vocaluxe/packages.config +++ b/Vocaluxe/packages.config @@ -1,10 +1,10 @@  - + - + diff --git a/VocaluxeLib/CBasic.cs b/VocaluxeLib/CBasic.cs index c100ea7b..a8f0484c 100644 --- a/VocaluxeLib/CBasic.cs +++ b/VocaluxeLib/CBasic.cs @@ -22,7 +22,6 @@ namespace VocaluxeLib public static IConfig Config; public static ISettings Settings; public static IThemes Themes; - public static ILog Log; public static IBackgroundMusic BackgroundMusic; public static IDrawing Drawing; public static IGraphics Graphics; @@ -39,14 +38,13 @@ namespace VocaluxeLib public static IControllers Controller; public static IPlaylist Playlist; - public static void Assign(IConfig config, ISettings settings, IThemes themes, ILog log, IBackgroundMusic backgroundMusic, + public static void Assign(IConfig config, ISettings settings, IThemes themes, IBackgroundMusic backgroundMusic, IDrawing draw, IGraphics graphics, IFonts fonts, ILanguage language, IGame game, IProfiles profiles, IRecording record, ISongs songs, IVideo video, ISound sound, ICover cover, IDataBase dataBase, IControllers controller, IPlaylist playlist) { Config = config; Settings = settings; Themes = themes; - Log = log; BackgroundMusic = backgroundMusic; Drawing = draw; Graphics = graphics; diff --git a/VocaluxeLib/CHelper.cs b/VocaluxeLib/CHelper.cs index 6c143b59..6c04fee7 100644 --- a/VocaluxeLib/CHelper.cs +++ b/VocaluxeLib/CHelper.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; +using VocaluxeLib.Log; namespace VocaluxeLib { @@ -297,7 +298,7 @@ namespace VocaluxeLib { if (!File.Exists(filePath)) { - CBase.Log.LogError("Can't find File: " + filePath); + CLog.Error("Can't find File: " + filePath); return null; } Bitmap bmp; @@ -307,7 +308,7 @@ namespace VocaluxeLib } catch (Exception) { - CBase.Log.LogError("Error loading bitmap: " + filePath); + CLog.Error("Error loading bitmap: " + filePath); return null; } return bmp; diff --git a/VocaluxeLib/IBasic.cs b/VocaluxeLib/IBasic.cs index d730db8b..e182273b 100644 --- a/VocaluxeLib/IBasic.cs +++ b/VocaluxeLib/IBasic.cs @@ -166,16 +166,6 @@ namespace VocaluxeLib IMenu GetScreen(EScreen screen); } - public interface ILog - { - void LogError(Exception exeption, string errorText, bool showMsg = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues); - void LogError(string errorText, bool showMsg = false, bool exit = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues); - void LogDebug(string text, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1, params object[] propertyValues); - void LogSongWarning(string text, params object[] propertyValues); - void LogSongError(Exception exception, string text, params object[] propertyValues); - void LogSongError(string text, params object[] propertyValues); - } - public interface IFonts { RectangleF GetTextBounds(CText text); diff --git a/VocaluxeLib/Log/CLog.cs b/VocaluxeLib/Log/CLog.cs new file mode 100644 index 00000000..00669bd7 --- /dev/null +++ b/VocaluxeLib/Log/CLog.cs @@ -0,0 +1,79 @@ +using System; +using System.IO; +using System.Text; +using Serilog; +using VocaluxeLib.Log.Enricher; +using VocaluxeLib.Log.Rolling; +using VocaluxeLib.Log.Serilog; + +namespace VocaluxeLib.Log +{ + public static partial class CLog + { + private static readonly StringBuilder _MainLogStringBuilder = new StringBuilder(); + private static readonly StringWriter _MainLogStringWriter = new StringWriter(_MainLogStringBuilder); + + private static ILogger _MainLog = new SilentLogger(); + private static ILogger _SongLog = new SilentLogger(); + private static string _LogFolder; + + private const string _MainLogTemplate = "[{TimeStampFromStart}] [{Level}] {Message}{NewLine}{Properties}{NewLine}{Exception}"; + private const string _SongLogTemplate = "{Message}{NewLine}Additional info:{Properties}{NewLine}{Exception}"; + + public static void Init(string logFolder, string fileNameMainLog, string fileNameSongInfoLog) + { + _LogFolder = logFolder; + if (!Directory.Exists(_LogFolder)) + Directory.CreateDirectory(_LogFolder); + + LogFileRoller.RollLogs(Path.Combine(_LogFolder, fileNameMainLog), 2); + LogFileRoller.RollLogs(Path.Combine(_LogFolder, fileNameSongInfoLog), 2); + + _MainLog = new LoggerConfiguration() + .Enrich.WithThreadId() + .Enrich.FromLogContext() + .Enrich.WithTimeStampFromStart() + .WriteTo.TextWriter(_MainLogStringWriter, outputTemplate: _MainLogTemplate) + // Json can be activated by adding "new CompactJsonFormatter()" as first argument + .WriteTo.File(Path.Combine(_LogFolder, fileNameMainLog), flushToDiskInterval: TimeSpan.FromSeconds(30), outputTemplate: _MainLogTemplate) +#if DEBUG + .WriteTo.Console(outputTemplate: _MainLogTemplate) +#endif + .CreateLogger(); + + _SongLog = new LoggerConfiguration() + .WriteTo.File(Path.Combine(_LogFolder, fileNameSongInfoLog), flushToDiskInterval: TimeSpan.FromSeconds(60), outputTemplate: _SongLogTemplate) +#if DEBUG + .WriteTo.Console(outputTemplate: "[SongInfo] " + _SongLogTemplate) +#endif + .CreateLogger(); + } + + public static void Close() + { + if (!(_MainLog is SilentLogger)) + { + ILogger loggerToDispose = _MainLog; + + _MainLog = new SilentLogger(); + (_MainLog as IDisposable)?.Dispose(); + } + + if (!(_SongLog is SilentLogger)) + { + ILogger loggerToDispose = _MainLog; + + _SongLog = new SilentLogger(); + (_SongLog as IDisposable)?.Dispose(); + } + + } + + private static void _ShowLogAssistent(string messageTemplate, object[] propertyValues, string callerMethodeName = "", string callerFilePath = "", int callerLineNumer = -1) + { + //Todo: Logsend assistent + //MessageBox.Show(messageTemplate, "Vocaluxe", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + } +} diff --git a/VocaluxeLib/Log/CLogFunctions.cs b/VocaluxeLib/Log/CLogFunctions.cs new file mode 100644 index 00000000..cf311403 --- /dev/null +++ b/VocaluxeLib/Log/CLogFunctions.cs @@ -0,0 +1,959 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using Serilog.Context; +using Serilog.Core; + +namespace VocaluxeLib.Log +{ + public static partial class CLog + { + + public static object[] Params(params object[] propertyValues) + { + return propertyValues; + } + + + #region Verbose functions + + /// + /// Write an event with the Verbose level to the Verbose log. + /// + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Verbose(string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Verbose(messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Verbose level with additional propertyValues to the Verbose log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Verbose(string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Verbose(messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Verbose level and associated exception to the Verbose log. + /// + /// Exception of this event. + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Verbose(Exception exception, string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Verbose( exception, messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Verbose level with additional propertyValues and associated exception to the Verbose log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Verbose(Exception exception, string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Verbose(exception, messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + #endregion + + #region Debug functions + + /// + /// Write an event with the Debug level to the Debug log. + /// + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Debug(string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Debug(messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Debug level with additional propertyValues to the Debug log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Debug(string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Debug(messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Debug level and associated exception to the Debug log. + /// + /// Exception of this event. + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Debug(Exception exception, string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Debug( exception, messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Debug level with additional propertyValues and associated exception to the Debug log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Debug(Exception exception, string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Debug(exception, messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + #endregion + + #region Information functions + + /// + /// Write an event with the Information level to the Information log. + /// + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Information(string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Information(messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Information level with additional propertyValues to the Information log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Information(string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Information(messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Information level and associated exception to the Information log. + /// + /// Exception of this event. + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Information(Exception exception, string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Information( exception, messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Information level with additional propertyValues and associated exception to the Information log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Information(Exception exception, string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Information(exception, messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + #endregion + + #region Warning functions + + /// + /// Write an event with the Warning level to the Warning log. + /// + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Warning(string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Warning(messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Warning level with additional propertyValues to the Warning log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Warning(string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Warning(messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Warning level and associated exception to the Warning log. + /// + /// Exception of this event. + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Warning(Exception exception, string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Warning( exception, messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Warning level with additional propertyValues and associated exception to the Warning log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Warning(Exception exception, string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Warning(exception, messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + #endregion + + #region Error functions + + /// + /// Write an event with the Error level to the Error log. + /// + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Error(string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Error(messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Error level with additional propertyValues to the Error log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Error(string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Error(messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Error level and associated exception to the Error log. + /// + /// Exception of this event. + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Error(Exception exception, string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Error( exception, messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Error level with additional propertyValues and associated exception to the Error log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Error(Exception exception, string messageTemplate, object[] propertyValues, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Error(exception, messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + #endregion + + #region Fatal functions + + /// + /// Write an event with the Fatal level to the Fatal log. + /// + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Fatal(string messageTemplate, bool show = true, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Fatal(messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Fatal level with additional propertyValues to the Fatal log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Fatal(string messageTemplate, object[] propertyValues, bool show = true, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Fatal(messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Fatal level and associated exception to the Fatal log. + /// + /// Exception of this event. + /// Message template for this event. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Fatal(Exception exception, string messageTemplate, bool show = true, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Fatal( exception, messageTemplate); + } + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + } + + /// + /// Write an event with the Fatal level with additional propertyValues and associated exception to the Fatal log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// True if an message should be shown to the user, false otherwise. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Fatal(Exception exception, string messageTemplate, object[] propertyValues, bool show = true, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _MainLog.Fatal(exception, messageTemplate, propertyValues); + } + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + } + + #endregion + + [SuppressMessage("ReSharper", "MemberHidesStaticFromOuterClass")] + public static class CSongLog + { + + #region Verbose functions + + /// + /// Write an event with the Verbose level to the Verbose log. + /// + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Verbose(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Verbose(messageTemplate); + } + } + + /// + /// Write an event with the Verbose level with additional propertyValues to the Verbose log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Verbose(string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Verbose(messageTemplate, propertyValues); + } + } + + /// + /// Write an event with the Verbose level and associated exception to the Verbose log. + /// + /// Exception of this event. + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Verbose(Exception exception, string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Verbose( exception, messageTemplate); + } + } + + /// + /// Write an event with the Verbose level with additional propertyValues and associated exception to the Verbose log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Verbose(Exception exception, string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Verbose(exception, messageTemplate, propertyValues); + } + } + + #endregion + + #region Debug functions + + /// + /// Write an event with the Debug level to the Debug log. + /// + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Debug(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Debug(messageTemplate); + } + } + + /// + /// Write an event with the Debug level with additional propertyValues to the Debug log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Debug(string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Debug(messageTemplate, propertyValues); + } + } + + /// + /// Write an event with the Debug level and associated exception to the Debug log. + /// + /// Exception of this event. + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Debug(Exception exception, string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Debug( exception, messageTemplate); + } + } + + /// + /// Write an event with the Debug level with additional propertyValues and associated exception to the Debug log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Debug(Exception exception, string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Debug(exception, messageTemplate, propertyValues); + } + } + + #endregion + + #region Information functions + + /// + /// Write an event with the Information level to the Information log. + /// + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Information(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Information(messageTemplate); + } + } + + /// + /// Write an event with the Information level with additional propertyValues to the Information log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Information(string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Information(messageTemplate, propertyValues); + } + } + + /// + /// Write an event with the Information level and associated exception to the Information log. + /// + /// Exception of this event. + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Information(Exception exception, string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Information( exception, messageTemplate); + } + } + + /// + /// Write an event with the Information level with additional propertyValues and associated exception to the Information log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Information(Exception exception, string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Information(exception, messageTemplate, propertyValues); + } + } + + #endregion + + #region Warning functions + + /// + /// Write an event with the Warning level to the Warning log. + /// + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Warning(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Warning(messageTemplate); + } + } + + /// + /// Write an event with the Warning level with additional propertyValues to the Warning log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Warning(string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Warning(messageTemplate, propertyValues); + } + } + + /// + /// Write an event with the Warning level and associated exception to the Warning log. + /// + /// Exception of this event. + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Warning(Exception exception, string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Warning( exception, messageTemplate); + } + } + + /// + /// Write an event with the Warning level with additional propertyValues and associated exception to the Warning log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Warning(Exception exception, string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Warning(exception, messageTemplate, propertyValues); + } + } + + #endregion + + #region Error functions + + /// + /// Write an event with the Error level to the Error log. + /// + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Error(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Error(messageTemplate); + } + } + + /// + /// Write an event with the Error level with additional propertyValues to the Error log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Error(string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Error(messageTemplate, propertyValues); + } + } + + /// + /// Write an event with the Error level and associated exception to the Error log. + /// + /// Exception of this event. + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Error(Exception exception, string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Error( exception, messageTemplate); + } + } + + /// + /// Write an event with the Error level with additional propertyValues and associated exception to the Error log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Error(Exception exception, string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Error(exception, messageTemplate, propertyValues); + } + } + + #endregion + + #region Fatal functions + + /// + /// Write an event with the Fatal level to the Fatal log. + /// + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Fatal(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Fatal(messageTemplate); + } + } + + /// + /// Write an event with the Fatal level with additional propertyValues to the Fatal log. + /// + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Fatal(string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Fatal(messageTemplate, propertyValues); + } + } + + /// + /// Write an event with the Fatal level and associated exception to the Fatal log. + /// + /// Exception of this event. + /// Message template for this event. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Fatal(Exception exception, string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Fatal( exception, messageTemplate); + } + } + + /// + /// Write an event with the Fatal level with additional propertyValues and associated exception to the Fatal log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void Fatal(Exception exception, string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + _SongLog.Fatal(exception, messageTemplate, propertyValues); + } + } + + #endregion + } + } +} \ No newline at end of file diff --git a/VocaluxeLib/Log/CLogFunctions.tt b/VocaluxeLib/Log/CLogFunctions.tt new file mode 100644 index 00000000..2fe5ec71 --- /dev/null +++ b/VocaluxeLib/Log/CLogFunctions.tt @@ -0,0 +1,139 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using Serilog.Context; +using Serilog.Core; + +namespace VocaluxeLib.Log +{ + public static partial class CLog + { + + public static object[] Params(params object[] propertyValues) + { + return propertyValues; + } + +<# foreach (var Log in new[]{"Main","Song"}) { #> +<# if(Log == "Song") { #> +<# PushIndent(" "); #> + + [SuppressMessage("ReSharper", "MemberHidesStaticFromOuterClass")] + public static class CSongLog + { +<# } #> +<# foreach (var LogLevel in new[]{"Verbose","Debug", "Information", "Warning", "Error", "Fatal"}) { #> + + #region <#= LogLevel #> functions + + /// + /// Write an event with the <#= LogLevel #> level to the <#= LogLevel #> log. + /// + /// Message template for this event. +<# if(Log == "Main") { #> + /// True if an message should be shown to the user, false otherwise. +<# } #> + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void <#= LogLevel #>(string messageTemplate, <#= Log == "Main"?(LogLevel == "Fatal"?"bool show = true, ":"bool show = false, "):"" #>[CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>(messageTemplate); + } +<# if(Log == "Main") { #> + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); +<# } #> + } + + /// + /// Write an event with the <#= LogLevel #> level with additional propertyValues to the <#= LogLevel #> log. + /// + /// Message template for this event. + /// Data inserted into the message template. +<# if(Log == "Main") { #> + /// True if an message should be shown to the user, false otherwise. +<# } #> + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void <#= LogLevel #>(string messageTemplate, object[] propertyValues, <#= Log == "Main"?(LogLevel == "Fatal"?"bool show = true, ":"bool show = false, "):"" #>[CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>(messageTemplate, propertyValues); + } +<# if(Log == "Main") { #> + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); +<# } #> + } + + /// + /// Write an event with the <#= LogLevel #> level and associated exception to the <#= LogLevel #> log. + /// + /// Exception of this event. + /// Message template for this event. +<# if(Log == "Main") { #> + /// True if an message should be shown to the user, false otherwise. +<# } #> + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void <#= LogLevel #>(Exception exception, string messageTemplate, <#= Log == "Main"?(LogLevel == "Fatal"?"bool show = true, ":"bool show = false, "):"" #>[CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>( exception, messageTemplate); + } +<# if(Log == "Main") { #> + if(show) + _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); +<# } #> + } + + /// + /// Write an event with the <#= LogLevel #> level with additional propertyValues and associated exception to the <#= LogLevel #> log. + /// + /// Exception of this event. + /// Message template for this event. + /// Data inserted into the message template. +<# if(Log == "Main") { #> + /// True if an message should be shown to the user, false otherwise. +<# } #> + /// Don't use! The methode name of the caller will be filled automatically by the compiler. + /// Don't use! The filepath of the caller will be filled automatically by the compiler. + /// Don't use! The line number of the caller will be filled automatically by the compiler. + [MessageTemplateFormatMethod("messageTemplate")] + public static void <#= LogLevel #>(Exception exception, string messageTemplate, object[] propertyValues, <#= Log == "Main"?(LogLevel == "Fatal"?"bool show = true, ":"bool show = false, "):"" #>[CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) + { + using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) + { + <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>(exception, messageTemplate, propertyValues); + } +<# if(Log == "Main") { #> + if(show) + _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); +<# } #> + } + + #endregion +<# } #> +<# if(Log == "Song") { #> +<# PopIndent(); #> + } +<# } #> +<# } #> + } +} \ No newline at end of file diff --git a/VocaluxeLib/Log/Enricher/CTimeStampFromStartEnricher.cs b/VocaluxeLib/Log/Enricher/CTimeStampFromStartEnricher.cs new file mode 100644 index 00000000..4b924dba --- /dev/null +++ b/VocaluxeLib/Log/Enricher/CTimeStampFromStartEnricher.cs @@ -0,0 +1,28 @@ +using System; +using Serilog; +using Serilog.Configuration; +using Serilog.Core; +using Serilog.Events; + +namespace VocaluxeLib.Log.Enricher +{ + public static class WithTimeStampFromStartExtension + { + // ReSharper disable once InconsistentNaming + public static LoggerConfiguration WithTimeStampFromStart(this LoggerEnrichmentConfiguration enrich) + { + return enrich.With(new CTimeStampFromStartEnricher()); + } + + private class CTimeStampFromStartEnricher : ILogEventEnricher + { + private readonly DateTimeOffset _Start = DateTimeOffset.Now; + + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory lepf) + { + logEvent.AddPropertyIfAbsent( + lepf.CreateProperty("TimeStampFromStart", logEvent.Timestamp - _Start)); + } + } + } +} diff --git a/VocaluxeLib/Log/Rolling/LogRoller.cs b/VocaluxeLib/Log/Rolling/LogRoller.cs new file mode 100644 index 00000000..adb69ab5 --- /dev/null +++ b/VocaluxeLib/Log/Rolling/LogRoller.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; + +namespace VocaluxeLib.Log.Rolling +{ + static class LogFileRoller + { + public static void RollLogs(string mainLogFile, int numLogsToKeep) + { + _DeleteOldLogs(mainLogFile, numLogsToKeep); + + string currentSourceFile = _GenerateRollLogFileName(mainLogFile, numLogsToKeep); + + + for (var i = numLogsToKeep; i > 1; i--) + { + string currentTargetFile = currentSourceFile; + currentSourceFile = _GenerateRollLogFileName(mainLogFile, i - 1); + + if (File.Exists(currentSourceFile)) + { + try + { + File.Move(currentSourceFile, currentTargetFile); + } + catch (Exception) + { + // Cant log anything here as the log isn't initialized yet + } + } + } + + if (File.Exists(mainLogFile)) + { + try + { + if (numLogsToKeep > 0) + File.Move(mainLogFile, currentSourceFile); + else + File.Delete(mainLogFile); + } + catch (Exception) + { + // Cant log anything here as the log isn't initialized yet + } + } + } + + private static void _DeleteOldLogs(string mainLogFile, int numLogsToKeep) + { + Regex r = new Regex($"{Path.GetDirectoryName(mainLogFile)}\\\\{Path.GetFileNameWithoutExtension(mainLogFile)}_([0-9]+){Path.GetExtension(mainLogFile)}", RegexOptions.IgnoreCase); + IEnumerable logFiles = Directory.EnumerateFiles($"{Path.GetDirectoryName(mainLogFile)}", $"{Path.GetFileNameWithoutExtension(mainLogFile)}*{Path.GetExtension(mainLogFile)}"); + foreach (string file in logFiles) + { + Match m = r.Match(file); + if (m.Success) + { + int logNum = -1; + Int32.TryParse(m.Groups[1].Value, out logNum); + + if (logNum < 0 || logNum > numLogsToKeep - 1) + { + try + { + File.Delete(file); + } + catch (Exception) + { + // Cant log anything here as the log isn't initialized yet + } + + } + } + } + } + + private static string _GenerateRollLogFileName(string mainLogFile, int number) + { + return $"{Path.GetDirectoryName(mainLogFile)}\\{Path.GetFileNameWithoutExtension(mainLogFile)}_{number}{Path.GetExtension(mainLogFile)}"; + } + } +} diff --git a/VocaluxeLib/Log/Serilog/SilentLogger.cs b/VocaluxeLib/Log/Serilog/SilentLogger.cs new file mode 100644 index 00000000..475a530f --- /dev/null +++ b/VocaluxeLib/Log/Serilog/SilentLogger.cs @@ -0,0 +1,355 @@ +// From https://github.com/serilog/serilog/blob/4736691b7a0cd574e178c24a61caa8a9196a6a61/src/Serilog/Core/Pipeline/SilentLogger.cs + +// Copyright 2013-2015 Serilog Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using Serilog; +using Serilog.Core; +using Serilog.Events; + +namespace VocaluxeLib.Log.Serilog +{ + class SilentLogger : ILogger + { + public ILogger ForContext(ILogEventEnricher enricher) + { + return this; + } + + public ILogger ForContext(IEnumerable enrichers) + { + return this; + } + + public ILogger ForContext(string propertyName, object value, bool destructureObjects = false) + { + return this; + } + + public ILogger ForContext() + { + return this; + } + + public ILogger ForContext(Type source) + { + return this; + } + + public void Write(LogEvent logEvent) + { + } + + public void Write(LogEventLevel level, string messageTemplate) + { + } + + public void Write(LogEventLevel level, string messageTemplate, T propertyValue) + { + } + + public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Write(LogEventLevel level, string messageTemplate, params object[] propertyValues) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public bool IsEnabled(LogEventLevel level) + { + return false; + } + + public void Verbose(string messageTemplate) + { + } + + public void Verbose(string messageTemplate, T propertyValue) + { + } + + public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Verbose(string messageTemplate, params object[] propertyValues) + { + } + + public void Verbose(Exception exception, string messageTemplate) + { + } + + public void Verbose(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Verbose(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Verbose(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Verbose(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Debug(string messageTemplate) + { + } + + public void Debug(string messageTemplate, T propertyValue) + { + } + + public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Debug(string messageTemplate, params object[] propertyValues) + { + } + + public void Debug(Exception exception, string messageTemplate) + { + } + + public void Debug(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Debug(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Debug(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Debug(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Information(string messageTemplate) + { + } + + public void Information(string messageTemplate, T propertyValue) + { + } + + public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Information(string messageTemplate, params object[] propertyValues) + { + } + + public void Information(Exception exception, string messageTemplate) + { + } + + public void Information(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Information(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Information(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Information(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Warning(string messageTemplate) + { + } + + public void Warning(string messageTemplate, T propertyValue) + { + } + + public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Warning(string messageTemplate, params object[] propertyValues) + { + } + + public void Warning(Exception exception, string messageTemplate) + { + } + + public void Warning(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Warning(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Warning(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Warning(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Error(string messageTemplate) + { + } + + public void Error(string messageTemplate, T propertyValue) + { + } + + public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Error(string messageTemplate, params object[] propertyValues) + { + } + + public void Error(Exception exception, string messageTemplate) + { + } + + public void Error(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Error(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Error(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Error(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Fatal(string messageTemplate) + { + } + + public void Fatal(string messageTemplate, T propertyValue) + { + } + + public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Fatal(string messageTemplate, params object[] propertyValues) + { + } + + public void Fatal(Exception exception, string messageTemplate) + { + } + + public void Fatal(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Fatal(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Fatal(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Fatal(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + [MessageTemplateFormatMethod("messageTemplate")] + public bool BindMessageTemplate(string messageTemplate, object[] propertyValues, out MessageTemplate parsedTemplate, out IEnumerable boundProperties) + { + parsedTemplate = null; + boundProperties = null; + return false; + } + + public bool BindProperty(string propertyName, object value, bool destructureObjects, out LogEventProperty property) + { + property = null; + return false; + } + } +} \ No newline at end of file diff --git a/VocaluxeLib/Menu/CMenu.cs b/VocaluxeLib/Menu/CMenu.cs index 3ea5af46..8c82185a 100644 --- a/VocaluxeLib/Menu/CMenu.cs +++ b/VocaluxeLib/Menu/CMenu.cs @@ -22,6 +22,7 @@ using System.Linq; using System.Windows.Forms; using System.Xml.Serialization; using VocaluxeLib.Draw; +using VocaluxeLib.Log; using VocaluxeLib.Menu.SingNotes; using VocaluxeLib.Menu.SongMenu; using VocaluxeLib.Xml; @@ -224,22 +225,14 @@ namespace VocaluxeLib.Menu msg += "the file is for newer program versions! "; msg += "Current screen version is " + _ScreenVersion; - CBase.Log.LogError(msg); + CLog.Error(msg); } foreach (IThemeable el in _Elements.Select(_GetElement).OfType()) el.LoadSkin(); } - catch (InvalidOperationException) - { - CBase.Log.LogError("Error while reading " + ThemeName + ".xml", true, true); - } - catch (CXmlException e) - { - CBase.Log.LogError("Error while reading " + ThemeName + ".xml: " + e, true, true); - } catch (Exception e) { - CBase.Log.LogError(e.Message + e.StackTrace, true, true); + CLog.Fatal(e, "Error while reading {ThemeName}.xml", CLog.Params(ThemeName), true); } } @@ -268,7 +261,7 @@ namespace VocaluxeLib.Menu } catch (Exception e) { - CBase.Log.LogError("Error while saving theme-file: " + ThemeName + " " + e.Message, true); + CLog.Error("Error while saving theme-file: " + ThemeName + " " + e.Message, true); } } diff --git a/VocaluxeLib/Menu/COrderedDictionaryLite.cs b/VocaluxeLib/Menu/COrderedDictionaryLite.cs index 3bb7d995..da8a2cbe 100644 --- a/VocaluxeLib/Menu/COrderedDictionaryLite.cs +++ b/VocaluxeLib/Menu/COrderedDictionaryLite.cs @@ -18,6 +18,7 @@ using System; using System.Collections; using System.Collections.Generic; +using VocaluxeLib.Log; namespace VocaluxeLib.Menu { @@ -57,7 +58,7 @@ namespace VocaluxeLib.Menu } catch (Exception) { - CBase.Log.LogError("Can't find " + typeof(T).Name.Substring(1) + " Element \"" + key + "\" in " + _ParentName); + CLog.Error("Can't find " + typeof(T).Name.Substring(1) + " Element \"" + key + "\" in " + _ParentName); throw; } } diff --git a/VocaluxeLib/Profile/CProfile.cs b/VocaluxeLib/Profile/CProfile.cs index 5f4de009..1f2e60ea 100644 --- a/VocaluxeLib/Profile/CProfile.cs +++ b/VocaluxeLib/Profile/CProfile.cs @@ -19,6 +19,7 @@ using System; using System.ComponentModel; using System.IO; using System.Xml.Serialization; +using VocaluxeLib.Log; using VocaluxeLib.Xml; namespace VocaluxeLib.Profile @@ -67,7 +68,7 @@ namespace VocaluxeLib.Profile { Avatar = CBase.Profiles.GetAvatarByFilename(value); if (Avatar == null) - CBase.Log.LogError("Avatar '" + value + "' not found"); + CLog.Error("Avatar '" + value + "' not found"); } } @@ -104,7 +105,7 @@ namespace VocaluxeLib.Profile { if (_ConvertProfile(ref e)) return true; - CBase.Log.LogError("Error loading profile file " + Path.GetFileName(FilePath) + ": " + e.Message); + CLog.Error("Error loading profile file " + Path.GetFileName(FilePath) + ": " + e.Message); return false; } return true; diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index 46fe2267..fb9c033e 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -21,6 +21,7 @@ using System.IO; using System.Linq; using System.Text; using VocaluxeLib.Draw; +using VocaluxeLib.Log; namespace VocaluxeLib.Songs { @@ -350,7 +351,7 @@ namespace VocaluxeLib.Songs for (int i = 0; i < Notes.VoiceCount; i++) { if (!Notes.VoiceNames.IsSet(i)) - CBase.Log.LogError("Warning: Can't find #P" + (i + 1) + "-tag for duets in \"" + Artist + " - " + Title + "\"."); + CLog.Error("Warning: Can't find #P" + (i + 1) + "-tag for duets in \"" + Artist + " - " + Title + "\"."); } } diff --git a/VocaluxeLib/Songs/CSongLoader.cs b/VocaluxeLib/Songs/CSongLoader.cs index 17a7bc7a..99702bc8 100644 --- a/VocaluxeLib/Songs/CSongLoader.cs +++ b/VocaluxeLib/Songs/CSongLoader.cs @@ -19,6 +19,7 @@ using System; using System.IO; using System.Linq; using System.Text; +using VocaluxeLib.Log; namespace VocaluxeLib.Songs { @@ -121,8 +122,7 @@ namespace VocaluxeLib.Songs if (value == "") { _Song.UnknownTags.Add(line); - CBase.Log.LogSongWarning("[{SongFileName}] Empty value skipped", _Song.FileName); - + CLog.CSongLog.Warning("[{SongFileName}] Empty value skipped", CLog.Params(_Song.FileName)); continue; } @@ -135,7 +135,7 @@ namespace VocaluxeLib.Songs { if (useSetEncoding) { - CBase.Log.LogSongWarning("[{SongFileName}] Duplicate encoding ignored", _Song.FileName); + CLog.CSongLog.Warning("[{SongFileName}] Duplicate encoding ignored", CLog.Params(_Song.FileName)); continue; } sr.Dispose(); @@ -181,7 +181,7 @@ namespace VocaluxeLib.Songs } else { - CBase.Log.LogSongError("[{SongFileName}] Can't find audio file: {AudioFile}", _Song.FileName, Path.Combine(_Song.Folder, value)); + CLog.CSongLog.Error("[{SongFileName}] Can't find audio file: {AudioFile}", CLog.Params(_Song.FileName, Path.Combine(_Song.Folder, value))); return false; } break; @@ -192,19 +192,19 @@ namespace VocaluxeLib.Songs headerFlags |= EHeaderFlags.BPM; } else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid BPM value: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid BPM value: {Value}", CLog.Params(_Song.FileName, value)); break; case "EDITION": if (value.Length > 1) _Song.Editions.Add(value); else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid edition: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid edition: {Value}", CLog.Params(_Song.FileName, value)); break; case "GENRE": if (value.Length > 1) _Song.Genres.Add(value); else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid genre: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid genre: {Value}", CLog.Params(_Song.FileName, value)); break; case "ALBUM": _Song.Album = value; @@ -214,13 +214,13 @@ namespace VocaluxeLib.Songs if (value.Length == 4 && int.TryParse(value, out num) && num > 0) _Song.Year = value; else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid year: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid year: {Value}", CLog.Params(_Song.FileName, value)); break; case "LANGUAGE": if (value.Length > 1) _Song.Languages.Add(_UnifyLanguage(value)); else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid language: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid language: {Value}", CLog.Params(_Song.FileName, value)); break; case "COMMENT": if (!String.IsNullOrEmpty(_Song._Comment)) @@ -231,49 +231,49 @@ namespace VocaluxeLib.Songs if (CHelper.TryParse(value, out _Song.Gap)) _Song.Gap /= 1000f; else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid gap: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid gap: {Value}", CLog.Params(_Song.FileName, value)); break; case "COVER": if (File.Exists(Path.Combine(_Song.Folder, value))) _Song.CoverFileName = value; else - CBase.Log.LogSongWarning("[{SongFileName}] Can't find cover file: {MissingFile}", _Song.FileName, Path.Combine(_Song.Folder, value)); + CLog.CSongLog.Warning("[{SongFileName}] Can't find cover file: {MissingFile}", CLog.Params(_Song.FileName, Path.Combine(_Song.Folder, value))); break; case "BACKGROUND": if (File.Exists(Path.Combine(_Song.Folder, value))) _Song.BackgroundFileNames.Add(value); else - CBase.Log.LogSongWarning("[{SongFileName}] Can't find background file: {MissingFile}", _Song.FileName, Path.Combine(_Song.Folder, value)); + CLog.CSongLog.Warning("[{SongFileName}] Can't find background file: {MissingFile}", CLog.Params(_Song.FileName, Path.Combine(_Song.Folder, value))); break; case "VIDEO": if (File.Exists(Path.Combine(_Song.Folder, value))) _Song.VideoFileName = value; else - CBase.Log.LogSongWarning("[{SongFileName}] Can't find video file: {MissingFile}", _Song.FileName, Path.Combine(_Song.Folder, value)); + CLog.CSongLog.Warning("[{SongFileName}] Can't find video file: {MissingFile}", CLog.Params(_Song.FileName, Path.Combine(_Song.Folder, value))); break; case "VIDEOGAP": if (!CHelper.TryParse(value, out _Song.VideoGap)) - CBase.Log.LogSongWarning("[{SongFileName}] Invalid videogap: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid videogap: {Value}", CLog.Params(_Song.FileName, value)); break; case "VIDEOASPECT": if (!CHelper.TryParse(value, out _Song.VideoAspect, true)) - CBase.Log.LogSongWarning("[{SongFileName}] Invalid videoaspect: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid videoaspect: {Value}", CLog.Params(_Song.FileName, value)); break; case "START": if (!CHelper.TryParse(value, out _Song.Start)) - CBase.Log.LogSongWarning("[{SongFileName}] Invalid start: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid start: {Value}", CLog.Params(_Song.FileName, value)); break; case "END": if (CHelper.TryParse(value, out _Song.Finish)) _Song.Finish /= 1000f; else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid end: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid end: {Value}", CLog.Params(_Song.FileName, value)); break; case "PREVIEWSTART": if (CHelper.TryParse(value, out _Song.Preview.StartTime) && _Song.Preview.StartTime >= 0f) _Song.Preview.Source = EDataSource.Tag; else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid previewstart: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid previewstart: {Value}", CLog.Params(_Song.FileName, value)); break; case "PREVIEW": if (CHelper.TryParse(value, out _Song.Preview.StartTime) && _Song.Preview.StartTime >= 0f) @@ -283,19 +283,19 @@ namespace VocaluxeLib.Songs _Song.Preview.Source = EDataSource.Tag; } else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid previewstart: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid previewstart: {Value}", CLog.Params(_Song.FileName, value)); break; case "MEDLEYSTARTBEAT": if (int.TryParse(value, out _Song.Medley.StartBeat)) headerFlags |= EHeaderFlags.MedleyStartBeat; else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid medleystartbeat: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid medleystartbeat: {Value}", CLog.Params(_Song.FileName, value)); break; case "MEDLEYENDBEAT": if (int.TryParse(value, out _Song.Medley.EndBeat)) headerFlags |= EHeaderFlags.MedleyEndBeat; else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid medleyendbeat: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid medleyendbeat: {Value}", CLog.Params(_Song.FileName, value)); break; case "ENDSHORT": if ((headerFlags & EHeaderFlags.BPM) != 0) @@ -307,7 +307,7 @@ namespace VocaluxeLib.Songs _Song.ShortEnd.Source = EDataSource.Tag; } else - CBase.Log.LogSongWarning("[{SongFileName}] Invalid shortendbeat: {Value}", _Song.FileName, value); + CLog.CSongLog.Warning("[{SongFileName}] Invalid shortendbeat: {Value}", CLog.Params(_Song.FileName, value)); } break; case "CALCMEDLEY": @@ -342,7 +342,7 @@ namespace VocaluxeLib.Songs else { _Song.UnknownTags.Add(line); - CBase.Log.LogSongWarning("[{SongFileName}] Unknown tag: #{Identifier}", _Song.FileName, identifier); + CLog.CSongLog.Warning("[{SongFileName}] Unknown tag: #{Identifier}", CLog.Params(_Song.FileName, identifier)); } break; @@ -352,32 +352,32 @@ namespace VocaluxeLib.Songs if (sr.EndOfStream) { //No other data then header - CBase.Log.LogSongError("[{SongFileName}] Lyrics/Notes missing", _Song.FileName); + CLog.CSongLog.Error("[{SongFileName}] Lyrics/Notes missing", CLog.Params(_Song.FileName)); return false; } if ((headerFlags & EHeaderFlags.Title) == 0) { - CBase.Log.LogSongError("[{SongFileName}] Title tag missing", _Song.FileName); + CLog.CSongLog.Error("[{SongFileName}] Title tag missing", CLog.Params(_Song.FileName)); return false; } if ((headerFlags & EHeaderFlags.Artist) == 0) { - CBase.Log.LogSongError("[{SongFileName}] Artist tag missing", _Song.FileName); + CLog.CSongLog.Error("[{SongFileName}] Artist tag missing", CLog.Params(_Song.FileName)); return false; } if ((headerFlags & EHeaderFlags.MP3) == 0) { - CBase.Log.LogSongError("[{SongFileName}] MP3 tag missing", _Song.FileName); + CLog.CSongLog.Error("[{SongFileName}] MP3 tag missing", CLog.Params(_Song.FileName)); return false; } if ((headerFlags & EHeaderFlags.BPM) == 0) { - CBase.Log.LogSongError("[{SongFileName}] BPM tag missing", _Song.FileName); + CLog.CSongLog.Error("[{SongFileName}] BPM tag missing", CLog.Params(_Song.FileName)); return false; } @@ -386,7 +386,7 @@ namespace VocaluxeLib.Songs { if (_Song.Medley.StartBeat > _Song.Medley.EndBeat) { - CBase.Log.LogSongError("[{SongFileName}] MedleyStartBeat is bigger than MedleyEndBeat in file: {StartBeat} > {EndBeat}", _Song.FileName, _Song.Medley.StartBeat > _Song.Medley.EndBeat); + CLog.CSongLog.Error("[{SongFileName}] MedleyStartBeat is bigger than MedleyEndBeat in file: {StartBeat} > {EndBeat}", CLog.Params(_Song.FileName, _Song.Medley.StartBeat > _Song.Medley.EndBeat)); headerFlags = headerFlags - EHeaderFlags.MedleyStartBeat - EHeaderFlags.MedleyEndBeat; } } @@ -412,7 +412,7 @@ namespace VocaluxeLib.Songs { if (sr != null) sr.Dispose(); - CBase.Log.LogSongError(e, "[{SongFileName}] Error reading txt header with Message: {ExceptionMessage}", e.Message, _Song.FileName); + CLog.CSongLog.Error(e, "[{SongFileName}] Error reading txt header with Message: {ExceptionMessage}", CLog.Params(e.Message, _Song.FileName)); return false; } _Song.Encoding = sr.CurrentEncoding; @@ -509,7 +509,7 @@ namespace VocaluxeLib.Songs if (!File.Exists(filePath)) { - CBase.Log.LogSongError("[{SongFileName}] The file songfile does not exist", _Song.FileName); + CLog.CSongLog.Error("[{SongFileName}] The file songfile does not exist", CLog.Params(_Song.FileName)); return false; } @@ -558,7 +558,7 @@ namespace VocaluxeLib.Songs if (!int.TryParse(line, out player)) { - CBase.Log.LogSongError("[{SongFileName}] Wrong or missing number after \"P\" (in line {LineNr})", _Song.FileName, _LineNr); + CLog.CSongLog.Error("[{SongFileName}] Wrong or missing number after \"P\" (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); return false; } currentBeat = 0; @@ -572,25 +572,25 @@ namespace VocaluxeLib.Songs { if (noteData.Length == 3) { - CBase.Log.LogSongWarning("[{SongFileName}] Ignored note without text (in line {LineNr})", _Song.FileName, _LineNr); + CLog.CSongLog.Warning("[{SongFileName}] Ignored note without text (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); changesMade.NoTextNoteCt++; continue; } - CBase.Log.LogSongError("[{SongFileName}] Invalid note found (in line {LineNr}): {noteData}", _Song.FileName, _LineNr, noteData); + CLog.CSongLog.Error("[{SongFileName}] Invalid note found (in line {LineNr}): {noteData}", CLog.Params(_Song.FileName, _LineNr, noteData)); sr.Dispose(); return false; } int tone; if (!int.TryParse(noteData[0], out beat) || !int.TryParse(noteData[1], out length) || !int.TryParse(noteData[2], out tone)) { - CBase.Log.LogSongError("[{SongFileName}] Invalid note found (non-numeric values) (in line {LineNr}): {noteData}", _Song.FileName, _LineNr, noteData); + CLog.CSongLog.Error("[{SongFileName}] Invalid note found (non-numeric values) (in line {LineNr}): {noteData}", CLog.Params(_Song.FileName, _LineNr, noteData)); sr.Dispose(); return false; } string text = noteData[3].TrimMultipleWs(); if (text == "") { - CBase.Log.LogSongWarning("[{SongFileName}] Ignored note without text (in line {LineNr})", _Song.FileName, _LineNr); + CLog.CSongLog.Warning("[{SongFileName}] Ignored note without text (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); changesMade.NoTextNoteCt++; continue; } @@ -601,12 +601,12 @@ namespace VocaluxeLib.Songs changesMade.ZeroLengthNoteCt++; if (_CurrentReadMode == ENoteReadMode.Normal && changesMade.ZeroLengthNoteCt > _MaxZeroNoteCt && changesMade.OverlapNoteCt <= _MaxOverlapNoteCt) { - CBase.Log.LogSongWarning("[{SongFileName}] Found more than {MaxZeroNoteCt} notes with length < 1. Trying alternative read mode.", _Song.FileName, _MaxZeroNoteCt); + CLog.CSongLog.Warning("[{SongFileName}] Found more than {MaxZeroNoteCt} notes with length < 1. Trying alternative read mode.", CLog.Params(_Song.FileName, _MaxZeroNoteCt)); _CurrentReadMode = ENoteReadMode.ZeroBased; sr.Dispose(); return ReadNotes(true); } - CBase.Log.LogSongWarning("[{SongFileName}] Ignored note with length < 1 (in line {LineNr})", _Song.FileName, _LineNr); + CLog.CSongLog.Warning("[{SongFileName}] Ignored note with length < 1 (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); } else { @@ -635,13 +635,13 @@ namespace VocaluxeLib.Songs changesMade.OverlapNoteCt++; if (changesMade.OverlapNoteCt > _MaxOverlapNoteCt && _CurrentReadMode == ENoteReadMode.ZeroBased) { - CBase.Log.LogSongWarning("[{SongFileName}] Found more than {MaxOverlapNoteCt} overlapping notes. Using standard mode.", _Song.FileName, _MaxOverlapNoteCt); + CLog.CSongLog.Warning("[{SongFileName}] Found more than {MaxOverlapNoteCt} overlapping notes. Using standard mode.", CLog.Params(_Song.FileName, _MaxOverlapNoteCt)); _CurrentReadMode = ENoteReadMode.OneBased; sr.Dispose(); return ReadNotes(true); } } - CBase.Log.LogSongWarning("[{SongFileName}] Ignored note for player {CurrentPlayerNumber} because it overlaps with other note (in line {LineNr})", _Song.FileName, (curPlayer + 1), _LineNr); + CLog.CSongLog.Warning("[{SongFileName}] Ignored note for player {CurrentPlayerNumber} because it overlaps with other note (in line {LineNr})", CLog.Params(_Song.FileName, (curPlayer + 1), _LineNr)); } } } @@ -650,13 +650,13 @@ namespace VocaluxeLib.Songs string[] lineBreakData = line.Split(splitChars); if (lineBreakData.Length < 1) { - CBase.Log.LogSongError("[{SongFileName}] Invalid line break found (No beat) (in line {LineNr}): {LineBreakData}", _Song.FileName, _LineNr, lineBreakData); + CLog.CSongLog.Error("[{SongFileName}] Invalid line break found (No beat) (in line {LineNr}): {LineBreakData}", CLog.Params(_Song.FileName, _LineNr, lineBreakData)); sr.Dispose(); return false; } if (!int.TryParse(lineBreakData[0], out beat)) { - CBase.Log.LogSongError("[{SongFileName}] Invalid line break found (Non-numeric value) (in line {LineNr}): {LineBreakData}", _Song.FileName, _LineNr, lineBreakData); + CLog.CSongLog.Error("[{SongFileName}] Invalid line break found (Non-numeric value) (in line {LineNr}): {LineBreakData}", CLog.Params(_Song.FileName, _LineNr, lineBreakData)); sr.Dispose(); return false; } @@ -666,7 +666,7 @@ namespace VocaluxeLib.Songs beat += currentBeat; if (lineBreakData.Length < 2 || !int.TryParse(lineBreakData[1], out length)) { - CBase.Log.LogSongWarning("[{SongFileName}] Missing line break length (in line {LineNr}):{LineBreakData}", _Song.FileName, _LineNr, lineBreakData); + CLog.CSongLog.Warning("[{SongFileName}] Missing line break length (in line {LineNr}):{LineBreakData}", CLog.Params(_Song.FileName, _LineNr, lineBreakData)); changesMade.NoLengthBreakCt++; currentBeat = beat; } @@ -676,7 +676,7 @@ namespace VocaluxeLib.Songs if (lastNote != null && beat <= lastNote.EndBeat) { - CBase.Log.LogSongWarning("[{SongFileName}] Line break is before previous note end. Adjusted. (in line {LineNr})", _Song.FileName, _LineNr); + CLog.CSongLog.Warning("[{SongFileName}] Line break is before previous note end. Adjusted. (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); changesMade.AjustedBreakCt++; if (_Song.Relative) @@ -686,7 +686,7 @@ namespace VocaluxeLib.Songs if (beat < 1) { - CBase.Log.LogSongWarning("[{SongFileName}] Ignored line break because position is < 1 (in line {LineNr})", _Song.FileName, _LineNr); + CLog.CSongLog.Warning("[{SongFileName}] Ignored line break because position is < 1 (in line {LineNr})", CLog.Params(_Song.FileName, _LineNr)); changesMade.InvalidPosBreakCt++; } else @@ -694,12 +694,12 @@ namespace VocaluxeLib.Songs foreach (int curPlayer in player.GetSetBits()) { if (!_NewSentence(curPlayer, beat)) - CBase.Log.LogSongWarning("[{SongFileName}] Ignored line break for player {CurPlayerNr} (Overlapping or duplicate) (in line {LineNr})", _Song.FileName, (curPlayer + 1) , _LineNr); + CLog.CSongLog.Warning("[{SongFileName}] Ignored line break for player {CurPlayerNr} (Overlapping or duplicate) (in line {LineNr})", CLog.Params(_Song.FileName, (curPlayer + 1) , _LineNr)); } } break; default: - CBase.Log.LogSongError("[{SongFileName}] Unexpected or missing character ({Tag})", _Song.FileName, tag); + CLog.CSongLog.Error("[{SongFileName}] Unexpected or missing character ({Tag})", CLog.Params(_Song.FileName, tag)); return false; } } @@ -709,13 +709,13 @@ namespace VocaluxeLib.Songs CVoice voice = _Song.Notes.GetVoice(i); int emptyLines = voice.RemoveEmptyLines(); if (emptyLines > 0) - CBase.Log.LogSongWarning("[{SongFileName}] Removed {NumEmptyLines} empty lines from P .This often indicates a problem with the line breaks in the file", _Song.FileName, emptyLines); + CLog.CSongLog.Warning("[{SongFileName}] Removed {NumEmptyLines} empty lines from P .This often indicates a problem with the line breaks in the file", CLog.Params(_Song.FileName, emptyLines)); voice.UpdateTimings(); } } catch (Exception e) { - CBase.Log.LogSongError(e, "[{SongFileName}] An unhandled exception occured: {ExceptionMessage}", _Song.FileName, e.Message); + CLog.CSongLog.Error(e, "[{SongFileName}] An unhandled exception occured: {ExceptionMessage}", CLog.Params(_Song.FileName, e.Message)); if (sr != null) sr.Dispose(); return false; @@ -732,14 +732,13 @@ namespace VocaluxeLib.Songs } catch (Exception e) { - CBase.Log.LogSongError(e, "[{SongFileName}] An unhandled exception occured: {ExceptionMessage}", _Song.FileName, e.Message); + CLog.CSongLog.Error(e, "[{SongFileName}] An unhandled exception occured: {ExceptionMessage}", CLog.Params(_Song.FileName, e.Message)); return false; } if (changesMade.IsModified) { - string msg = "Automatic changes have been made to " + filePath + " Please check result!\r\n" + changesMade; - CBase.Log.LogError("Warning: " + msg); + CLog.Warning("Automatic changes have been made to {FilePath} Please check result!\r\n{ChangesMade}" , CLog.Params(filePath, changesMade)); if (CBase.Config.GetSaveModifiedSongs() == EOffOn.TR_CONFIG_ON) { string name = Path.GetFileNameWithoutExtension(_Song.FileName); diff --git a/VocaluxeLib/Songs/CSongWriter.cs b/VocaluxeLib/Songs/CSongWriter.cs index 7d5eb4ca..bdae39c4 100644 --- a/VocaluxeLib/Songs/CSongWriter.cs +++ b/VocaluxeLib/Songs/CSongWriter.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.IO; +using VocaluxeLib.Log; namespace VocaluxeLib.Songs { @@ -207,12 +208,12 @@ namespace VocaluxeLib.Songs } catch (UnauthorizedAccessException) { - CBase.Log.LogError("Cannot write " + filePath + ". Directory might be readonly or requires admin rights."); + CLog.Error("Cannot write " + filePath + ". Directory might be readonly or requires admin rights."); return false; } catch (Exception e) { - CBase.Log.LogError("Unhandled exception while writing " + filePath + ": " + e); + CLog.Error("Unhandled exception while writing " + filePath + ": " + e); return false; } finally diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj index c0265ccf..c8d5068e 100644 --- a/VocaluxeLib/VocaluxeLib.csproj +++ b/VocaluxeLib/VocaluxeLib.csproj @@ -82,6 +82,31 @@ true + + + ..\packages\Serilog.2.6.0\lib\net46\Serilog.dll + True + + + ..\packages\Serilog.Enrichers.Thread.3.0.0\lib\net45\Serilog.Enrichers.Thread.dll + True + + + ..\packages\Serilog.Sinks.Console.3.1.1\lib\net45\Serilog.Sinks.Console.dll + True + + + ..\packages\Serilog.Sinks.File.4.0.0\lib\net45\Serilog.Sinks.File.dll + True + + + ..\packages\Serilog.Sinks.TextWriter.2.1.0\lib\net45\Serilog.Sinks.TextWriter.dll + True + + + ..\packages\SerilogTimings.2.2.0\lib\net45\SerilogTimings.dll + True + @@ -91,10 +116,19 @@ + + + + + + True + True + CLogFunctions.tt + @@ -160,6 +194,18 @@ + + + + + + TextTemplatingFileGenerator + CLogFunctions.cs + + + + + diff --git a/VocaluxeLib/Xml/CXmlDeserializer.cs b/VocaluxeLib/Xml/CXmlDeserializer.cs index 0a3b6e70..5a415397 100644 --- a/VocaluxeLib/Xml/CXmlDeserializer.cs +++ b/VocaluxeLib/Xml/CXmlDeserializer.cs @@ -24,6 +24,7 @@ using System.Linq; using System.Reflection; using System.Text; using System.Xml; +using VocaluxeLib.Log; namespace VocaluxeLib.Xml { @@ -173,7 +174,7 @@ namespace VocaluxeLib.Xml { if (e.IsError) throw e; - CBase.Log.LogError(e.ToString()); + CLog.Error(e.ToString()); } } diff --git a/VocaluxeLib/packages.config b/VocaluxeLib/packages.config new file mode 100644 index 00000000..66ace7ae --- /dev/null +++ b/VocaluxeLib/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/VocaluxeTests/PartyModeTests.cs b/VocaluxeTests/PartyModeTests.cs index 4108e8df..04374e62 100644 --- a/VocaluxeTests/PartyModeTests.cs +++ b/VocaluxeTests/PartyModeTests.cs @@ -4,6 +4,7 @@ using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Vocaluxe.Base; using VocaluxeLib; +using VocaluxeLib.Log; using VocaluxeLib.PartyModes.Challenge; namespace VocaluxeTests @@ -46,8 +47,7 @@ namespace VocaluxeTests public void TestRoundGeneration() { CBase.Game = new CBGame(); - CLog.Init(CSettings.FolderNameLogs); - CBase.Log = new CBlog(); + CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog); for (int i = 0; i < 10; i++) { for (int numPlayer = 1; numPlayer <= 20; numPlayer++) @@ -61,7 +61,7 @@ namespace VocaluxeTests Assert.IsTrue(rounds.Count >= numRounds); _CheckRounds(rounds, numPlayer); if (rounds.Count != numRounds) - CBase.Log.LogDebug("Number of rounds does not match. Expected: " + numRounds + ", Is: " + rounds.Count + " for " + numPlayer + "/" + + CLog.Debug("Number of rounds does not match. Expected: " + numRounds + ", Is: " + rounds.Count + " for " + numPlayer + "/" + ((numPlayer < numMic) ? numPlayer : numMic)); } } From b4bb001c51b79cfb8a5ab300ab4352dd8330e95b Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Tue, 16 Jan 2018 22:31:16 +0100 Subject: [PATCH 03/28] Remove SerilogTimings and add a benchmark that fit our needs. --- Vocaluxe/Base/CGraphics.cs | 7 +- Vocaluxe/Base/CSongs.cs | 12 +-- Vocaluxe/Base/Fonts/CFonts.cs | 4 +- Vocaluxe/Program.cs | 42 ++++---- Vocaluxe/Screens/CScreenLoad.cs | 6 +- Vocaluxe/Vocaluxe.csproj | 4 - Vocaluxe/packages.config | 1 - VocaluxeLib/Log/CBenchmark.cs | 165 ++++++++++++++++++++++++++++++++ VocaluxeLib/Utils/ObjectPool.cs | 61 ++++++++++++ VocaluxeLib/VocaluxeLib.csproj | 6 +- VocaluxeLib/packages.config | 1 - 11 files changed, 259 insertions(+), 50 deletions(-) create mode 100644 VocaluxeLib/Log/CBenchmark.cs create mode 100644 VocaluxeLib/Utils/ObjectPool.cs diff --git a/Vocaluxe/Base/CGraphics.cs b/Vocaluxe/Base/CGraphics.cs index 495090a8..8f8e22fd 100644 --- a/Vocaluxe/Base/CGraphics.cs +++ b/Vocaluxe/Base/CGraphics.cs @@ -21,7 +21,6 @@ using System.Diagnostics; using System.Drawing; using System.Windows.Forms; using Serilog.Events; -using SerilogTimings; using Vocaluxe.Base.Fonts; using Vocaluxe.Base.ThemeSystem; using Vocaluxe.Screens; @@ -65,7 +64,7 @@ namespace Vocaluxe.Base { // Add Screens, must be the same order as in EScreens! CLog.Information("Build Screen List"); - using (Operation.At(LogEventLevel.Information).Time("Build Screen List")) + using (CBenchmark.Time("Build Screen List")) { _Screens.Add(new CScreenTest()); _Screens.Add(new CScreenLoad()); @@ -102,7 +101,7 @@ namespace Vocaluxe.Base ZOffset = 0f; CLog.Information("Load Theme"); - using (Operation.At(LogEventLevel.Information).Time("Load Theme")) + using (CBenchmark.Time("Load Theme")) { LoadTheme(); } @@ -123,7 +122,7 @@ namespace Vocaluxe.Base for (int i = 0; i < _Screens.Count; i++) { CLog.Information("Load Theme " + Enum.GetNames(typeof(EScreen))[i]); - using (Operation.At(LogEventLevel.Information).Time("Load Theme " + Enum.GetNames(typeof(EScreen))[i])) + using (CBenchmark.Time("Load Theme " + Enum.GetNames(typeof(EScreen))[i])) { _Screens[i].Init(); _Screens[i].LoadTheme(CThemes.GetThemeScreensPath(_Screens[i].PartyModeID)); diff --git a/Vocaluxe/Base/CSongs.cs b/Vocaluxe/Base/CSongs.cs index e2a6e3f8..5c9b1943 100644 --- a/Vocaluxe/Base/CSongs.cs +++ b/Vocaluxe/Base/CSongs.cs @@ -22,8 +22,6 @@ using System.Linq; using System.Threading; using System.IO; using System.Threading.Tasks; -using Serilog.Events; -using SerilogTimings; using VocaluxeLib; using VocaluxeLib.Log; using VocaluxeLib.Songs; @@ -337,7 +335,7 @@ namespace Vocaluxe.Base public static void LoadSongs() { CLog.Information("Load Songs"); - using (Operation.At(LogEventLevel.Information).Time("Load Songs")) + using (CBenchmark.Time("Load Songs")) { SongsLoaded = false; _Songs.Clear(); @@ -345,7 +343,7 @@ namespace Vocaluxe.Base var files = new List(); CLog.Information("List Songs"); - using (Operation.At(LogEventLevel.Information).Time("List Songs")) + using (CBenchmark.Time("List Songs")) { foreach (string p in CConfig.SongFolders) @@ -360,7 +358,7 @@ namespace Vocaluxe.Base } CLog.Information("Read TXTs"); - using (Operation.At(LogEventLevel.Information).Time("Read TXTs")) + using (CBenchmark.Time("Read TXTs")) { foreach (string file in files) { @@ -374,7 +372,7 @@ namespace Vocaluxe.Base } CLog.Information("Sort Songs"); - using (Operation.At(LogEventLevel.Information).Time("Sorted Songs")) + using (CBenchmark.Time("Sorted Songs")) { Sorter.SongSorting = CConfig.Config.Game.SongSorting; Sorter.IgnoreArticles = CConfig.Config.Game.IgnoreArticles; @@ -411,7 +409,7 @@ namespace Vocaluxe.Base private static void _LoadCovers() { CLog.Information("Load Covers"); - using (Operation.At(LogEventLevel.Information).Time("Loaded Covers")) + using (CBenchmark.Time("Loaded Covers")) { int songCount = _Songs.Count; AutoResetEvent ev = new AutoResetEvent(songCount == 0); diff --git a/Vocaluxe/Base/Fonts/CFonts.cs b/Vocaluxe/Base/Fonts/CFonts.cs index a91faec2..fbb7d236 100644 --- a/Vocaluxe/Base/Fonts/CFonts.cs +++ b/Vocaluxe/Base/Fonts/CFonts.cs @@ -21,8 +21,6 @@ using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; -using Serilog.Events; -using SerilogTimings; using VocaluxeLib; using VocaluxeLib.Log; using VocaluxeLib.Menu; @@ -292,7 +290,7 @@ namespace Vocaluxe.Base.Fonts } CLog.Information("Build Glyphs"); - using (Operation.At(LogEventLevel.Information).Time("Build Glyphs")) + using (CBenchmark.Time("Build Glyphs")) { _BuildGlyphs(); } diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index 84ea160b..eb8b5a11 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -24,8 +24,6 @@ using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Threading; using System.Windows.Forms; -using Serilog.Events; -using SerilogTimings; using Vocaluxe.Base; using Vocaluxe.Base.Fonts; using Vocaluxe.Base.Server; @@ -106,14 +104,14 @@ namespace Vocaluxe CProgrammHelper.Init(); CLog.Information("Init Program"); - using (Operation.At(LogEventLevel.Information).Time("Init Program")) + using (CBenchmark.Time("Init Program")) { CMain.Init(); Application.DoEvents(); // Init Language CLog.Information("Init Language"); - using (Operation.At(LogEventLevel.Information).Time("Init Language")) + using (CBenchmark.Time("Init Language")) { if (!CLanguage.Init()) throw new CLoadingException("Language"); @@ -123,7 +121,7 @@ namespace Vocaluxe // load config CLog.Information("Init Config"); - using (Operation.At(LogEventLevel.Information).Time("Init Config")) + using (CBenchmark.Time("Init Config")) { CConfig.LoadCommandLineParams(args); CConfig.UseCommandLineParamsBefore(); @@ -139,7 +137,7 @@ namespace Vocaluxe // Init Draw CLog.Information("Init Draw"); - using (Operation.At(LogEventLevel.Information).Time("Init Draw")) + using (CBenchmark.Time("Init Draw")) { if (!CDraw.Init()) throw new CLoadingException("drawing"); @@ -149,7 +147,7 @@ namespace Vocaluxe // Init Playback CLog.Information("Init Playback"); - using (Operation.At(LogEventLevel.Information).Time("Init Playback")) + using (CBenchmark.Time("Init Playback")) { if (!CSound.Init()) throw new CLoadingException("playback"); @@ -159,7 +157,7 @@ namespace Vocaluxe // Init Record CLog.Information("Init Record"); - using (Operation.At(LogEventLevel.Information).Time("Init Record")) + using (CBenchmark.Time("Init Record")) { if (!CRecord.Init()) throw new CLoadingException("record"); @@ -169,7 +167,7 @@ namespace Vocaluxe // Init VideoDecoder CLog.Information("Init Videodecoder"); - using (Operation.At(LogEventLevel.Information).Time("Init Videodecoder")) + using (CBenchmark.Time("Init Videodecoder")) { if (!CVideo.Init()) throw new CLoadingException("video"); @@ -179,7 +177,7 @@ namespace Vocaluxe // Init Database CLog.Information("Init Database"); - using (Operation.At(LogEventLevel.Information).Time("Init Database")) + using (CBenchmark.Time("Init Database")) { if (!CDataBase.Init()) throw new CLoadingException("database"); @@ -189,7 +187,7 @@ namespace Vocaluxe //Init Webcam CLog.Information("Init Webcam"); - using (Operation.At(LogEventLevel.Information).Time("Init Webcam")) + using (CBenchmark.Time("Init Webcam")) { if (!CWebcam.Init()) throw new CLoadingException("webcam"); @@ -199,7 +197,7 @@ namespace Vocaluxe // Init Background Music CLog.Information("Init Background Music"); - using (Operation.At(LogEventLevel.Information).Time("Init Background Music")) + using (CBenchmark.Time("Init Background Music")) { CBackgroundMusic.Init(); } @@ -208,7 +206,7 @@ namespace Vocaluxe // Init Profiles CLog.Information("Init Profiles"); - using (Operation.At(LogEventLevel.Information).Time("Init Profiles")) + using (CBenchmark.Time("Init Profiles")) { CProfiles.Init(); } @@ -217,7 +215,7 @@ namespace Vocaluxe // Init Fonts CLog.Information("Init Fonts"); - using (Operation.At(LogEventLevel.Information).Time("Init Fonts")) + using (CBenchmark.Time("Init Fonts")) { if (!CFonts.Init()) throw new CLoadingException("fonts"); @@ -227,14 +225,14 @@ namespace Vocaluxe // Theme System CLog.Information("Init Theme"); - using (Operation.At(LogEventLevel.Information).Time("Init Theme")) + using (CBenchmark.Time("Init Theme")) { if (!CThemes.Init()) throw new CLoadingException("theme"); } CLog.Information("Load Theme"); - using (Operation.At(LogEventLevel.Information).Time("Load Theme")) + using (CBenchmark.Time("Load Theme")) { CThemes.Load(); } @@ -243,7 +241,7 @@ namespace Vocaluxe // Load Cover CLog.Information("Init Cover"); - using (Operation.At(LogEventLevel.Information).Time("Init Cover")) + using (CBenchmark.Time("Init Cover")) { if (!CCover.Init()) throw new CLoadingException("covertheme"); @@ -253,7 +251,7 @@ namespace Vocaluxe // Init Screens CLog.Information("Init Screens"); - using (Operation.At(LogEventLevel.Information).Time("Init Screens")) + using (CBenchmark.Time("Init Screens")) { CGraphics.Init(); } @@ -262,7 +260,7 @@ namespace Vocaluxe // Init Server CLog.Information("Init Server"); - using (Operation.At(LogEventLevel.Information).Time("Init Server")) + using (CBenchmark.Time("Init Server")) { CVocaluxeServer.Init(); } @@ -271,7 +269,7 @@ namespace Vocaluxe // Init Input CLog.Information("Init Input"); - using (Operation.At(LogEventLevel.Information).Time("Init Input")) + using (CBenchmark.Time("Init Input")) { CController.Init(); CController.Connect(); @@ -281,7 +279,7 @@ namespace Vocaluxe // Init Game; CLog.Information("Init Game"); - using (Operation.At(LogEventLevel.Information).Time("Init Game")) + using (CBenchmark.Time("Init Game")) { CGame.Init(); CProfiles.Update(); @@ -292,7 +290,7 @@ namespace Vocaluxe // Init Party Modes; CLog.Information("Init Party Modes"); - using (Operation.At(LogEventLevel.Information).Time("Init Party Modes")) + using (CBenchmark.Time("Init Party Modes")) { if (!CParty.Init()) throw new CLoadingException("Party Modes"); diff --git a/Vocaluxe/Screens/CScreenLoad.cs b/Vocaluxe/Screens/CScreenLoad.cs index 7ace4847..38f28903 100644 --- a/Vocaluxe/Screens/CScreenLoad.cs +++ b/Vocaluxe/Screens/CScreenLoad.cs @@ -18,8 +18,6 @@ using System; using System.Threading; using System.Windows.Forms; -using Serilog.Events; -using SerilogTimings; using Vocaluxe.Base; using VocaluxeLib; using VocaluxeLib.Log; @@ -130,7 +128,7 @@ namespace Vocaluxe.Screens } CLog.Information("Load Songs Full"); - _TimerLoadSongsFull = Operation.At(LogEventLevel.Information).Time("Loaded Songs Full"); + _TimerLoadSongsFull = CBenchmark.Time("Loaded Songs Full"); _SongLoaderThread = new Thread(CSongs.LoadSongs) {Name = "SongLoader", IsBackground = true}; _SongLoaderThread.Start(); @@ -189,7 +187,7 @@ namespace Vocaluxe.Screens //Init Playlists CLog.Information("Init Playlists"); - using (Operation.At(LogEventLevel.Information).Time("Init Playlists")) + using (CBenchmark.Time("Init Playlists")) { CPlaylists.Init(); } diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index 8b69d5f0..9f4706c5 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -183,10 +183,6 @@ ..\packages\Serilog.Sinks.TextWriter.2.1.0\lib\net45\Serilog.Sinks.TextWriter.dll True - - ..\packages\SerilogTimings.2.2.0\lib\net45\SerilogTimings.dll - True - diff --git a/Vocaluxe/packages.config b/Vocaluxe/packages.config index 1cb54eb4..9b219db0 100644 --- a/Vocaluxe/packages.config +++ b/Vocaluxe/packages.config @@ -6,7 +6,6 @@ - diff --git a/VocaluxeLib/Log/CBenchmark.cs b/VocaluxeLib/Log/CBenchmark.cs new file mode 100644 index 00000000..60e5818b --- /dev/null +++ b/VocaluxeLib/Log/CBenchmark.cs @@ -0,0 +1,165 @@ +using System; +using System.Diagnostics; +using VocaluxeLib.Utils; + +namespace VocaluxeLib.Log +{ + /// + /// Class for Benchmarks. + /// Can be used as instance or through the static wrappers. + /// + public class CBenchmark : IDisposable + { + // Pool of stop watches shared between all instances (we can get more than 5 but it only keeps 5) + private static readonly CObjectPool _WatchesPool = new CObjectPool(() => new Stopwatch(), 5); + private static readonly double _NanosecPerTick = (1000.0 * 1000.0 * 1000.0) / Stopwatch.Frequency; + + // The stop watch used by this instance + private Stopwatch _Watch = null; + // The operation name the will be written to the log on events for this instance + private readonly string _OperationName; + + /// + /// Creates a new benchmark instance. + /// + /// The name of the operation (will be used for log messages). + public CBenchmark(string operationName) + { + _OperationName = operationName; + } + + /// + /// Starts this benchmark. + /// + public void Start() + { + if (_Watch != null) + throw new InvalidOperationException("Timer is already running."); + _Watch = _WatchesPool.GetObject(); + _Watch.Restart(); + CLog.Information("Started {StartedOperation}", CLog.Params(_OperationName)); + } + + /// + /// Stops this benchmark. + /// + /// If true a success message will be added to the log otherwise a message for failure. + public void Stop(bool success = true) + { + if (_Watch == null) + throw new InvalidOperationException("Timer is not running."); + _Watch.Stop(); + double duration = _GetElapsedTime(_Watch); + + _WatchesPool.PutObject(_Watch); + _Watch = null; + if (success) + CLog.Information("Finished {StartedOperation} successfully in {Duration:#,##0.00}ms", CLog.Params(_OperationName, duration)); + else + CLog.Information("Failed {StartedOperation} in {Duration:#,##0.00}ms", CLog.Params(_OperationName, duration)); + } + + /// + /// Helper method to extract the elapsed time from a stopwatch. + /// + /// The stopwatch. + /// The elapsed time of the given stop watch. + private double _GetElapsedTime(Stopwatch watch) + { + if (Stopwatch.IsHighResolution && _NanosecPerTick > 0) + return (float)((_NanosecPerTick * watch.ElapsedTicks) / (1000.0 * 1000.0)); + else + return watch.ElapsedMilliseconds; + } + + /// + /// Ends a possibly running benchmark (sucessfully). + /// + public void Dispose() + { + if (_Watch != null) + Stop(); + } + + #region Static functions + + /// + /// Logs the time from now to disposal of the returned object. + /// + /// The name of the operation (will be used for log messages). + /// Object which needs to be disposed to stop the benchmark. + /// + /// + /// using(CBenchmark.Time("TestOperation")){ + /// //do someting + /// } + /// + /// + public static IDisposable Time(string operationName) + { + CBenchmark benchmark = new CBenchmark(operationName); + benchmark.Start(); + return benchmark; + } + + /// + /// Logs the time from now to the call of End() on the returned object (== success) or it's disposal (== failure). + /// + /// The name of the operation (will be used for log messages). + /// Object to stop the benchmark. + /// + /// + /// using(var op = CBenchmark.Begin("TestOperation")){ + /// //do someting that could fail + /// op.End(); + /// } + /// + /// + public static COperation Begin(string operationName) + { + CBenchmark benchmark = new CBenchmark(operationName); + benchmark.Start(); + return new COperation(benchmark); + } + + #region Helper class for operation + + /// + /// Helper object to end a benchmark (End() for success and Dispose() for failure). + /// + public class COperation : IDisposable + { + private readonly CBenchmark _Benchmark; + + /// + /// Creates a new instance of the benchmark helper object. + /// + /// The associated benchmark object. + internal COperation(CBenchmark benchmark) + { + _Benchmark = benchmark; + + } + + /// + /// End the associated benchmark sucessfully. + /// + public void End() + { + _Benchmark.Stop(true); + } + + /// + /// End the associated benchmark not sucessfully. + /// + public void Dispose() + { + _Benchmark.Stop(false); + } + } + + #endregion + + #endregion + } +} diff --git a/VocaluxeLib/Utils/ObjectPool.cs b/VocaluxeLib/Utils/ObjectPool.cs new file mode 100644 index 00000000..7f1b75fb --- /dev/null +++ b/VocaluxeLib/Utils/ObjectPool.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VocaluxeLib.Utils +{ + // Modified version of https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/how-to-create-an-object-pool + + /// + /// Pool for objects. + /// + /// Type of the objects in the pool. + public class CObjectPool + { + private readonly ConcurrentBag _Objects; + private readonly Func _ObjectGenerator; + private readonly int _Poolsize; + + /// + /// Create a new object pool. + /// + /// Function witch generate a new object instance. + /// Number of objects to keep in the pool. + public CObjectPool(Func objectGenerator, int poolSize) + { + if (objectGenerator == null) + throw new ArgumentNullException(nameof(objectGenerator)); + if (poolSize <= 0) + throw new ArgumentNullException(nameof(poolSize)); + _Objects = new ConcurrentBag(); + _ObjectGenerator = objectGenerator; + _Poolsize = poolSize; + } + + /// + /// Get an object (from the pool or a new one). + /// + /// An object instance. + public T GetObject() + { + T item; + if (_Objects.TryTake(out item)) return item; + return _ObjectGenerator(); + } + + /// + /// Put an intance back to the pool. + /// + /// The object instance that is given back to the pool. + public void PutObject(T item) + { + if(_Objects.Count < _Poolsize) + _Objects.Add(item); + else + (item as IDisposable)?.Dispose(); + } + } +} diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj index c8d5068e..55d77e22 100644 --- a/VocaluxeLib/VocaluxeLib.csproj +++ b/VocaluxeLib/VocaluxeLib.csproj @@ -103,10 +103,6 @@ ..\packages\Serilog.Sinks.TextWriter.2.1.0\lib\net45\Serilog.Sinks.TextWriter.dll True - - ..\packages\SerilogTimings.2.2.0\lib\net45\SerilogTimings.dll - True - @@ -129,6 +125,7 @@ True CLogFunctions.tt + @@ -157,6 +154,7 @@ + diff --git a/VocaluxeLib/packages.config b/VocaluxeLib/packages.config index 66ace7ae..7bd9eaac 100644 --- a/VocaluxeLib/packages.config +++ b/VocaluxeLib/packages.config @@ -5,5 +5,4 @@ - \ No newline at end of file From 330b1eada23d8237a31c01a89997ba2ec41fe01b Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Wed, 17 Jan 2018 17:47:27 +0100 Subject: [PATCH 04/28] Add crash marker --- Vocaluxe/Base/CSettings.cs | 17 +++++++++++++---- Vocaluxe/Program.cs | 2 +- VocaluxeLib/Log/CLog.cs | 33 ++++++++++++++++++++++++++++++++- VocaluxeTests/PartyModeTests.cs | 2 +- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/Vocaluxe/Base/CSettings.cs b/Vocaluxe/Base/CSettings.cs index abe324ed..c682016b 100644 --- a/Vocaluxe/Base/CSettings.cs +++ b/Vocaluxe/Base/CSettings.cs @@ -63,15 +63,15 @@ namespace Vocaluxe.Base { get { - if (_Version.ToLower().Contains("alpha")) + if (Version.ToLower().Contains("alpha")) { return ERevision.Alpha; } - if (_Version.ToLower().Contains("beta")) + if (Version.ToLower().Contains("beta")) { return ERevision.Beta; } - if (_Version.ToLower().Contains("rc")) + if (Version.ToLower().Contains("rc")) { return ERevision.RC; } @@ -115,6 +115,7 @@ namespace Vocaluxe.Base public const string FileNameMainLog = "Vocaluxe.log"; public const string FileNameSongLog = "Song.log"; + public const string FileNameCrashMarker = "Marker"; public const string FileNameRequiredSkinElements = "RequiredSkinElements.xml"; @@ -194,13 +195,21 @@ namespace Vocaluxe.Base get { return _Assembly.Name; } } - private static string _Version + public static string Version { get { return Application.ProductVersion.Split('-').First(); } } + + public static string FullVersion + { + get + { + return Application.ProductVersion; + } + } public static string GetFullVersionText() { diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index eb8b5a11..94cc8d18 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -97,7 +97,7 @@ namespace Vocaluxe Directory.CreateDirectory(CSettings.DataFolder); // Init Log - CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog); + CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog, CSettings.FileNameCrashMarker, CSettings.FullVersion); if (!CProgrammHelper.CheckRequirements()) return; diff --git a/VocaluxeLib/Log/CLog.cs b/VocaluxeLib/Log/CLog.cs index 00669bd7..a67752a0 100644 --- a/VocaluxeLib/Log/CLog.cs +++ b/VocaluxeLib/Log/CLog.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Linq; using System.Text; using Serilog; using VocaluxeLib.Log.Enricher; @@ -16,16 +17,43 @@ namespace VocaluxeLib.Log private static ILogger _MainLog = new SilentLogger(); private static ILogger _SongLog = new SilentLogger(); private static string _LogFolder; + private static string _CrashMarkerFilePath; private const string _MainLogTemplate = "[{TimeStampFromStart}] [{Level}] {Message}{NewLine}{Properties}{NewLine}{Exception}"; private const string _SongLogTemplate = "{Message}{NewLine}Additional info:{Properties}{NewLine}{Exception}"; - public static void Init(string logFolder, string fileNameMainLog, string fileNameSongInfoLog) + public static void Init(string logFolder, string fileNameMainLog, string fileNameSongInfoLog, string fileNameCrashMarker, string currentVersion) { _LogFolder = logFolder; + _CrashMarkerFilePath = Path.Combine(_LogFolder, fileNameCrashMarker); + + + // Creates the log directory if it does not exist if (!Directory.Exists(_LogFolder)) Directory.CreateDirectory(_LogFolder); + // Check if crash marker file + if (File.Exists(_CrashMarkerFilePath)) + { + // There was a crash in the last run -> check version tag of the crashed application instance + string versionTag; + using (StreamReader reader = new StreamReader(_CrashMarkerFilePath, Encoding.UTF8)) + { + versionTag = (reader.ReadLine() ?? "").Trim(); + } + + // Delete the old marker + File.Delete(_CrashMarkerFilePath); + + if (currentVersion == versionTag) + { + // TODO: Load log + Crashreport + } + } + + // Write new marker + File.WriteAllText(_CrashMarkerFilePath, currentVersion, Encoding.UTF8); + LogFileRoller.RollLogs(Path.Combine(_LogFolder, fileNameMainLog), 2); LogFileRoller.RollLogs(Path.Combine(_LogFolder, fileNameSongInfoLog), 2); @@ -67,6 +95,9 @@ namespace VocaluxeLib.Log (_SongLog as IDisposable)?.Dispose(); } + // Delete the crash marker + File.Delete(_CrashMarkerFilePath); + } private static void _ShowLogAssistent(string messageTemplate, object[] propertyValues, string callerMethodeName = "", string callerFilePath = "", int callerLineNumer = -1) diff --git a/VocaluxeTests/PartyModeTests.cs b/VocaluxeTests/PartyModeTests.cs index 04374e62..e5409952 100644 --- a/VocaluxeTests/PartyModeTests.cs +++ b/VocaluxeTests/PartyModeTests.cs @@ -47,7 +47,7 @@ namespace VocaluxeTests public void TestRoundGeneration() { CBase.Game = new CBGame(); - CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog); + CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog, CSettings.FileNameCrashMarker, CSettings.FullVersion); for (int i = 0; i < 10; i++) { for (int numPlayer = 1; numPlayer <= 20; numPlayer++) From e236535a428bb085939a89f92096a7c3ddd0c445 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Wed, 17 Jan 2018 17:57:36 +0100 Subject: [PATCH 05/28] Fatal is now the new "error + terminate application" --- VocaluxeLib/Log/CLogFunctions.cs | 96 +++++++------------------------- VocaluxeLib/Log/CLogFunctions.tt | 33 +++++++++-- 2 files changed, 49 insertions(+), 80 deletions(-) diff --git a/VocaluxeLib/Log/CLogFunctions.cs b/VocaluxeLib/Log/CLogFunctions.cs index cf311403..d241cc4c 100644 --- a/VocaluxeLib/Log/CLogFunctions.cs +++ b/VocaluxeLib/Log/CLogFunctions.cs @@ -438,7 +438,7 @@ namespace VocaluxeLib.Log #region Fatal functions /// - /// Write an event with the Fatal level to the Fatal log. + /// Write an event with the Fatal level to the Fatal log and TERMINATES the appication. /// /// Message template for this event. /// True if an message should be shown to the user, false otherwise. @@ -454,10 +454,14 @@ namespace VocaluxeLib.Log } if(show) _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + + // Close logs and exit + Close(); + Environment.Exit(Environment.ExitCode); } /// - /// Write an event with the Fatal level with additional propertyValues to the Fatal log. + /// Write an event with the Fatal level with additional propertyValues to the Fatal log and TERMINATES the appication. /// /// Message template for this event. /// Data inserted into the message template. @@ -474,10 +478,14 @@ namespace VocaluxeLib.Log } if(show) _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + + // Close logs and exit + Close(); + Environment.Exit(Environment.ExitCode); } /// - /// Write an event with the Fatal level and associated exception to the Fatal log. + /// Write an event with the Fatal level and associated exception to the Fatal log and TERMINATES the appication. /// /// Exception of this event. /// Message template for this event. @@ -494,10 +502,14 @@ namespace VocaluxeLib.Log } if(show) _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + + // Close logs and exit + Close(); + Environment.Exit(Environment.ExitCode); } /// - /// Write an event with the Fatal level with additional propertyValues and associated exception to the Fatal log. + /// Write an event with the Fatal level with additional propertyValues and associated exception to the Fatal log and TERMINATES the appication. /// /// Exception of this event. /// Message template for this event. @@ -515,6 +527,10 @@ namespace VocaluxeLib.Log } if(show) _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + + // Close logs and exit + Close(); + Environment.Exit(Environment.ExitCode); } #endregion @@ -882,78 +898,6 @@ namespace VocaluxeLib.Log } #endregion - - #region Fatal functions - - /// - /// Write an event with the Fatal level to the Fatal log. - /// - /// Message template for this event. - /// Don't use! The methode name of the caller will be filled automatically by the compiler. - /// Don't use! The filepath of the caller will be filled automatically by the compiler. - /// Don't use! The line number of the caller will be filled automatically by the compiler. - [MessageTemplateFormatMethod("messageTemplate")] - public static void Fatal(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) - { - using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _SongLog.Fatal(messageTemplate); - } - } - - /// - /// Write an event with the Fatal level with additional propertyValues to the Fatal log. - /// - /// Message template for this event. - /// Data inserted into the message template. - /// Don't use! The methode name of the caller will be filled automatically by the compiler. - /// Don't use! The filepath of the caller will be filled automatically by the compiler. - /// Don't use! The line number of the caller will be filled automatically by the compiler. - [MessageTemplateFormatMethod("messageTemplate")] - public static void Fatal(string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) - { - using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _SongLog.Fatal(messageTemplate, propertyValues); - } - } - - /// - /// Write an event with the Fatal level and associated exception to the Fatal log. - /// - /// Exception of this event. - /// Message template for this event. - /// Don't use! The methode name of the caller will be filled automatically by the compiler. - /// Don't use! The filepath of the caller will be filled automatically by the compiler. - /// Don't use! The line number of the caller will be filled automatically by the compiler. - [MessageTemplateFormatMethod("messageTemplate")] - public static void Fatal(Exception exception, string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) - { - using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _SongLog.Fatal( exception, messageTemplate); - } - } - - /// - /// Write an event with the Fatal level with additional propertyValues and associated exception to the Fatal log. - /// - /// Exception of this event. - /// Message template for this event. - /// Data inserted into the message template. - /// Don't use! The methode name of the caller will be filled automatically by the compiler. - /// Don't use! The filepath of the caller will be filled automatically by the compiler. - /// Don't use! The line number of the caller will be filled automatically by the compiler. - [MessageTemplateFormatMethod("messageTemplate")] - public static void Fatal(Exception exception, string messageTemplate, object[] propertyValues, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) - { - using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _SongLog.Fatal(exception, messageTemplate, propertyValues); - } - } - - #endregion } } } \ No newline at end of file diff --git a/VocaluxeLib/Log/CLogFunctions.tt b/VocaluxeLib/Log/CLogFunctions.tt index 2fe5ec71..1f49063c 100644 --- a/VocaluxeLib/Log/CLogFunctions.tt +++ b/VocaluxeLib/Log/CLogFunctions.tt @@ -29,11 +29,12 @@ namespace VocaluxeLib.Log { <# } #> <# foreach (var LogLevel in new[]{"Verbose","Debug", "Information", "Warning", "Error", "Fatal"}) { #> +<# if(LogLevel == "Fatal" && Log == "Song") continue; #> #region <#= LogLevel #> functions /// - /// Write an event with the <#= LogLevel #> level to the <#= LogLevel #> log. + /// Write an event with the <#= LogLevel #> level to the <#= LogLevel #> log<#= LogLevel == "Fatal"?" and TERMINATES the appication":"" #>. /// /// Message template for this event. <# if(Log == "Main") { #> @@ -52,11 +53,17 @@ namespace VocaluxeLib.Log <# if(Log == "Main") { #> if(show) _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); +<# } #> +<# if(LogLevel == "Fatal") { #> + + // Close logs and exit + Close(); + Environment.Exit(Environment.ExitCode); <# } #> } /// - /// Write an event with the <#= LogLevel #> level with additional propertyValues to the <#= LogLevel #> log. + /// Write an event with the <#= LogLevel #> level with additional propertyValues to the <#= LogLevel #> log<#= LogLevel == "Fatal"?" and TERMINATES the appication":"" #>. /// /// Message template for this event. /// Data inserted into the message template. @@ -76,11 +83,17 @@ namespace VocaluxeLib.Log <# if(Log == "Main") { #> if(show) _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); +<# } #> +<# if(LogLevel == "Fatal") { #> + + // Close logs and exit + Close(); + Environment.Exit(Environment.ExitCode); <# } #> } /// - /// Write an event with the <#= LogLevel #> level and associated exception to the <#= LogLevel #> log. + /// Write an event with the <#= LogLevel #> level and associated exception to the <#= LogLevel #> log<#= LogLevel == "Fatal"?" and TERMINATES the appication":"" #>. /// /// Exception of this event. /// Message template for this event. @@ -100,11 +113,17 @@ namespace VocaluxeLib.Log <# if(Log == "Main") { #> if(show) _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); +<# } #> +<# if(LogLevel == "Fatal") { #> + + // Close logs and exit + Close(); + Environment.Exit(Environment.ExitCode); <# } #> } /// - /// Write an event with the <#= LogLevel #> level with additional propertyValues and associated exception to the <#= LogLevel #> log. + /// Write an event with the <#= LogLevel #> level with additional propertyValues and associated exception to the <#= LogLevel #> log<#= LogLevel == "Fatal"?" and TERMINATES the appication":"" #>. /// /// Exception of this event. /// Message template for this event. @@ -125,6 +144,12 @@ namespace VocaluxeLib.Log <# if(Log == "Main") { #> if(show) _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); +<# } #> +<# if(LogLevel == "Fatal") { #> + + // Close logs and exit + Close(); + Environment.Exit(Environment.ExitCode); <# } #> } From 6f9fcfc03a89df61eb55148f5d6025e10c78b8d1 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Wed, 17 Jan 2018 18:08:44 +0100 Subject: [PATCH 06/28] Update UnhandledExceptionHandler --- Vocaluxe/Program.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index 94cc8d18..36c12851 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -453,16 +453,7 @@ namespace Vocaluxe [HandleProcessCorruptedStateExceptions] private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) { - var e = (Exception)args.ExceptionObject; - string stackTrace = ""; - try - { - stackTrace = e.StackTrace; - } - catch {} - MessageBox.Show("Unhandled exception: " + e.Message + stackTrace); - CLog.Error("Unhandled exception: " + e.Message + stackTrace); - _CloseProgram(); + CLog.Fatal((Exception)args.ExceptionObject, "Unhandled exception: {ExceptionMessage}", CLog.Params(e.Message)); } #endif From 47bd3286d5b1afc47a3f100de30263c3d0d2dbc6 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Wed, 17 Jan 2018 18:13:42 +0100 Subject: [PATCH 07/28] Use log version tag for crash marker --- Vocaluxe/Program.cs | 2 +- VocaluxeTests/PartyModeTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index 36c12851..a2611c08 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -97,7 +97,7 @@ namespace Vocaluxe Directory.CreateDirectory(CSettings.DataFolder); // Init Log - CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog, CSettings.FileNameCrashMarker, CSettings.FullVersion); + CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog, CSettings.FileNameCrashMarker, CSettings.GetFullVersionText()); if (!CProgrammHelper.CheckRequirements()) return; diff --git a/VocaluxeTests/PartyModeTests.cs b/VocaluxeTests/PartyModeTests.cs index e5409952..8784d049 100644 --- a/VocaluxeTests/PartyModeTests.cs +++ b/VocaluxeTests/PartyModeTests.cs @@ -47,7 +47,7 @@ namespace VocaluxeTests public void TestRoundGeneration() { CBase.Game = new CBGame(); - CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog, CSettings.FileNameCrashMarker, CSettings.FullVersion); + CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog, CSettings.FileNameCrashMarker, CSettings.GetFullVersionText()); for (int i = 0; i < 10; i++) { for (int numPlayer = 1; numPlayer <= 20; numPlayer++) From d9a98fcd145cd63145309f4ab730c10309d2d200 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Thu, 18 Jan 2018 13:14:27 +0100 Subject: [PATCH 08/28] Add a basic log issue reporter --- Vocaluxe/Program.cs | 3 +- Vocaluxe/Reporting/Reporter.Designer.cs | 186 +++ Vocaluxe/Reporting/Reporter.cs | 258 ++++ Vocaluxe/Reporting/Reporter.resx | 1615 +++++++++++++++++++++++ Vocaluxe/Vocaluxe.csproj | 16 + Vocaluxe/packages.config | 1 + VocaluxeLib/Log/CLog.cs | 32 +- VocaluxeLib/Log/ShowReporterDelegate.cs | 5 + VocaluxeLib/VocaluxeLib.csproj | 1 + VocaluxeTests/PartyModeTests.cs | 3 +- 10 files changed, 2107 insertions(+), 13 deletions(-) create mode 100644 Vocaluxe/Reporting/Reporter.Designer.cs create mode 100644 Vocaluxe/Reporting/Reporter.cs create mode 100644 Vocaluxe/Reporting/Reporter.resx create mode 100644 VocaluxeLib/Log/ShowReporterDelegate.cs diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index a2611c08..1c531a16 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -28,6 +28,7 @@ using Vocaluxe.Base; using Vocaluxe.Base.Fonts; using Vocaluxe.Base.Server; using Vocaluxe.Base.ThemeSystem; +using Vocaluxe.Reporting; using VocaluxeLib.Log; [assembly: InternalsVisibleTo("VocaluxeTests")] @@ -97,7 +98,7 @@ namespace Vocaluxe Directory.CreateDirectory(CSettings.DataFolder); // Init Log - CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog, CSettings.FileNameCrashMarker, CSettings.GetFullVersionText()); + CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog, CSettings.FileNameCrashMarker, CSettings.GetFullVersionText(), CReporter.ShowReporterFunc); if (!CProgrammHelper.CheckRequirements()) return; diff --git a/Vocaluxe/Reporting/Reporter.Designer.cs b/Vocaluxe/Reporting/Reporter.Designer.cs new file mode 100644 index 00000000..cbdee73e --- /dev/null +++ b/Vocaluxe/Reporting/Reporter.Designer.cs @@ -0,0 +1,186 @@ +namespace Vocaluxe.Reporting +{ + partial class CReporter + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CReporter)); + this.Title = new System.Windows.Forms.Label(); + this.Message = new System.Windows.Forms.TextBox(); + this.Log = new System.Windows.Forms.TextBox(); + this.GistOnly = new System.Windows.Forms.RadioButton(); + this.GistAndIssue = new System.Windows.Forms.RadioButton(); + this.NoUpload = new System.Windows.Forms.RadioButton(); + this.Submit = new System.Windows.Forms.Button(); + this.Url = new System.Windows.Forms.TextBox(); + this.SubmitedMessage = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // Title + // + this.Title.AutoSize = true; + this.Title.Enabled = false; + this.Title.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Title.Location = new System.Drawing.Point(9, 8); + this.Title.Name = "Title"; + this.Title.Size = new System.Drawing.Size(110, 20); + this.Title.TabIndex = 0; + this.Title.Text = "We are sorry"; + // + // Message + // + this.Message.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.Message.Enabled = false; + this.Message.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Message.Location = new System.Drawing.Point(13, 31); + this.Message.Multiline = true; + this.Message.Name = "Message"; + this.Message.ReadOnly = true; + this.Message.Size = new System.Drawing.Size(625, 63); + this.Message.TabIndex = 0; + this.Message.TabStop = false; + this.Message.Text = "that Vocaluxe run into an error and crashed.\r\nTo help us fix the problem please s" + + "end us a report.\r\nEdit the following log before submitting to remove possibly se" + + "nsitive information."; + // + // Log + // + this.Log.AcceptsReturn = true; + this.Log.AcceptsTab = true; + this.Log.Location = new System.Drawing.Point(13, 100); + this.Log.Multiline = true; + this.Log.Name = "Log"; + this.Log.Size = new System.Drawing.Size(625, 189); + this.Log.TabIndex = 1; + // + // GistOnly + // + this.GistOnly.AutoSize = true; + this.GistOnly.Checked = true; + this.GistOnly.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.GistOnly.Location = new System.Drawing.Point(12, 295); + this.GistOnly.Name = "GistOnly"; + this.GistOnly.Size = new System.Drawing.Size(385, 24); + this.GistOnly.TabIndex = 2; + this.GistOnly.TabStop = true; + this.GistOnly.Text = "Upload and get a link to your report (publicly visible)"; + this.GistOnly.UseVisualStyleBackColor = true; + // + // GistAndIssue + // + this.GistAndIssue.AutoSize = true; + this.GistAndIssue.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.GistAndIssue.Location = new System.Drawing.Point(12, 325); + this.GistAndIssue.Name = "GistAndIssue"; + this.GistAndIssue.Size = new System.Drawing.Size(519, 24); + this.GistAndIssue.TabIndex = 3; + this.GistAndIssue.TabStop = true; + this.GistAndIssue.Text = "Upload and open an issue (publicly visible + requires a github account)"; + this.GistAndIssue.UseVisualStyleBackColor = true; + // + // NoUpload + // + this.NoUpload.AutoSize = true; + this.NoUpload.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.NoUpload.Location = new System.Drawing.Point(12, 355); + this.NoUpload.Name = "NoUpload"; + this.NoUpload.Size = new System.Drawing.Size(493, 24); + this.NoUpload.TabIndex = 4; + this.NoUpload.TabStop = true; + this.NoUpload.Text = "Don\'t upload anything (you can still copy the error message above)"; + this.NoUpload.UseVisualStyleBackColor = true; + this.NoUpload.CheckedChanged += new System.EventHandler(this.NoUpload_CheckedChanged); + // + // Submit + // + this.Submit.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Submit.Location = new System.Drawing.Point(537, 306); + this.Submit.Name = "Submit"; + this.Submit.Size = new System.Drawing.Size(101, 73); + this.Submit.TabIndex = 5; + this.Submit.Text = "Submit"; + this.Submit.UseVisualStyleBackColor = true; + this.Submit.Click += new System.EventHandler(this.Submit_Click); + // + // Url + // + this.Url.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Url.Location = new System.Drawing.Point(12, 327); + this.Url.Name = "Url"; + this.Url.ReadOnly = true; + this.Url.Size = new System.Drawing.Size(519, 22); + this.Url.TabIndex = 6; + this.Url.Text = "https://gist.github.com/lukeIam/cc81f7ef3545ef2baf0ca9a28ffe4bd8"; + this.Url.Visible = false; + // + // SubmitedMessage + // + this.SubmitedMessage.AutoSize = true; + this.SubmitedMessage.Enabled = false; + this.SubmitedMessage.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.SubmitedMessage.Location = new System.Drawing.Point(9, 299); + this.SubmitedMessage.Name = "SubmitedMessage"; + this.SubmitedMessage.Size = new System.Drawing.Size(189, 20); + this.SubmitedMessage.TabIndex = 8; + this.SubmitedMessage.Text = "The link to your report:"; + this.SubmitedMessage.Visible = false; + // + // CReporter + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(650, 387); + this.Controls.Add(this.SubmitedMessage); + this.Controls.Add(this.Url); + this.Controls.Add(this.Submit); + this.Controls.Add(this.NoUpload); + this.Controls.Add(this.GistAndIssue); + this.Controls.Add(this.GistOnly); + this.Controls.Add(this.Log); + this.Controls.Add(this.Message); + this.Controls.Add(this.Title); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "CReporter"; + this.Text = "Vocaluxe Issue Reporter"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label Title; + private System.Windows.Forms.TextBox Message; + private System.Windows.Forms.TextBox Log; + private System.Windows.Forms.RadioButton GistOnly; + private System.Windows.Forms.RadioButton GistAndIssue; + private System.Windows.Forms.RadioButton NoUpload; + private System.Windows.Forms.Button Submit; + private System.Windows.Forms.TextBox Url; + private System.Windows.Forms.Label SubmitedMessage; + } +} \ No newline at end of file diff --git a/Vocaluxe/Reporting/Reporter.cs b/Vocaluxe/Reporting/Reporter.cs new file mode 100644 index 00000000..0c813bec --- /dev/null +++ b/Vocaluxe/Reporting/Reporter.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Forms; +using Newtonsoft.Json; +using VocaluxeLib.Log; + +namespace Vocaluxe.Reporting +{ + public partial class CReporter : Form + { + private string _TitleText = "We are sorry"; + private string _MessageCrashText = "that Vocaluxe run into an error and crashed.\r\nTo help us fix the problem please send us a report.\r\nEdit the following log before submitting to remove possibly sensitive information."; + private string _MessageNoCrashText = "that you experienced an error.\r\nTo help us fix the problem please send us a report.\r\nEdit the following log before submitting to remove possibly sensitive information."; + private string _NoUploadText = "Don\'t upload anything (you can still copy the error message above)"; + private string _GistAndIssueText = "Upload and open an issue (publicly visible + requires a github account)"; + private string _GistOnlyText = "Upload and get a link to your report (publicly visible)"; + private string _SubmitStep0Text = "Submit"; + private string _SubmitStep1Text = "Uploading"; + private string _SubmitStep2CrashText = "Exit"; + private string _SubmitStep2NoCrashText = "Continue"; + private string _SubmitedMessageText = "The link to your report:"; + private string _LogUploadErrorText = "Error uploading the log."; + + + private string _IssueTemplate = "Describe your issue here.\n\n### Steps to reproduce\nTell us how to reproduce this issue.\n\n### Vocaluxe version and logfile\n{0}\n{1}"; + + private int _Step = 0; + private readonly bool _Crash; + private readonly string _VocaluxeVersionTag; + private readonly bool _ShowContinue; + + private static readonly HttpClient _Client = new HttpClient(); + private static readonly Regex _GetGistUrlRegex = new Regex("\"raw_url\": *\"([^\"]+)\""); + + + public static ShowReporterDelegate ShowReporterFunc + { + get { return _ShowReporter; } + } + + private static void _ShowReporter(bool crash, bool showContinue, string vocaluxeVersionTag, string log) + { + using (var reporter = new CReporter(crash, showContinue, vocaluxeVersionTag, log)) + { + reporter.ShowDialog(); + } + } + + /// + /// Creates a new instance of the log file reporter. + /// + /// True if we are submitting a crash, false otherwise (e.g. error). + /// True if the reporter show show a continue message, false if it should show an exit message. + /// The full version tag of this instance (like it is diplayed in the main menu). + /// The log to submit. + public CReporter(bool crash, bool showContinue, string vocaluxeVersionTag, string log) + { + InitializeComponent(); + _Crash = crash; + _ShowContinue = showContinue; + _VocaluxeVersionTag = vocaluxeVersionTag; + _Init(log); + } + + /// + /// Initilaize the gui. + /// + /// The Log to submit. + private void _Init(string log) + { + _Client.DefaultRequestHeaders.Add("accept", "application/json"); + _Client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"); + + this.Title.Text = _TitleText; + this.Message.Text = _Crash ? _MessageCrashText : _MessageNoCrashText; + this.NoUpload.Text = _NoUploadText; + this.GistAndIssue.Text = _GistAndIssueText; + this.GistOnly.Text = _GistOnlyText; + this.Submit.Text = _SubmitStep0Text; + this.SubmitedMessage.Text = _SubmitedMessageText; + this.Log.Text = log.Replace("\n","\r\n"); + this.Log.SelectionStart = 0; + } + + private async void Submit_Click(object sender, EventArgs e) + { + switch (_Step) + { + case 0: + if (this.NoUpload.Checked) + { + this.Close(); + } + else + { + _Step = 1; + this.Submit.Text = _SubmitStep1Text; + this.Submit.Enabled = false; + this.NoUpload.Visible = false; + this.GistAndIssue.Visible = false; + this.GistOnly.Visible = false; + this.Log.Enabled = false; + + string url; + if (this.GistOnly.Checked) + { + await Task.Run(() => _StartGistUpload(this.Log.Text)); + } + else if (this.GistAndIssue.Checked) + { + await Task.Run(() => _StartIssueUpload(this.Log.Text)); + } + else + { + _Step = 0; + this.Submit.Text = _SubmitStep0Text; + this.Submit.Enabled = true; + this.NoUpload.Visible = true; + this.GistAndIssue.Visible = true; + this.GistOnly.Visible = true; + this.Log.Enabled = true; + } + + } + + break; + case 2: + this.Close(); + break; + } + } + + private void NoUpload_CheckedChanged(object sender, EventArgs e) + { + this.Submit.Text = this.NoUpload.Checked ? (_ShowContinue ? _SubmitStep2CrashText : _SubmitStep2NoCrashText) : _SubmitStep0Text; + } + + /// + /// Updates the gui after the upload finished. + /// + /// Url to show, if null or empty an error message is shown. + private void _UploadFinished(string url) + { + if (this.InvokeRequired) + { + this.Invoke(new MethodInvoker(() => { _UploadFinished(url); })); + } + else + { + _Step = 2; + this.Submit.Text = _ShowContinue ? _SubmitStep2CrashText : _SubmitStep2NoCrashText; + this.Submit.Enabled = true; + this.SubmitedMessage.Visible = true; + this.Url.Text = string.IsNullOrWhiteSpace(url)?_LogUploadErrorText:url; + this.Url.Visible = true; + this.Url.SelectAll(); + } + } + + /// + /// Upload a log to gist and updates the gui. + /// + /// The log to upload. + private async void _StartGistUpload(string log) + { + // Upload log to github gist and show the link + _UploadFinished(await _UploadLogToGist(log)); + } + + /// + /// Upload a log to gist, opens an issue template on github and updates the gui. + /// + /// The log to upload. + private async void _StartIssueUpload(string log) + { + // Upload log to github gist + string gistUrl = await _UploadLogToGist(log); + + // Build issue body + string template = string.Format(_IssueTemplate, _VocaluxeVersionTag, gistUrl); + + // Build url for github issue template + string issueUrl = $"https://github.com/Vocaluxe/Vocaluxe/issues/new?title=Give%20me%20a%20meaningful%20title&body={ Uri.EscapeDataString(template) }"; + + // Start the browser with the issue template url + System.Diagnostics.Process.Start(issueUrl); + + // Show the link + _UploadFinished(issueUrl); + } + + /// + /// Uploads a log to a new anonymous gist. + /// + /// The log that should be uploaded. + /// The link to the uploaded file. + private async Task _UploadLogToGist(string log) + { + string json = JsonConvert.SerializeObject(new CGistCreateData() + { + Description = $"An { (_Crash ? "crash" : "error") } log submission for { _VocaluxeVersionTag }", + Public = false, + Files = new Dictionary() + { + { + "Vocaluxe.log", + new CGistFileData() + { + Content = log + } + } + } + }); + + var content = new StringContent(json, Encoding.UTF8, "application/json"); + string responseString = ""; + try + { + var response = await _Client.PostAsync("https://api.github.com/gists", content); + + responseString = await response.Content.ReadAsStringAsync(); + } + catch (HttpRequestException e) + { + CLog.Error(e, "Couldn't upload log", show:true, propertyValues:CLog.Params(json)); + } + + return _GetGistUrlRegex.Match(responseString)?.Groups[1]?.Value??""; + } + + /// + /// Helper class for gist creation. + /// + private class CGistCreateData + { + [JsonProperty("description")] + public string Description { get; set; } + [JsonProperty("public")] + public bool Public { get; set; } + [JsonProperty("files")] + public Dictionary Files { get; set; } + + } + + /// + /// Helper class for gist creation. + /// + private class CGistFileData + { + [JsonProperty("content")] + public string Content { get; set; } + } + } +} diff --git a/Vocaluxe/Reporting/Reporter.resx b/Vocaluxe/Reporting/Reporter.resx new file mode 100644 index 00000000..c223cf8e --- /dev/null +++ b/Vocaluxe/Reporting/Reporter.resx @@ -0,0 +1,1615 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + 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 + aIAAh1bHbIZXgAiGfox27HeACIVnw0eIZ4AAh+hnds54gACGjHdXeM+AAHzndjduiAAM58dxR3fsAAjn + eI+I944ABoyACIAAAAAAgAAAAAAAAP+/AAD4HwAA8AcAAOADAADAAwAAwAEAAMABAACAAQAAgAEAAMAB + AADAAQAAwAMAAIADAACAAwAAhn8AAN//AAAoAAAAMAAAAGAAAAABAAgAAAAAAAAJAAAAAAAAAAAAAAAB + AAAAAQAAAAAAAA8PDwAVFRUAHBwcACIiIgAqKioANTU1ADw8PABFKBkAaDceAEIsIABGOzYASDkxAEM/ + PgBJQDsAZkAsAHJFLwBlRjYAekszAHBQPwBCQkIAS0tLAFBFQABSSEMAVFRUAFxVUgBbW1sAY1VOAH5c + SgBjW1YAa1tTAGZfXABrYl0Af2BQAHBiWwBkZGQAamRiAGtoZgBqamoAcnJyAHl5eQCgTB4Ag0MiAItF + IgCCSCoAkkkjAJtOJgCXTigAl1AsAIZNMQCNUTIAmFc1AJNXOACbWzsArFUlAKBUKwCzViIAs1glALxc + JAC7XikAp1w1AL5gKQCqYDkAumk/AMVaHwDIXR4AzF8eANJiHgDVZR8A2mgeAN5pHQDgax4A4GwfAMVb + IADFXiIAwFslAMZgJADLYiIAzWgnAMNjKgDIZSkAyGgtANJmIQDUbCQA2GkgAN1rIADbbSIA3m8jANlu + JADXcCcA3HIlANdxKADadCkAxWcxAMNmNADFZzQAymwyAMRqOgDIazkAyG05AMhuPQDPcTMA0XQ0ANR4 + NgDbfjYA1ns5ANh+OgDhbiAA43MjAOZ4JQDhdygA43wqAOh9KACDWEMAi1tDAIRdSgCWYEQAm2NGAJdk + SgCIa1wAmWtTAJNtWQCsaEQAum1EALNtSgCicVcAr3hcALB5XACOcWIAw21BAMFwRQDIckQAyXRHAMJ5 + UwDNflMAw31ZANyDPQDngS0A64MsAO2KMQDwjTMAuoVqAKmHdwCuiXYAp4p7AN+LRwDhjEQA4Y1IAOiT + RADtmEcA5ZNKAOiWSgDsnEwA7J5RAO2hVADupVoA7qhdAMKBYADEhGMAwoRlAMOJbADRh2AA0YliANSQ + awDTkG0A1JBsAMOMcQDFlX4A1JNwANaYeADTm34A2Zx7AO+qYADwq2IAiomJAJWUlACbmpoAvpaCALad + jwCknpwApqGfALugkgCkoqEAqqWkAKyopgCuqqoAsKuoALCsqgCxrq4Ata+tAKm1qwCtuK8AsbCvALOz + sQC5trUAvLe1ALO6tAC5uLgAvLu7ALi+uAC/vLsAvr29AMmbhADHn4sA2qOGANqkiADdqYwAyqiXANyw + mgDgrZEA0biqAMG8ugDBwL8AyMK/ANPEvQDkxLQAwcDAAMTCwgDFxMQAyMLAAMnEwgDNxcEAyMfGAM7H + xQDFycUAzsnGAMrJyQDMysoAzczMANDJxQDQzs4A1NDOANLR0QDU0tIA1dTUANjU0gDY19YA29raAOLM + wQDr2tIA4N7eAOHg3wDw5d8A4eHhAOTi4gDl5eUA6OfmAOnp6QDs6+sA7u3tAPHx8QD08vIA9fX1APj3 + 9wD5+fkA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApWJBhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAADNYk1DRUVrSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOvT3p9USEVr + a2trQbLnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe6vX3919Ia2tGRkZGRqfYvMMAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4fD3+/v37kJIa0ZGRkZrVIfq5tm80wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAADr+/39+/v7qlRra0ZrRmtrSF/q6ubmybgAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5ff9 + /f39/fv1YWtIa2tra2tIa0LW6urm5tm85gAAAAAAAAAAAAAAAAAAAAAAAADe+/39/f396iMKQWtra0hI + SEhra1N5yerq5ubZweEAAAAAAAAAAAAAAAAAAAAAAN77/f39/fsnBBQyVG1rbWtrbWtrbWsqBCfm5ubm + 5rzmAAAAAAAAAAAAAAAAAAAAAPn9/f397QcGGCM4bW1ta21rbW1tbWs4GAUHyerm5tm8AAAAAAAAAAAA + AAAAAAAA7f79/f3mBBUjIxxTa21tbXBtbWttbXBCIh8VBMDm5ubZvAAAAAAAAAAAAAAAAADe+/39/eYF + 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 + AAD/AAAAAD8AAP4AAAAAHwAA/gAAAAAfAAD8AAAAAA8AAPwAAAAADwAA+AAAAAAHAAD4AAAAAAcAAPgA + AAAABwAA+AAAAAAHAADwAAAAAAcAAPAAAAAAAwAA8AAAAAADAADwAAAAAAMAAPAAAAAAAwAA8AAAAAAH + AAD4AAAAAAcAAPgAAAAABwAA+AAAAAAHAAD4AAAAAAcAAPwAAAAADwAA/AAAAAAPAAD+AAAAAB8AAPwA + AAAAHwAA+AAAAAA/AADmAAAAAD8AAMCAAAAAPwAAwAAAAAA/AADAAAAAAB8AAMAAAAAAHwAAwAAAAAAf + AADAAAAAAA8AAMABwAD/7wAA4AP4D///AADgA/////8AAPAH/////wAA/B//////AAD///////8AACgA + AAAgAAAAQAAAAAEACAAAAAAAAAQAAAAAAAAAAAAAAAEAAAABAAAAAAAAKCgoAC8vLwA/MiwAMTExADU1 + NQA5OTkAPDw7AD09PQBlOSMAXkAxAFxENwBWQjgAfkYnAEFBQQBFRUUAS0dFAElJSQBOTk4AWU1GAFNT + UwBVVVUAWVlZAF1dXQBlT0QAblBBAGhSRwBvWk8AclZJAHdcTwByXlMAel5QAGNgXgBtY14AfmhdAGFh + YQBlZWUAaGNgAGlpaQBsamkAbGxsAHlrZABwa2kAcnFxAHV1dQCETCwAi00qAJBRLACGUjgAjVo/AJZX + MgCdXTkAm18/AKZcLwC7XyYAol02AKZfNgCpXzIAr18wALphJwC9aC8AqWA0ALFiMQC0ZjUAtWg3AL9q + MAC+bTcAs2Y5ALBoOwDQYh8A1GQfANlnHwDeaR0A4GseAOBsHwDPYSAAzWUlAMppJwDFZyoAymgpAM5p + KwDRYyEA0mYhANRoIQDVbSUA3W0gANFrKgDacicA3nMlAN51JwDbcygA23UpANx4LQDffC8AxWYwAMhq + MgDOazIAyWwwANJvMADLdTkA03Q0ANB4OADWfDsA1n4+AOFvIADicSEA5HQjAOFzJQDidSUA5XYlAOZ6 + JwDheioA534qAOl+KQCEXUkAiF9KAJFeQwCCZFUAsnNPALt0TwCnd14At3xbAMVzRgDKdEEAyXVFAMR3 + TQDUfUcA0ntJANF/SgDLf1AA3oE2ANeAPwDZgT4A3YQ9AN+IPwDqgSsA6oIsAOyFLQDtiC8A7YkwAO2O + OADukDkA7pI9ALGAaADUgEcA2oRBAN+IQQDeikYA0oFMANOFUwDUiFcA1oxbAOOORADkkUUA5JRMAOiX + SwDrmksA7JtLAOmZTQDsnU0A6pxQAOyeUADtoFIA7aJVAO6kVwDtolgA7qVZAO+mXADvqF0AyodiANaN + YADPkm4A1ZNsAMaPcgDFk3kA2Zp0ANuffwDvqmAA8KphAPCsYwDwrWQAg4ODAIWFhQCWlZUAoJ+fAKid + mAC9qJ4AoaGhAKampgCopqUAoq+kAKqpqQCtqqkAr6+vALCtrACrta0AtLGvALOxsAC0srEAuLW0ALq5 + uQC+ubgAxZiBAMmeiQDJoIsA2qCBANWljADbpooA3qmLAMmpmQDfrJAA2q+ZAMCupgDOu7IAzb21AMK+ + vQDFwsEAxcTEAMjFwwDIxsYAysnJAM3KyQDLzMoAzMzLAM7NzQDSzssA0M7OANTPzQDO0M4A0dDPANTR + zwDd088A0tHQANTS0gDV1NQA2NPSANjW1gDb2NYA2djYANza2gDf3t4A6dvUAOje2QDi4eEA5OPjAObl + 5QDo5+cA6unpAO3t7QDy7+0A8fHxAPTz8wD29vYA+Pf3APr6+gD///8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADSf0tHSwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOPj + fEpJSUlSuu0AAAAAAAAAAAAAAAAAAAAAAAAAAOr4+PJSSklJSUnL3MfqAAAAAAAAAAAAAAAAAAAAAOv6 + /fr601VKSkpKaKnq49nHAAAAAAAAAAAAAAAAAADt/v7++LY6aWloaGhpX8fq3NzHAAAAAAAAAAAAAAAA + 6/7+/rwCE1JpaWlpaWlTAybj6tzHAAAAAAAAAAAAAOP+/v4sCCR0aW5pbm5pbmlyEg7Z3NzHAAAAAAAA + AAAA+Pr+LA4VFjZubm5ubm5ubjcWEQjc6tnjAAAAAAAAAOP6+scIKyZ1WG6Hboduh4duUCYsFSTq3McA + AAAAAAAA9vj4CBYkFkOHh4eHU26Hh4dZHRYkBMfq2+sAAAAAANv2+LwIFhYeWYeKh24wWYuHi4c0FhYR + JOrqyQAAAAAA1/X1FiQmLDiLi4qLUBE7i4uKik4sLCYE6urXAAAAAADj9fUEFhYeWYuLi4suCC2Li4uL + bhwWFgTJ6twAAAAAAOrc7wImJj2Mjo6OXAwOC1yOjo6MMyQkEbvc6gAAAAAA6sPcAiQfhaGioaE5CAgI + TqGhoqFlKiQOt8PqAAAAAADc7+4BFj6hoqKhhgoEBgUtoaGioplzFgi/9eoAAAAAANjq7ghymaKipKE+ + EQ4PDxqFoqKiokAmCNz17wAAAAAAwurqJUGipKSikhkWDg4RFUGiqKeikhgI+PXjAAAAAAC/4+qtm6Kk + qKI6IyYkFiMkMaCiqKegL7b4+N0AAAAAAADZ1maipKiikiImJiYsJiYphaSoqKRm5/j4AAAAAAAAAL3K + gWagpKAyFhUWFhYWFhZDpKiypKDO+usAAAAAAADOXkVLfXqSZSUmJiwmJiYmLHKasbKxpJD5AAAAAAAA + zc6wX2lpUH0JFiYkJCMmJiQkJGOktLSxoMwAAAAAAABpaWKueFBufMcOCBEWFRYWFRUIDaSxtLSolQAA + AAAAf4eHi4dfj3xT1uq8Jg4EBAQIJLzxkbG0tLGaAAAAAABijo6Ojo6CeXfR6urv7+/q8Pj4+PqspKiy + qKSqAAAAAJCioqKooqKZdrnd6urv6r719fj4+tSrqpBnkmIAAAAA0KGiqKiooqKQAAC/wtnq6u/jyccA + AAAAAAAAAAAAAAAAkKCoqKikkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAr2eTkpUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////g////AD///A + AP//AAB//gAAP/wAAB/4AAAP+AAAB/AAAAfwAAAD4AAAA+AAAAPgAAAD4AAAA+AAAAPgAAAD4AAAA+AA + AAPgAAAD8AAAB/AAAAfgAAAPwAAAD8AAAA+AAAAPgAAAB4AAAAeAMAf/wH///+D/////////KAAAABAA + AAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA/Pz8ART88AEhAPABxSzUAR0dHAEpK + SgBOTk4AUkhDAFJSUgBYWFgAXl5eAH5bRQBnX1sAc15SAGdhXgBiYmIAZmJhAGVlZQBrZmMAaGhoAHFw + cAB8e3sAfn19AJ5aLgCGVjUAqGE0AK1vPwDdaR4A4W0fAMVrLADPbywA1m0qAMtxLADacycA1nkvANN0 + NADVfT8A4W8gAOR0IwDkdSQA5nwpAOh8KACAX0sAlmVEAJlqSQCBY1IAompEAKVyTACwcUAAunJHALV8 + UwDPdEAAznhBAMF8TwDSgD4A3oU5AOqCLADtiDAAk4F4ANKGRgDZhkYA24tFANmMSgDqlkcA7ZdEAO2Y + RgDklE0A7ptKAOudTwDklVAA4ZVWAOSXVQDmmFAA6Z5XAO2iVQDupVgA76dcAO+pXgDZkGcA3ZdlANKa + dADvqmAA8K1lAIODgwCnoZ8ApqWlAL2upgCwr68Avbu6AL+9vADIn4cA3KiCAMChkADjr40AwbWvAMq6 + swDHyscAycrIAMzKyQDOzcwAzdDNANPR0QDW1dUA3tnXANrZ2QDe2tkA3t3dAObXzwDk4eAA5OTkAOrq + 6gDu7OwA8O7uAPHx8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAABPAAAAAAAAAAAAAABt + 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 + qqtjoyYthvnnLgHQEoYoM7YQohhmzOrsD4lieO3aqqeqKsvcKMFAwosxAoJPT2jHX1OHgi8Y7/3q938i + yVLx9791f0t6emoyMMcvINE0PzHzJ66bmBjn+uuvZ2xsjOzsbJYtW0Zra6uW0HLv3r2UlpbS09PDypUK + aPjee+9hsVjYvn07LpeL8fFxysvLmZyc5MiRI9TU1NDW1qblxGtqauKdd97hzJkz3HPPPWRkZPDWW2/R + 1tbGnz8XpGc80t9JubmkrIh1lkmoFptvzr4kSswccbUEbQ48fZFBwiT4uKv2JOfOneHixYtcc801mM1m + zp07x/LlSttPnDhBVlYWJSubeLEnkvhD0OvJbGxYuOULPNvVEgBSMITz1GmVawB4/A+yOdfxPvX19QiC + wIEDBygoKGDXrl2MjIwwPT1NRUUFFy9e5MSJE6xZs0YzC1f9Sz788EP6+/u5+eabycrK4tChQ9TUKB6W + //Iv/0JeXh6VlZUcO3aMvr4+zdU8Pz+fJ574+TwgoNL4xTK/JIW1JYzf78PvD+IPBMSuC2MfhsPSzA// + +A++DPiBECAuVgBcxXgAyi8sS0M6QVc0ZXd60tNTk5dq6DNfnXqeICjWgMuWLdOi2er1et544w38fj86 + nY7Ozk52797NAw88wMGDB3n++eexWhVjnjfffJP77ruPqqoqnn32Wd555x2CwSCvv/4669at48EHHyQU + CvGtb32LwUFlKvmb3/yGm266ib/8y7/kL/7iLxj9+CyUfl1rVWBqStEIZMTG6ZsD92kFV6oWYF7NgFps + ysnCNzKKNIt5BOVknn6lnelBJR1ad3c3DQ0N7Nu3j7feeouTJ0/i9/sRBIEMawnkR65pys7Srj1vW5Ze + tTAtQgMQckzHML/g6ePVX73En//5n/P444/z/PPPa/EQXnvtNb7+9a9TVlbG008/zfvvv4/f7+eVV16h + tbWVL3/5y3i9Xv74j/+Y/n5FcB46dIibbrqJr3/96/zP//k/ee655wgGlVh+FotFUy3a7XbN4KqysnJJ + hj6JmF8N/KGu/yVJEQiBQMgjy6DX64dQQoDN1zXz0lUFAQE5HAr2IMDYuNV9OYY+89XFA4rKCKZM36xW + awwwEwqFOHr0KBcuXODEiRMa8wMMDw9z5swZTpw4QWdnpwbwBYNBBgYGeP/99+ns7NSYHxQQ8YUXXsDv + 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 + 9//29vb/9PT0//Py8v/w8PD/05t+/9lpIP/hbiD/4Gwf/+BrHv/fax7/32se/+BsH//gbR//4W8g/8Nm + NP/V09P/09LS/9HQ0P/NzMz/vr29/6aioe3Fvrtu1dHQCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4rqoNtaunoOvq + 6vv6+fn++/r6/vn5+f/49/f+9vb2/vT09P/k4uL+xWo6/uJwIf/ibyD+4W4g/uFtH//gbR/+4G0f/uFu + IP/hbyD+4nAh/spgIf/TxL3+1dTU/9PS0v7Rz8/+zs3N/8bExP6tqqr5tq+smdnV1A4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALas + qA22rKit8vLx/f39/f79/f3+/Pz8/vr6+v/5+fn+0tLS/mFhYf9CLCD+zGAg/uNyIv/jcSL+4nAh/uJv + If/ibyH+4m8h/uJwIf/icSL+43Ii/tRnIf+TbVn+v76+/9XU1P7T0dH+0M/P/87Nzf7Jx8f+sbCv/LOs + qKPX09INAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAtqyoB7KopKLy8vL9/Pz8//7+/v/+/v7//f39/+/u7v9xcXH/ICAg/0FBQf+OUzT/220i/+R0 + I//jcyP/43Ii/+NyIv/jciL/43Ii/+NyIv/jcyL/5HQj/95wI/+DQyL/ISEh/3BwcP/Pzs7/0tHR/9DP + z//OzMz/ycjI/7Gvr/y3sK2W1dHPCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAqZ6Zee7t7fv6+vr+/Pz8//39/f7+/v7+0dHR/jc3N/80NDT+VFRU/mdk + Yv+2VyH+5HYk/uV1JP/kdST+5HQj/uR0JP/kdCP+5HQj/uR0JP/kdST+5XUk/uV2JP+wViT+VlVU/y4u + Lv49PT3+ubi4/9LR0f7Qzs7+zszM/8jHx/6tq6r5xL26bc7KyAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACpnpk/3drY8ff29v/5+fn/+/v7//z8/P/ExMT/Jycn/0lJ + Sf9lZWX/Y2Nj/39dTP/SZyL/5ngm/+V4Jf/ldyX/5Xcl/+V3Jf/ldyX/5Xcl/+V3Jf/ldyX/5Xgl/+Z4 + Jv/LYSH/cGFZ/15eXv9ISEj/JCQk/7Szs//S0dH/0M7O/83MzP/FxMT/p6Oh7NTPzDYAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK+loQu9trLK8/Ly/vb29v/49/f++fn5/8rK + yv4mJib+T09P/lVVVf9jY2P+Z2dn/plOJ//idib+53om/ud5Jv/neSb+53km/ud5Jv/neSb+53km/ud5 + Jv/neSb+53om/ud6Jv/YbST+g1hC/2hoaP5gYGD+Pz8//ykpKf65t7f+0tDQ/8/Ozv7Ny8v+v729/6yl + orzU0M4LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKKXkmnr6ur88vLy/vT0 + 9P/29vb+6enp/zQ0NP5CQkL+Z2dn/m5ubv9paWn+Y1VO/slhI//ofCj+6Hwo/uh8KP/ofCj+6Hwo/uh8 + KP/ofCj+6Hwo/uh8KP/ofCj+6Hwo/uh8KP/keCf+qWA7/11dXf5qamr+aWlp/0JCQv47Ozv+zczM/9HQ + 0P7Pzs7+y8rK/7GurvnHwL1aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqJ6aCr+4 + tdjv7u7+8fDw/vPy8v/19PT+ZWVl/zk5Of5jY2P+YWFh/k1NTf9gYGD+m2NG/t1zJv/pfyn+6X8p/ul/ + Kf/pfyn+6X8p/ul/Kf/pfyn+6X8p/ul/Kf/pfyn+6X8p/ul/Kf/pfyn+v1og/2xoZv5jY2P+U1NT/1NT + U/44ODj+bW1t/9PS0v7R0ND+z83N/8TCwv6qo6DH0s7MCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAnZKNVOfl5fzt7Oz/7+/v//Hx8f+/v7//LCws/0hISP9ZWVn/aGho/2tra/9mX1z/vFsk/+qA + Kv/qgSv/6oEr/+qBK//qgSv/6oEr/9VtJf/ddCf/6oEr/+qBK//qgSv/6oEr/+qBK//qgCv/1Gsk/2VF + Nf9lZWX/a2tr/2dnZ/9HR0f/Hh4e/7y7u//T0tL/0dDQ/83MzP+xr673ycPARAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACRiIQBpZ2ZrOno6P/r6ur+7e3t/u/v7/9RUVH+NjY2/2ZmZv5nZ2f+X19f/kpK + Sv+Za1P+2XAm/uuDLP/rgyz+64Ms/uuELf/rgyz+6IAs/rppP/+9XSX+64Ms/uuDLf/rgyz+64Ms/uuD + LP/rgiz+43sp/5tbO/5iYmL+UFBQ/1ZWVv5iYmL+PDw8/15dXf7V1NT+09LS/9HPz/7CwMD+ubKwl8jD + wQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB7c28VxL685+fm5v/p6Oj+6+vr/tHR0f8hISH+V1dX/1BQ + UP5ZWVn+ZmZm/m1paP+0WSb+6oIs/uyFLf/shS7+7IUu/uyFLv/shS3+2nMn/nFFL/+XUCz+538r/uyF + Lv/shS7+7IYu/uyFLv/shS3+64Qt/71eKP5mY2H+aGho/2ZmZv5aWlr+Pz8//yoqKv7My8v+1dTU/9PR + 0f7Mysr+qaKg29DMygkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwZ2ND3NrZ+uXk5P/n5ub/6enp/4iI + iP8mJib/YWFh/21tbf9sbGz/Z2dn/4NZRP/XcCf/7IYu/+2HLv/thy//7Ycv/+2HLv/shy7/xV8i/1FI + RP9mQCz/2HEo/+yHLv/thy//7Ycv/+2HL//thy7/7IYu/9NrJf+OcWL/W1tb/19fX/9qamr/Z2dn/yIi + Iv+VlJT/19bW/9XU1P/R0ND/tLGw9cjBvjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAByamZy4eDg/uPi + 4v/l5OT+5+bm/kxMTP9JSUn+ZWVl/1paWv5gYGD+bGlo/rldKv/rhS7+7Ygw/u6JMP/uiTD+7okw/u2I + MP/ngS3+jkYh/j8/P/9KQDv+xWAi/u2IMP/tiTD+7okw/u6JMf/uiTD+7Ygw/+R+LP6YVzX+aGho/2dn + Z/5hYWH+VlZW/0NDQ/5eXl7+2djY/9fW1v7U09P+wcC//r62s1gAAAAAAAAAAAAAAAAAAAAAAAAAAF9X + VAF6cm+Y397e/uHg4P/j4uL+5eXl/iwsLP9NTU3+ampq/2tra/5sbGz+iVxG/thyKP/tiTD+7oox/u+L + Mf/uizH+7oox/u6JMP/UbSf+Zkc3/kFBQf87Ozv+oFMq/uuGL//tijH+7osx/u+LMv/uizH+7oox/+2I + MP6/Xib+aGNh/2VlZf5ra2v+a2tr/05OTv46Ojr+29ra/9nY2P7W1dX+yMfH/762s30AAAAAAAAAAAAA + AAAAAAAAAAAAAF9YVQKOhoOx3dzc/9/e3v/i4eH/3Nvb/xoaGv9XV1f/Z2dn/2BgYP9sZmP/t1kk/+2J + Mf/vjDP/740z//CNM//vjTP/74wy/+yIMf+xWCb/Pz07/z8/P/89PT3/ckYv/9x2Kv/uizL/74wz//CN + M//vjTP/74wz/+6LMv/XcSj/f1xJ/2VlZf9kZGT/X19f/1ZWVv8oKCj/2tnZ/9zb2//Z2Nj/zs3N/7au + q5W6s7ABAAAAAAAAAAAAAAAAAAAAAEdCPwOSjIm+2NfX/q24r/+zurT+zczM/h4eHv9bW1v+a2tr/2dn + Z/6OW0H+2384/u2ZSP/tnEv+7p5N/u6eTf/tnEv+7ZlI/tt+Nv96SzP+Pz8//kFBQf8+Pj7+S0A6/sln + Kf/tmEf+7ZtK/u6dTf/unk3+7p1M/+2bSf7okUL+p1w1/2hoaP5qamr+Z2dn/1xcXP4hISH+1NPT/6i1 + qv64vrj+0tHR/7exraK4sa4BAAAAAAAAAAAAAAAAAAAAAEQ/PQORi4i/1tTU/rC2sf/DyMP+ysnJ/h4e + Hv9WVlb+Z2dn/2tiXf67XSb+7JlK/u2eTv/toFH+7aFS/u2gUf/tnk7+7JpK/r5gKf9IQT3+Pj4+/j8/ + P/86Ojr+PT09/p5PJf/plUf+7J1O/u2gUf/toVL+7qBS/+2eT/7sm0v+yWgs/3BkXv5nZ2f+ZWVl/1hY + WP4gICD+09LS/7K5sv7Hy8f+1dTU/7mxrqK4sa4BAAAAAAAAAAAAAAAAAAAAAEdCQAOLg4C019bW/9jX + 1//a2dn/1NPT/xkZGf9dXV3/ampq/41QMP/fhz7/7Z1O/+2gUf/uolP/7qJT/+2gUf/tnU7/34Y+/4dO + Mf89PT3/PT09/0FBQf9AQED/QUFB/3FFL//bgDr/7Z1O/+2gUv/uolT/7qJU/+2gUv/tnU7/3YQ9/5dk + Sv9nZ2f/YGBg/1paWv8pKSn/4N/f/+Hg4P/f3t7/1tXV/7avrJUAAAAAAAAAAAAAAAAAAAAAAAAAAFJM + SQJybGme1dTU/tbV1f/Y19f+29ra/igoKP9GRkb+bl1V/8ZmK/7snE3+7aBR/u2iVP/to1X+7aJU/u2g + Uf/snE3+w2Qq/kY7Nv8+Pj7+Pz8//j09Pf82Njb+Ojo6/lBFQP/IaS3+7J1N/u2gUv/tolT+7qNV/+2i + VP7tn1H+7JtM/69XJv5nZWT+Z2dn/0lJSf44ODj+5eXl/+Pi4v7h4OD+19bW/7Cno30AAAAAAAAAAAAA + AAAAAAAAAAAAAEhCQAFcVVN71NPT/tTT0//W1dX+2djY/kJCQv9QUFD+kkok/+SPRf7tn1H+7qJU/u6k + Vv/upFb+7aJU/u2fUP/giED+k1c4/kxMTP87Ozv+Ojo6/kJCQv9DQ0P+Q0ND/kdGRv+rYDj+6JVJ/u2g + Uv/uo1X+7qRX/+6kVv7tolT+7Z5Q/9N2Nf5/YFD+WFhY/0lJSf5fX1/+6Ofn/+Xl5f7j4uL+19bW/qmf + m1gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHQD1PzcvL+9LR0f/U09P/19bW/3d2dv9FKBn/z3Ey/+2f + UP/uolX/7qRX/++lWP/upFf/7qFU/+2dT//EZy7/VElD/1VVVf9NTU3/R0dH/0BAQP82Njb/QkJC/1hY + WP+EXUr/2H05/+6fUv/uo1b/76VY/++lWP/upFf/7qFT/+eUSf+SSSL/YmJi/yAgIP+bmpr/6unp/+fn + 5//l5OT/zsvK9ayini8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/OjggtrGv7NHPz//S0dH+1dPT/rm1 + tP+gTB7+6ZdL/+2hVP7upFf+7qZZ/u6lWf/upFf+7aBT/t2GP/+FTTH+bm5u/mZmZv9RUVH+TU1N/ltb + W/9iYmL+ZWVl/lZWVv9cVVL+w2Ut/u2fUf/to1X+7qVZ/+6mWv7upVn+7qNW/+2fUf7LbTD+SDkx/yws + LP7e3t7+7Ovr/+np6f7l5eX+u7Sx3rOrqAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCPjwFhoB9u9DO + zv/Qz8/+09HR/smbhP/XfDn+7Z9S/+2jV/7upln+7qZa/u6lWf/to1b+7J5R/rxfKv9jW1b+SUlJ/l5e + Xv9nZ2f+aGho/l5eXv9LS0v+V1dX/mdnZ/9oaGj+iUUj/uaUSf/tolX+7qVZ/+6nXP7up1v+7qVZ/+2i + Vf7gi0P+gkgq/15eXv7v7+/+7e3t/+vr6/7l5eX+sKejmQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABEPz0BSUJAZ87MzP3Ozc3/z8XA/8VnMf/rmkz/7aBT/+6jV//uplr/7qZa/+6lWP/uoVT/2H88/3ZH + L/9paWn/bW1t/2dnZ/9YWFj/V1dX/2lpaf9sbGz/aWlp/1hYWP9XV1f/iGtc/9N4Nv/uolX/7qVZ/++o + XP/vqF3/7qdc/+6kWP/toFP/uVwm/8vFw//x8fH/7+/v/+3s7P/d29r4q6CcQwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAODMyGKagnuDNzMz+woJh/th8OP/smUz+7p9S/+6iVv7upFn+7qRZ/u6j + Vv/qmk/+qlMk/mtoZv9iYmL+VlZW/mVlZf9qamr+a2tr/mVlZf9XV1f+YGBg/mpqav9ra2v+Z2Zl/rNa + J//tn1P+76VZ/++oXf7vqV/+76le/++nW/7uo1f+2oE+/9ywmv7z8/P+8fHx/+7t7f66sq/Ktq2pBgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa0o5AlVMSX/MxMH9w4xx/8KBYP/DbkD/z3Ey/+eV + TP/uolX/7qFV/+6gU//RdDT/cFA//2FhYf9nZ2f/Z2dn/2ZmZv9dXV3/XV1d/2dnZ/9nZ2f/ZmZm/11d + Xf9dXV3/Z2dn/5ZgRP/gi0X/7qRZ/++oXf/wqmD/8Kpg/++pXv/vplr/7J9T/8hyRP/08vH/8/Ly/+jn + 5vuonZhbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACvThwHqVAiZqxPHtPFWh/+yFwe/sVb + IP/CbED+w4Fg/8FvRf7Zfzz+7Z1Q/uSPRv+XTij+bGtr/m1tbf9qamr+YmJi/mtra/9sbGz+bGxs/mxs + bP9jY2P+aGho/mxsbP9sbGz+bGxs/mxcVP/MbjL+7qNY/+6oXf7vqmD+8Kti/++rYf7vqF7+7qRZ/9R5 + OP7kxLT+9PT0/7uzsMG2raoHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALxUHgO8Ux5XvFMe2Mld + H/7ZaSD+4W8g/uJvIP/gbiH+0mQg/8FrPf7CeVP+1Xg3/sBhKf9pWVH+YWFh/mVlZf9nZ2f+Y2Nj/mdn + Z/9iYmL+YmJi/mhoaP9jY2P+ZmZm/mJiYv9iYmL+aGho/mRjY/+hVS3+6ZtR/++nXP7vqmH+8Kxj//Cs + Y/7vqmH+76dc/+iaUf7Mf1f+39za8audlzkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwlkfD71V + HtK9VR7Av1YebsBXHkm8VB6Zwlke89hpIf/jcyP/5HQj/95vI//CaDj/w3xY/2g3Hv8+Pj7/Wlpa/2ho + aP9ra2v/ZmZm/2tra/9oaGj/aGho/2xsbP9nZ2f/ampq/2hoaP9oaGj/bGxs/2dnZ/9+W0n/1308/++m + W//wqmD/8K1k//GtZf/wrGP/8Kpg/++lWv/PcjX/qXthiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAvlYeldpvJP/kdyb+12wj/sZdIPi7VB6lcTQTUIpLK8nGYCX+33Ik/+Z5Jv7abyT+xIRj/r+8 + u/9ISEj+IiIi/kVFRf9YWFj+Wlpa/mVlZf9lZWX+ZmZm/mdnZ/9fX1/+ZGRk/mVlZf9lZWX+Y2Nj/lBQ + UP9DPz7+slgl/+6kWf7vqV/+8Kxj//CuZv7wrmX+8Kxj/++oXv7llU3+vVsmpgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADBWB8Uxl0g9Ol+Kf/pfin+6X4p/ul/Kf/hdyj+ymIi+5NIIryaeGbcwXBF/tZs + JP7ofin+x2Em/tHIxP/U09P+oaCg/j4+Pv8aGhr+NjY2/klJSf9OTk7+VFRU/l9fX/9fX1/+XFxc/k1N + Tf9AQED+MjIy/h4eHv9SUlL+uoVq/9+LR/7vp13+8Kti//CuZf7wrmb+8K1k/++qYf7vpVv+ymou+sRj + Kh8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAWB9Q1m4m/+uELf/rhS3/7IUt/+yFLv/shS7/7IUu/+B5 + Kv+7WiT3qYFt7sOJbP7OZiT/1Gwl/8qol//T0dH/1dTU/9fV1f+5uLj/eXl5/0RERP8hISH/FhYW/xYW + Fv8PDw//ExMT/ycnJ/9QUFD/jIuL/9bW1v/08/P/8OXf/8psMv/vpVr/76lf//CsY//wrWX/8K1k//Cq + Yf/vp13/4Y1I/8BaI40AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAWSBw3Xcr/u2KMv/uizP+7owz/u+M + NP/vjDT+74wz/u+MM//riTL+zWgn/qxyVPK9lH/5wFsl/saWf//Qz8/+09LS/tXU1P/X1tb+2tnZ/tzb + 2//f3t7+2NfX/s7Nzf/Q0ND+4ODg/urp6f/s7Oz+7u7u/vDw8P/y8vL+9PT0/9GRcP7jkEr+76Vb/++o + X/7wqmH+8Kph/++pYP7vplz+7qJX/8ViKe/FYigTAAAAAAAAAAAAAAAAAAAAAAAAAADAWSFq24A5/u2b + S//tnk/+7aBR/u6iU//tolP+7aJS/u6hUv/toFH+7J5O/9Z7OP6mXDbsnnhl4cefi/3Qz8/+0dDQ/tPS + 0v/V1NT+2NfX/trZ2f/c29v+3Nzc/rW9tv/Mzsz+5uXl/ujn5//q6ur+7Ozs/u/u7v/w8PD+8/Ly/+va + 0v7KazH+5ZJL/+qdVP7upFr+76dc/++mW/7vpFn+7qFV/9yEP/7AWyN3AAAAAAAAAAAAAAAAAAAAAAAA + AADBWyI+0nQz/u2cTv/toFL/7qNW/+6lWP/upVj/7qVY/+6kV//uo1b/7aFT/+2eT//cgzz/ok0i0FlM + RommoZ/h0c/P/dPS0v/U09P/1tXV/9jX1//a2dn/0tHR/6q2rP/FysX/5OPj/+bl5f/o6Oj/6+rq/+3s + 7P/v7+//7Ovr/LawrdeATjSIulwojsBbJKzBXCXLw2An6clpLv7QcjT/1no5/9qBPv++WCHivFYfCAAA + AAAAAAAAAAAAAAAAAADCXSQHwVsj3+ubTf/toVT+7qRY/u6mWv/vp1z+76dc/u+nW//upVn+7qNW/+2f + Uv7rmUv+wVwk5oRRNQc6MS0aQTs4aoV/fL63srHt0c/P+9nY2P7a2dn+3Nvb/tfW1v/e3d3+4uLi/uXl + 5f/n5ub+4d/f+sS/veiLhYKyUktIW1lQTA+KZVMBAAAAAAAAAAAAAAAAAAAAAMZkKgnGZConxGEnRsBb + JGS+WCGDvFQfLAAAAAAAAAAAAAAAAAAAAAAAAAAAwVwjZtV4N//uoFT+7qRY/u+mW//vqF3+76hd/u+n + Xf/uplv+7qNX/+2fUv7UeDb+vlskaAAAAAAAAAAAREA+AT06OAc7NzUjQDo3U1NNSoBvaWeiiIF/uJGK + iMSRi4nCioOBtXBqZ51ZU1B4SkRBSktGRBpYVFIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxGEnAb9ZIqTYfzz/7qJW/++l + Wv/vplz/76dc/++mXP/vpVn/7qJW/9h/PP/AWiOlxmQpAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEtG + RAFHQ0EDSUVDBE9LSgVQTEoFTUlHBFpVUwJXUk8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMVi + KAPBXCSEyWgt9+CMR//sn1X+7aFX/uueVP/gjEf+yWgs98FcJIbHZisEAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAxWIpIsJdJYDAWiO4wVwkyb9ZI7XCXSWAxmUqIQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///+P//wAA///8A///AAD//8AA//8AAP//AAAf/wAA//wAAA// + AAD/8AAAA/8AAP/gAAAB/wAA/8AAAAD/AAD/gAAAAH8AAP8AAAAAPwAA/wAAAAAfAAD+AAAAAB8AAPwA + AAAADwAA/AAAAAAPAAD4AAAAAAcAAPgAAAAABwAA8AAAAAADAADwAAAAAAMAAPAAAAAAAwAA8AAAAAAD + AADgAAAAAAMAAOAAAAAAAQAA4AAAAAABAADgAAAAAAEAAOAAAAAAAwAA4AAAAAADAADgAAAAAAMAAPAA + AAAAAwAA8AAAAAADAADwAAAAAAcAAPAAAAAABwAA+AAAAAAHAAD4AAAAAA8AAPAAAAAADwAA4AAAAAAf + AADAAAAAAD8AAMAAAAAAPwAAgAAAAAAfAACAAAAAAB8AAIAAAAAADwAAgAAAAAAPAACAAAAAAAcAAIAA + AAAeBwAAwAGAAP//AADAAfgH//8AAOAD/////wAA+A//////AAD///////8AACgAAAAgAAAAQAAAAAEA + IAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL5VHhC9VB5NvFQebQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAANbRzwLOxL8cy5yDUslwQJPIXSDPz2Ae/NlnH//MXx7105+DCQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjBvhXFvrtxyMPBytDOzffKdkb+4Gwf/99qHv/faR3/32oe/9Rk + H/+0nJHfv7q4j87IxizV0c8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMG5tQfDu7hx1NHQ6ezs7P/v7+//6N7Z/9FjIf/gbB//32oe/99p + Hf/fax7/3Wof/8meif/Lysr/trOy97u1s53Tz80aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC1q6cbysTCu/Ly8v749/f/9fX1//Ly8v/ar5n/3Wwg/+Ft + H//gbB//4Gse/+BtH//hbyD/yodi/9TT0//Rz8//xsXF/7CsquDMx8VCAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsaeiIdXRz9f7+/v//Pz8//r6+v/n5+f/hYWF/69f + MP/jcSL/4nAh/+FvIP/hbyD/4m8h/+NxIv/IajL/uLe3/9TT0//Qz8//y8rK/7KvrvTLxsNTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK2inRTTzszT+/v7//7+/v/9/f3/oqKi/zEx + Mf9ZTUb/0mYh/+R0I//jcyL/43Ii/+NyIv/jcyL/5HQj/9RoIf8/Miz/aWho/87MzP/Qz8//zMrK/7Kv + rvPMx8VBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjmJIBwLi1q/f39//6+vr/+/v7/3Nz + c/8+Pj7/Z2dn/5FeQ//jdSX/5Xcl/+V2Jf/ldiX/5XYl/+V2Jf/ldyX/4XMl/4RcSP9PT0//QkFB/8XE + xP/Qzs7/y8nJ/7CsquDQy8kZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKuhnVTu7u389vb2//n4 + +P93d3f/PT09/1VVVf9ZWFj/u18m/+d7J//neif/53on/+d6J//neif/53on/+d6J//neyf/ol02/1lZ + Wf9JSUn/Pj4+/8zLy//Pzs7/xsTE/7m0sZvRzMoBAAAAAAAAAAAAAAAAAAAAAAAAAACbkIsEycTB0/Hw + 8P/08/P/r6+v/zo6Ov9ycnL/bGxs/4JkVf/dcyb/6X4p/+l+Kf/pfin/6X4p/+l+Kf/pfin/6X4p/+l+ + Kf/KaCn/bGpp/3BwcP9TU1P/ZmVl/9LR0f/Pzc3/trOz98nEwSoAAAAAAAAAAAAAAAAAAAAAAAAAAJ2T + j0fp6Oj97u7u/+3s7P8+Pj7/Wlpa/2FhYf9eXl7/s2Y5/+qBK//qgSv/6oIr/+qBK//NZyf/534q/+qC + K//qgiv/6oEr/951J/93XE//YGBg/19fX/8zMzP/tLOz/9LR0f/KyMj/vbi1jQAAAAAAAAAAAAAAAAAA + AAAAAAAApqCdm+jn5//r6+v/oaGh/0BAQP9aWlr/XV1d/3JeU//acif/7IQt/+yELv/shS3/43sq/4ZS + OP/bcyj/7IUt/+yFLv/shC3/6oMs/5tfP/9bW1v/XFxc/0tLS/9lZWX/1dTU/9HQ0P+0r63c0MrIBQAA + AAAAAAAAAAAAAFlSTwK5tLLa5eTk/+jo6P9dXFz/X19f/25ubv92dnb/pl82/+yGLv/tiC//7Ygv/+2H + L//KaSf/S0dF/7phJ//thy//7Ygv/+2IL//shy//xWcq/3Nycf90dHT/aWlp/zMzM//W1dX/1dPT/8C+ + vfrEvbomAAAAAAAAAAAAAAAAVU5KFc/My/Xi4eH/5eTk/zQ0NP9dXV3/W1tb/29aT//bdSn/7oox/+6L + Mf/uijH/7Igw/4tNKv86Ojr/hUwt/+yIMP/uijH/74sy/+6KMf/geiv/clZJ/11dXf9ZWVn/NDQ0/7q5 + uf/Y19f/y8rK/7y1sk4AAAAAAAAAAAAAAABVTUop19XV+87Qzv/b2tr/KSkp/2xsbP9lZWX/qWA0/+2O + OP/ukTr/75I6/+6QOf/ceC3/VkI4/0FBQf9cRDf/33wv/+6QOf/vkjr/75E6/+2OOP+dXTn/ZGRk/2Rk + ZP9ISEj/oJ+f/8vMyv/S0dH/uLGuZwAAAAAAAAAAAAAAAElCPy/U09P9q7Wt/8zMy/8vLy//YmJi/3pe + UP/dhDz/7Z5O/+6gUf/tn1D/7JtL/6lfMv89PT3/Ozs7/zw8O/+/ajD/7Z1N/+2gUf/uoFH/7Z1N/9F4 + OP9wa2n/Y2Nj/0RERP+WlZX/q7at/9XU1P+5s7BtAAAAAAAAAAAAAAAAS0RBJs/NzfrY19f/2NfX/ygo + KP9eXV3/sWIx/+ydTv/toVL/7qJU/+2fUP/fiD//XkAx/zc3N/84ODj/Nzc3/4RMLP/rmkv/7aFS/+6i + VP/toFH/5ZFF/4hfSv9ZWVn/PDw8/6qpqf/h4OD/2djY/7WtqmAAAAAAAAAAAAAAAAA/OjcQwLy78tXU + 1P/Z2Nj/PT09/4VeSv/jjkT/7aFT/+6jVv/uolX/7Z5Q/7RmNf9HR0f/QkJC/0ZGRv9GRkb/aFJH/92F + Pv/toVP/7qNW/+6jVf/tn1D/tWg3/21tbf85OTn/0M/P/+Tj4//a2tr+rqWhQQAAAAAAAAAAAAAAADUx + LwKgm5nP0tHR/9bU1P9oY2D/vWgv/+2gUv/upFf/7qVY/+6iVP/fiEH/blBB/1ZWVv9CQkL/QUFB/0dH + R/9UVFT/vm03/+2gUv/upFj/76VY/+6iVf/dhT//ZU9E/zw8PP/r6ur/5+fn/9LPzvaqoJsYAAAAAAAA + AAAAAAAAAAAAAHFsao3Qz8//09HR/8aPcv/ol0v/7qNX/++mWv/upVn/7aBT/6ZcL/9hYWH/aWlp/2Bg + YP9eXl7/X19f/2FhYf+NWj//6ptP/+6lWP/vp1v/7qVZ/+2fUv+QUSz/g4OD/+3t7f/q6en/wry5yAAA + AAAAAAAAAAAAAAAAAAAAAAAASUNBN8jGxvvNvbX/1nw7/+2fUf/upFj/7qZa/+6jV//agz//fmhd/2tr + a/9qamr/ampq/3Jycv9wcHD/a2tr/3lrZP/YgD3/7qRY/++oXf/vqF3/7qRY/9B5OP/d08//8PDw/+vr + 6/+1rKhsAAAAAAAAAAAAAAAAAAAAAAAAAABUOy8Ci4WDu8WYgf/Lf1D/1n49/+ucUf/uolb/65xQ/5ZX + Mv9bW1v/VlZW/1xcXP9XV1f/Wlpa/1lZWf9XV1f/XFxc/7BoO//uo1f/76le//CqYP/vqF3/6pxR/9qm + i//z8vL/1tLQ6KyinhEAAAAAAAAAAAAAAAAAAAAAulIdBq5QH4LBWh/r0GIf/85kJP/Gc0b/xXRH/+OO + Rf/Odzr/bWNe/2lpaf9sbGz/c3Nz/2xsbP9oaGj/aWlp/2xsbP9zc3P/hF5L/+WVTf/vqF7/8Kti/++r + Yf/uplv/1IBH//Lv7f61rKhtAAAAAAAAAAAAAAAAAAAAAL9WHga9VR6OvVUehcBXHpHKXh/p3m4h/+Ny + Iv/OaSv/xHdN/2U5I/9YWFj/aGho/2BgYP9iYmL/YGBg/2hoaP9mZmb/YGBg/2JiYv9jYF7/yXQ5/++n + Xf/wrGP/8K1k/++qYP/ml0//uYRpxLGVhwYAAAAAAAAAAAAAAAAAAAAAwFgfeOJ2Jv/fcyX/z2Ui7pxH + GpWQUjLI0Wsq/uZ5J//KdEH/s7Gw/0dHR/83Nzf/Tk5O/1lZWf9XV1f/Xl5e/1xcXP9WVlb/VFRU/z4+ + Pv9+Rif/7KJY//CrYv/wrmb/8K1k/++nXf/LbTLVuWIyAQAAAAAAAAAAAAAAAAAAAADMZCPV64Ms/+uD + LP/shC3/6IAs/8dmJ/SteV/wyXVE/tVtJf/Ou7L/1dTU/6inp/9oaGj/RERE/zExMf8xMTH/MDAw/z4+ + Pv9lZGT/pqam/+nb1P/Zg0L/76lf//CtZP/wrWX/76lg/+GQSv/CXiVLAAAAAAAAAAAAAAAAAAAAANJt + KvTtkDr/7pI9/+6TPv/ukz3/7pI9/96BNv61d1X1unFL+cmpmf/S0dH/1dTU/9nY2P/c29v/3Nvb/9DQ + z//f3t7/6unp/+3t7f/w8PD/8/Pz/9WTbP/uolf/76he//CqYP/vqF7/76NY/8lnLL8AAAAAAAAAAAAA + AAAAAAAAzW0u3O2dTv/tolT/7qRX/+6kV//uo1b/7aFT/+SRRv+mXDPbjH53wMvJyPvT0tL/1tXV/9nY + 2P/X1tb/oq+k/+Pj4//n5ub/6urq/+7t7f/v7u79tJ+V1bpiLbDNcDPN0XY469d+Pv7dhkL/03Y2/r1W + IDQAAAAAAAAAAAAAAADCXSSG6ZhM/+6jV//up1v/76hc/+6nW//upFf/7Z5R/8pqLdtxRi4FQz48OW1o + Zo+gnJrRw7++89PR0frZ19f93Nra+9LQz/a5tbPdiIOBoGNdW0xgU00HAAAAAAAAAAAAAAAAwl4mC8Jd + JSm/WSJIvFUfMgAAAAAAAAAAAAAAAMFcJA7KaS3W7J5T/++mW//vp1z/76Zc/+6jV//XfTv5w2AmQgAA + AAAAAAAAAAAAADc0MgM8NzYSRT89KU5IRTNMRkMtSURBGEZBQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJfJhXFYyml1n08+t6KRv/ag0H/ymsvzcRh + KD0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBXSUPwVwkL8Fd + JRwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+P///wB///wAD//wAAf/4A + AD/8AAAf+AAAD/AAAAfwAAAD4AAAA+AAAAPgAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAeAA + AAPgAAAD4AAAA8AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAA4AAAcOAOA//wH////H///8oAAAAEAAAACAA + AAABACAAAAAAAEAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAko6MB6x5 + XjnDWiB3yFserKN7ZwIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACuqKUe08/Nm9zX + 1e/XbCn+32oe/9xpHv/Cr6fbxcC/cK+sqwYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACxqKRF7evr7vPz + 8//AoZD/4W8h/+FtH//hbyD/yJ+H/8/Ozv++u7rFxsLBFQAAAAAAAAAAAAAAAAAAAACakIsw8O7u9Nra + 2v9eXl7/qGE0/+R1JP/kdCP/5HUk/55aLv9ycXH/zMvL/767usSwrasGAAAAAAAAAABJQj8B1dHPyOTk + 5P9YWFj/Z19b/9pzJ//ofCj/6Hwo/+h8KP/Pbyz/X19f/3FwcP/OzMz/wr68bwAAAAAAAAAAc2xoOOrq + 6v6Dg4P/XV1d/5dlRf/rgyz/6YEs/8VrLP/rgyz/6H8r/3NeUv9OTk7/sK+v/8PAv9qMiokBAAAAAI2I + hXnl5OT/U1NT/2tmY//WeS//7okw/8txLP9xSzX/7Ygw/+2JMP+iakT/ZWVl/359ff/Ozcz9o56cHQAA + AACSjoyUyMvI/0pKSv+Zakn/7ZdE/+6XRP+GVjX/RT88/96FOf/umUb/0oA+/2dlZf9wcHD/x8rH/5uW + kzUAAAAAhoKAidfW1v9SSEP/24tF/+6iVP/bi0X/SEA8/z8/P/+tbz//7aJU/+udT/+BY1L/fHt7/97d + 3f6Si4goAAAAAFxYVljT0dH/tXxT/+6jV//uo1b/lWVE/1hYWP9RUVH/gF9L/+2hVP/upVj/sHFA/6al + pf/Z19bvbGVhBgAAAAA7KyIOuaig7dmGRv/tolb/0oZG/2dhXv9iYmL/ZWVl/2ZiYf/ZjEr/76le/+aY + UP/m18//yMPAmQAAAABaKA4BuVMeZ8dcH9nVbiv/znhB/35bRf9nZ2f/aGho/2hoaP9oaGj/pXJM/++q + YP/vqmD/2qN78JKEfRwAAAAAr1EcU+Z8Kf/QbCXgtWc469N0NP6noZ//ZWVl/0pKSv9HR0f/U1NT/5OB + eP/pnlf/8K1l/+OTTvSRRx4TAAAAALtdJHTtmEb/7ptK/+qWR/6+dUbyvbCq7tbV1f/a2dn/zdDN/+rq + 6v/w8PD+zI1h4N+OSu3klE3+xmMpfAAAAACdSRwl45FJ9O+nXP/upVn/1Hk4xVQ1JQ9dWVdZhoKAipOQ + jpaHgoF8VVFPOzkpIQEAAAAAejoWDYo/Fx4AAAAAAAAAAK9UIS7OcDOOymouepdHGw8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwfAADwBwAA4AMAAMABAACAAQAAgAAAAIAA + AACAAAAAgAAAAIAAAACAAQAAAAEAAAABAAAAAQAAAAkAAIf/AAA= + + + \ No newline at end of file diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index 9f4706c5..388cb950 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -159,6 +159,10 @@ + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + True + ..\packages\Serilog.2.6.0\lib\net46\Serilog.dll True @@ -186,6 +190,7 @@ + @@ -282,6 +287,12 @@ + + Form + + + Reporter.cs + @@ -512,6 +523,11 @@ + + + Reporter.cs + + xcopy /E /D /Y "$(ProjectDir)\Website" "$(SolutionDir)\PhoneGap" diff --git a/Vocaluxe/packages.config b/Vocaluxe/packages.config index 9b219db0..c6d7fd55 100644 --- a/Vocaluxe/packages.config +++ b/Vocaluxe/packages.config @@ -1,5 +1,6 @@  + diff --git a/VocaluxeLib/Log/CLog.cs b/VocaluxeLib/Log/CLog.cs index a67752a0..8fbd5cb4 100644 --- a/VocaluxeLib/Log/CLog.cs +++ b/VocaluxeLib/Log/CLog.cs @@ -18,20 +18,27 @@ namespace VocaluxeLib.Log private static ILogger _SongLog = new SilentLogger(); private static string _LogFolder; private static string _CrashMarkerFilePath; + private static ShowReporterDelegate _ShowReporterFunc; + private static string _CurrentVersion; private const string _MainLogTemplate = "[{TimeStampFromStart}] [{Level}] {Message}{NewLine}{Properties}{NewLine}{Exception}"; private const string _SongLogTemplate = "{Message}{NewLine}Additional info:{Properties}{NewLine}{Exception}"; - public static void Init(string logFolder, string fileNameMainLog, string fileNameSongInfoLog, string fileNameCrashMarker, string currentVersion) + public static void Init(string logFolder, string fileNameMainLog, string fileNameSongInfoLog, string fileNameCrashMarker, string currentVersion, ShowReporterDelegate showReporterFunc) { _LogFolder = logFolder; - _CrashMarkerFilePath = Path.Combine(_LogFolder, fileNameCrashMarker); + _ShowReporterFunc = showReporterFunc; + _CurrentVersion = currentVersion; + _CrashMarkerFilePath = Path.Combine(_LogFolder, fileNameCrashMarker); // Creates the log directory if it does not exist if (!Directory.Exists(_LogFolder)) Directory.CreateDirectory(_LogFolder); + var mainLogFilePath = Path.Combine(_LogFolder, fileNameMainLog); + var songLogFilePath = Path.Combine(_LogFolder, fileNameSongInfoLog); + // Check if crash marker file if (File.Exists(_CrashMarkerFilePath)) { @@ -45,17 +52,18 @@ namespace VocaluxeLib.Log // Delete the old marker File.Delete(_CrashMarkerFilePath); - if (currentVersion == versionTag) + if (_CurrentVersion == versionTag && File.Exists(mainLogFilePath)) { - // TODO: Load log + Crashreport + string logContent = File.ReadAllText(mainLogFilePath, Encoding.UTF8); + _ShowReporterFunc(crash:true, allowContinue:true, vocaluxeVersionTag:versionTag, log:logContent); } } // Write new marker - File.WriteAllText(_CrashMarkerFilePath, currentVersion, Encoding.UTF8); + File.WriteAllText(_CrashMarkerFilePath, _CurrentVersion, Encoding.UTF8); - LogFileRoller.RollLogs(Path.Combine(_LogFolder, fileNameMainLog), 2); - LogFileRoller.RollLogs(Path.Combine(_LogFolder, fileNameSongInfoLog), 2); + LogFileRoller.RollLogs(mainLogFilePath, 2); + LogFileRoller.RollLogs(songLogFilePath, 2); _MainLog = new LoggerConfiguration() .Enrich.WithThreadId() @@ -63,14 +71,14 @@ namespace VocaluxeLib.Log .Enrich.WithTimeStampFromStart() .WriteTo.TextWriter(_MainLogStringWriter, outputTemplate: _MainLogTemplate) // Json can be activated by adding "new CompactJsonFormatter()" as first argument - .WriteTo.File(Path.Combine(_LogFolder, fileNameMainLog), flushToDiskInterval: TimeSpan.FromSeconds(30), outputTemplate: _MainLogTemplate) + .WriteTo.File(mainLogFilePath, flushToDiskInterval: TimeSpan.FromSeconds(30), outputTemplate: _MainLogTemplate) #if DEBUG .WriteTo.Console(outputTemplate: _MainLogTemplate) #endif .CreateLogger(); _SongLog = new LoggerConfiguration() - .WriteTo.File(Path.Combine(_LogFolder, fileNameSongInfoLog), flushToDiskInterval: TimeSpan.FromSeconds(60), outputTemplate: _SongLogTemplate) + .WriteTo.File(songLogFilePath, flushToDiskInterval: TimeSpan.FromSeconds(60), outputTemplate: _SongLogTemplate) #if DEBUG .WriteTo.Console(outputTemplate: "[SongInfo] " + _SongLogTemplate) #endif @@ -102,8 +110,10 @@ namespace VocaluxeLib.Log private static void _ShowLogAssistent(string messageTemplate, object[] propertyValues, string callerMethodeName = "", string callerFilePath = "", int callerLineNumer = -1) { - //Todo: Logsend assistent - //MessageBox.Show(messageTemplate, "Vocaluxe", MessageBoxButtons.OK, MessageBoxIcon.Error); + // Flush the _MainLogStringWriter to get the latest entries to _MainLogStringBuilder + _MainLogStringWriter.Flush(); + // Show the Reporter + _ShowReporterFunc(crash: true, allowContinue: true, vocaluxeVersionTag: _CurrentVersion, log: _MainLogStringBuilder.ToString()); } } diff --git a/VocaluxeLib/Log/ShowReporterDelegate.cs b/VocaluxeLib/Log/ShowReporterDelegate.cs new file mode 100644 index 00000000..ad335b44 --- /dev/null +++ b/VocaluxeLib/Log/ShowReporterDelegate.cs @@ -0,0 +1,5 @@ + +namespace VocaluxeLib.Log +{ + public delegate void ShowReporterDelegate(bool crash, bool allowContinue, string vocaluxeVersionTag, string log); +} diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj index 55d77e22..00d4b977 100644 --- a/VocaluxeLib/VocaluxeLib.csproj +++ b/VocaluxeLib/VocaluxeLib.csproj @@ -112,6 +112,7 @@ + diff --git a/VocaluxeTests/PartyModeTests.cs b/VocaluxeTests/PartyModeTests.cs index 8784d049..e5c1eda6 100644 --- a/VocaluxeTests/PartyModeTests.cs +++ b/VocaluxeTests/PartyModeTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio.TestTools.UnitTesting; using Vocaluxe.Base; +using Vocaluxe.Reporting; using VocaluxeLib; using VocaluxeLib.Log; using VocaluxeLib.PartyModes.Challenge; @@ -47,7 +48,7 @@ namespace VocaluxeTests public void TestRoundGeneration() { CBase.Game = new CBGame(); - CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog, CSettings.FileNameCrashMarker, CSettings.GetFullVersionText()); + CLog.Init(CSettings.FolderNameLogs, CSettings.FileNameMainLog, CSettings.FileNameSongLog, CSettings.FileNameCrashMarker, CSettings.GetFullVersionText(), CReporter.ShowReporterFunc); for (int i = 0; i < 10; i++) { for (int numPlayer = 1; numPlayer <= 20; numPlayer++) From de4e3e3f5ffc2946522333784cff875c16bddca5 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Fri, 19 Jan 2018 19:04:11 +0100 Subject: [PATCH 09/28] Reporter improvement + Show it when the last run crashed, an event with show = true took place and when the user presses F8. --- Vocaluxe/Base/CGraphics.cs | 5 + Vocaluxe/Reporting/Reporter.Designer.cs | 243 +++++++++++++----------- Vocaluxe/Reporting/Reporter.cs | 120 +++++++----- VocaluxeLib/Log/CLog.cs | 31 ++- VocaluxeLib/Log/CLogFunctions.cs | 48 ++--- VocaluxeLib/Log/CLogFunctions.tt | 8 +- VocaluxeLib/Log/ShowReporterDelegate.cs | 10 +- 7 files changed, 280 insertions(+), 185 deletions(-) diff --git a/Vocaluxe/Base/CGraphics.cs b/Vocaluxe/Base/CGraphics.cs index 8f8e22fd..5d5be4da 100644 --- a/Vocaluxe/Base/CGraphics.cs +++ b/Vocaluxe/Base/CGraphics.cs @@ -443,6 +443,11 @@ namespace Vocaluxe.Base HidePopup(EPopupScreens.PopupServerQR); } + if (keyEvent.Key == Keys.F8) + { + CLog.ShowLogAssistent("", null); + } + if (popupPlayerControlAllowed && keyEvent.Key == Keys.Tab) { if (_CurrentPopupScreen == EPopupScreens.NoPopup && CConfig.Config.Sound.BackgroundMusic == EBackgroundMusicOffOn.TR_CONFIG_ON) diff --git a/Vocaluxe/Reporting/Reporter.Designer.cs b/Vocaluxe/Reporting/Reporter.Designer.cs index cbdee73e..59fc5577 100644 --- a/Vocaluxe/Reporting/Reporter.Designer.cs +++ b/Vocaluxe/Reporting/Reporter.Designer.cs @@ -29,107 +29,111 @@ private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CReporter)); - this.Title = new System.Windows.Forms.Label(); - this.Message = new System.Windows.Forms.TextBox(); - this.Log = new System.Windows.Forms.TextBox(); - this.GistOnly = new System.Windows.Forms.RadioButton(); - this.GistAndIssue = new System.Windows.Forms.RadioButton(); - this.NoUpload = new System.Windows.Forms.RadioButton(); - this.Submit = new System.Windows.Forms.Button(); + this.TitleLabel = new System.Windows.Forms.Label(); + this.MessageText = new System.Windows.Forms.TextBox(); + this.LogBox = new System.Windows.Forms.TextBox(); + this.GistOnlySelect = new System.Windows.Forms.RadioButton(); + this.GistAndIssueSelect = new System.Windows.Forms.RadioButton(); + this.NoUploadSelect = new System.Windows.Forms.RadioButton(); + this.SubmitButton = new System.Windows.Forms.Button(); this.Url = new System.Windows.Forms.TextBox(); - this.SubmitedMessage = new System.Windows.Forms.Label(); + this.SubmitedTitleLabel = new System.Windows.Forms.Label(); + this.LastErrorTitleLabel = new System.Windows.Forms.Label(); + this.LastErrorBox = new System.Windows.Forms.TextBox(); this.SuspendLayout(); // - // Title + // TitleLabel // - this.Title.AutoSize = true; - this.Title.Enabled = false; - this.Title.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.Title.Location = new System.Drawing.Point(9, 8); - this.Title.Name = "Title"; - this.Title.Size = new System.Drawing.Size(110, 20); - this.Title.TabIndex = 0; - this.Title.Text = "We are sorry"; + this.TitleLabel.AutoSize = true; + this.TitleLabel.Enabled = false; + this.TitleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.TitleLabel.Location = new System.Drawing.Point(9, 8); + this.TitleLabel.Name = "TitleLabel"; + this.TitleLabel.Size = new System.Drawing.Size(110, 20); + this.TitleLabel.TabIndex = 0; + this.TitleLabel.Text = "We are sorry"; // - // Message + // MessageText // - this.Message.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.Message.Enabled = false; - this.Message.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.Message.Location = new System.Drawing.Point(13, 31); - this.Message.Multiline = true; - this.Message.Name = "Message"; - this.Message.ReadOnly = true; - this.Message.Size = new System.Drawing.Size(625, 63); - this.Message.TabIndex = 0; - this.Message.TabStop = false; - this.Message.Text = "that Vocaluxe run into an error and crashed.\r\nTo help us fix the problem please s" + + this.MessageText.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.MessageText.Enabled = false; + this.MessageText.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.MessageText.Location = new System.Drawing.Point(13, 31); + this.MessageText.Multiline = true; + this.MessageText.Name = "MessageText"; + this.MessageText.ReadOnly = true; + this.MessageText.Size = new System.Drawing.Size(625, 63); + this.MessageText.TabIndex = 0; + this.MessageText.TabStop = false; + this.MessageText.Text = "that Vocaluxe run into an error and crashed.\r\nTo help us fix the problem please s" + "end us a report.\r\nEdit the following log before submitting to remove possibly se" + "nsitive information."; // - // Log + // LogBox // - this.Log.AcceptsReturn = true; - this.Log.AcceptsTab = true; - this.Log.Location = new System.Drawing.Point(13, 100); - this.Log.Multiline = true; - this.Log.Name = "Log"; - this.Log.Size = new System.Drawing.Size(625, 189); - this.Log.TabIndex = 1; + this.LogBox.AcceptsReturn = true; + this.LogBox.AcceptsTab = true; + this.LogBox.Location = new System.Drawing.Point(13, 182); + this.LogBox.Multiline = true; + this.LogBox.Name = "LogBox"; + this.LogBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.LogBox.Size = new System.Drawing.Size(625, 189); + this.LogBox.TabIndex = 1; + this.LogBox.WordWrap = false; // - // GistOnly + // GistOnlySelect // - this.GistOnly.AutoSize = true; - this.GistOnly.Checked = true; - this.GistOnly.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.GistOnly.Location = new System.Drawing.Point(12, 295); - this.GistOnly.Name = "GistOnly"; - this.GistOnly.Size = new System.Drawing.Size(385, 24); - this.GistOnly.TabIndex = 2; - this.GistOnly.TabStop = true; - this.GistOnly.Text = "Upload and get a link to your report (publicly visible)"; - this.GistOnly.UseVisualStyleBackColor = true; + this.GistOnlySelect.AutoSize = true; + this.GistOnlySelect.Checked = true; + this.GistOnlySelect.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.GistOnlySelect.Location = new System.Drawing.Point(12, 377); + this.GistOnlySelect.Name = "GistOnlySelect"; + this.GistOnlySelect.Size = new System.Drawing.Size(385, 24); + this.GistOnlySelect.TabIndex = 2; + this.GistOnlySelect.TabStop = true; + this.GistOnlySelect.Text = "Upload and get a link to your report (publicly visible)"; + this.GistOnlySelect.UseVisualStyleBackColor = true; // - // GistAndIssue + // GistAndIssueSelect // - this.GistAndIssue.AutoSize = true; - this.GistAndIssue.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.GistAndIssue.Location = new System.Drawing.Point(12, 325); - this.GistAndIssue.Name = "GistAndIssue"; - this.GistAndIssue.Size = new System.Drawing.Size(519, 24); - this.GistAndIssue.TabIndex = 3; - this.GistAndIssue.TabStop = true; - this.GistAndIssue.Text = "Upload and open an issue (publicly visible + requires a github account)"; - this.GistAndIssue.UseVisualStyleBackColor = true; + this.GistAndIssueSelect.AutoSize = true; + this.GistAndIssueSelect.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.GistAndIssueSelect.Location = new System.Drawing.Point(12, 407); + this.GistAndIssueSelect.Name = "GistAndIssueSelect"; + this.GistAndIssueSelect.Size = new System.Drawing.Size(519, 24); + this.GistAndIssueSelect.TabIndex = 3; + this.GistAndIssueSelect.TabStop = true; + this.GistAndIssueSelect.Text = "Upload and open an issue (publicly visible + requires a github account)"; + this.GistAndIssueSelect.UseVisualStyleBackColor = true; // - // NoUpload + // NoUploadSelect // - this.NoUpload.AutoSize = true; - this.NoUpload.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.NoUpload.Location = new System.Drawing.Point(12, 355); - this.NoUpload.Name = "NoUpload"; - this.NoUpload.Size = new System.Drawing.Size(493, 24); - this.NoUpload.TabIndex = 4; - this.NoUpload.TabStop = true; - this.NoUpload.Text = "Don\'t upload anything (you can still copy the error message above)"; - this.NoUpload.UseVisualStyleBackColor = true; - this.NoUpload.CheckedChanged += new System.EventHandler(this.NoUpload_CheckedChanged); + this.NoUploadSelect.AutoSize = true; + this.NoUploadSelect.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.NoUploadSelect.Location = new System.Drawing.Point(12, 437); + this.NoUploadSelect.Name = "NoUploadSelect"; + this.NoUploadSelect.Size = new System.Drawing.Size(493, 24); + this.NoUploadSelect.TabIndex = 4; + this.NoUploadSelect.TabStop = true; + this.NoUploadSelect.Text = "Don\'t upload anything (you can still copy the error message above)"; + this.NoUploadSelect.UseVisualStyleBackColor = true; + this.NoUploadSelect.CheckedChanged += new System.EventHandler(this.NoUpload_CheckedChanged); // - // Submit + // SubmitButton // - this.Submit.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.Submit.Location = new System.Drawing.Point(537, 306); - this.Submit.Name = "Submit"; - this.Submit.Size = new System.Drawing.Size(101, 73); - this.Submit.TabIndex = 5; - this.Submit.Text = "Submit"; - this.Submit.UseVisualStyleBackColor = true; - this.Submit.Click += new System.EventHandler(this.Submit_Click); + this.SubmitButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.SubmitButton.Location = new System.Drawing.Point(537, 388); + this.SubmitButton.Name = "SubmitButton"; + this.SubmitButton.Size = new System.Drawing.Size(101, 73); + this.SubmitButton.TabIndex = 5; + this.SubmitButton.Text = "Submit"; + this.SubmitButton.UseVisualStyleBackColor = true; + this.SubmitButton.Click += new System.EventHandler(this.Submit_Click); // // Url // this.Url.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.Url.Location = new System.Drawing.Point(12, 327); + this.Url.Location = new System.Drawing.Point(12, 409); this.Url.Name = "Url"; this.Url.ReadOnly = true; this.Url.Size = new System.Drawing.Size(519, 22); @@ -137,32 +141,57 @@ this.Url.Text = "https://gist.github.com/lukeIam/cc81f7ef3545ef2baf0ca9a28ffe4bd8"; this.Url.Visible = false; // - // SubmitedMessage + // SubmitedTitleLabel // - this.SubmitedMessage.AutoSize = true; - this.SubmitedMessage.Enabled = false; - this.SubmitedMessage.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.SubmitedMessage.Location = new System.Drawing.Point(9, 299); - this.SubmitedMessage.Name = "SubmitedMessage"; - this.SubmitedMessage.Size = new System.Drawing.Size(189, 20); - this.SubmitedMessage.TabIndex = 8; - this.SubmitedMessage.Text = "The link to your report:"; - this.SubmitedMessage.Visible = false; + this.SubmitedTitleLabel.AutoSize = true; + this.SubmitedTitleLabel.Enabled = false; + this.SubmitedTitleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.SubmitedTitleLabel.Location = new System.Drawing.Point(9, 381); + this.SubmitedTitleLabel.Name = "SubmitedTitleLabel"; + this.SubmitedTitleLabel.Size = new System.Drawing.Size(189, 20); + this.SubmitedTitleLabel.TabIndex = 8; + this.SubmitedTitleLabel.Text = "The link to your report:"; + this.SubmitedTitleLabel.Visible = false; + // + // LastErrorTitleLabel + // + this.LastErrorTitleLabel.AutoSize = true; + this.LastErrorTitleLabel.Enabled = false; + this.LastErrorTitleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.LastErrorTitleLabel.Location = new System.Drawing.Point(9, 97); + this.LastErrorTitleLabel.Name = "LastErrorTitleLabel"; + this.LastErrorTitleLabel.Size = new System.Drawing.Size(87, 20); + this.LastErrorTitleLabel.TabIndex = 9; + this.LastErrorTitleLabel.Text = "Last error"; + // + // LastErrorBox + // + this.LastErrorBox.AcceptsReturn = true; + this.LastErrorBox.AcceptsTab = true; + this.LastErrorBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.LastErrorBox.Location = new System.Drawing.Point(12, 120); + this.LastErrorBox.Multiline = true; + this.LastErrorBox.Name = "LastErrorBox"; + this.LastErrorBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.LastErrorBox.Size = new System.Drawing.Size(626, 38); + this.LastErrorBox.TabIndex = 10; // // CReporter // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(650, 387); - this.Controls.Add(this.SubmitedMessage); + this.ClientSize = new System.Drawing.Size(650, 467); + this.Controls.Add(this.LastErrorBox); + this.Controls.Add(this.LastErrorTitleLabel); + this.Controls.Add(this.SubmitedTitleLabel); this.Controls.Add(this.Url); - this.Controls.Add(this.Submit); - this.Controls.Add(this.NoUpload); - this.Controls.Add(this.GistAndIssue); - this.Controls.Add(this.GistOnly); - this.Controls.Add(this.Log); - this.Controls.Add(this.Message); - this.Controls.Add(this.Title); + this.Controls.Add(this.SubmitButton); + this.Controls.Add(this.NoUploadSelect); + this.Controls.Add(this.GistAndIssueSelect); + this.Controls.Add(this.GistOnlySelect); + this.Controls.Add(this.LogBox); + this.Controls.Add(this.MessageText); + this.Controls.Add(this.TitleLabel); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "CReporter"; this.Text = "Vocaluxe Issue Reporter"; @@ -173,14 +202,16 @@ #endregion - private System.Windows.Forms.Label Title; - private System.Windows.Forms.TextBox Message; - private System.Windows.Forms.TextBox Log; - private System.Windows.Forms.RadioButton GistOnly; - private System.Windows.Forms.RadioButton GistAndIssue; - private System.Windows.Forms.RadioButton NoUpload; - private System.Windows.Forms.Button Submit; + private System.Windows.Forms.Label TitleLabel; + private System.Windows.Forms.TextBox MessageText; + private System.Windows.Forms.TextBox LogBox; + private System.Windows.Forms.RadioButton GistOnlySelect; + private System.Windows.Forms.RadioButton GistAndIssueSelect; + private System.Windows.Forms.RadioButton NoUploadSelect; + private System.Windows.Forms.Button SubmitButton; private System.Windows.Forms.TextBox Url; - private System.Windows.Forms.Label SubmitedMessage; + private System.Windows.Forms.Label SubmitedTitleLabel; + private System.Windows.Forms.Label LastErrorTitleLabel; + private System.Windows.Forms.TextBox LastErrorBox; } } \ No newline at end of file diff --git a/Vocaluxe/Reporting/Reporter.cs b/Vocaluxe/Reporting/Reporter.cs index 0c813bec..e5e74105 100644 --- a/Vocaluxe/Reporting/Reporter.cs +++ b/Vocaluxe/Reporting/Reporter.cs @@ -20,10 +20,12 @@ namespace Vocaluxe.Reporting private string _GistOnlyText = "Upload and get a link to your report (publicly visible)"; private string _SubmitStep0Text = "Submit"; private string _SubmitStep1Text = "Uploading"; - private string _SubmitStep2CrashText = "Exit"; - private string _SubmitStep2NoCrashText = "Continue"; + private string _SubmitStep2ExitText = "Exit"; + private string _SubmitStep2ContinueText = "Continue"; private string _SubmitedMessageText = "The link to your report:"; private string _LogUploadErrorText = "Error uploading the log."; + private string _LastErrorTitleText = "Last error"; + private string _LastErrorNA = "Not available"; private string _IssueTemplate = "Describe your issue here.\n\n### Steps to reproduce\nTell us how to reproduce this issue.\n\n### Vocaluxe version and logfile\n{0}\n{1}"; @@ -34,7 +36,7 @@ namespace Vocaluxe.Reporting private readonly bool _ShowContinue; private static readonly HttpClient _Client = new HttpClient(); - private static readonly Regex _GetGistUrlRegex = new Regex("\"raw_url\": *\"([^\"]+)\""); + private static readonly Regex _GetGistUrlRegex = new Regex("\"html_url\": *\"([^\"]+)\""); public static ShowReporterDelegate ShowReporterFunc @@ -42,9 +44,17 @@ namespace Vocaluxe.Reporting get { return _ShowReporter; } } - private static void _ShowReporter(bool crash, bool showContinue, string vocaluxeVersionTag, string log) + /// + /// Shows a new instance of the log file reporter. + /// + /// True if we are submitting a crash, false otherwise (e.g. error). + /// True if the reporter show show a continue message, false if it should show an exit message. + /// The full version tag of this instance (like it is diplayed in the main menu). + /// The log to submit. + /// The last error message. + private static void _ShowReporter(bool crash, bool showContinue, string vocaluxeVersionTag, string log, string lastError) { - using (var reporter = new CReporter(crash, showContinue, vocaluxeVersionTag, log)) + using (var reporter = new CReporter(crash, showContinue, vocaluxeVersionTag, log, lastError)) { reporter.ShowDialog(); } @@ -57,33 +67,37 @@ namespace Vocaluxe.Reporting /// True if the reporter show show a continue message, false if it should show an exit message. /// The full version tag of this instance (like it is diplayed in the main menu). /// The log to submit. - public CReporter(bool crash, bool showContinue, string vocaluxeVersionTag, string log) + /// The last error message. + public CReporter(bool crash, bool showContinue, string vocaluxeVersionTag, string log, string lastError) { InitializeComponent(); _Crash = crash; _ShowContinue = showContinue; _VocaluxeVersionTag = vocaluxeVersionTag; - _Init(log); + _Init(log, lastError); } /// /// Initilaize the gui. /// /// The Log to submit. - private void _Init(string log) + /// The last error message. + private void _Init(string log, string lastError) { _Client.DefaultRequestHeaders.Add("accept", "application/json"); _Client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"); - this.Title.Text = _TitleText; - this.Message.Text = _Crash ? _MessageCrashText : _MessageNoCrashText; - this.NoUpload.Text = _NoUploadText; - this.GistAndIssue.Text = _GistAndIssueText; - this.GistOnly.Text = _GistOnlyText; - this.Submit.Text = _SubmitStep0Text; - this.SubmitedMessage.Text = _SubmitedMessageText; - this.Log.Text = log.Replace("\n","\r\n"); - this.Log.SelectionStart = 0; + this.TitleLabel.Text = _TitleText; + this.MessageText.Text = _Crash ? _MessageCrashText : _MessageNoCrashText; + this.NoUploadSelect.Text = _NoUploadText; + this.GistAndIssueSelect.Text = _GistAndIssueText; + this.GistOnlySelect.Text = _GistOnlyText; + this.SubmitButton.Text = _SubmitStep0Text; + this.SubmitedTitleLabel.Text = _SubmitedMessageText; + this.LastErrorTitleLabel.Text = _LastErrorTitleText; + this.LastErrorBox.Text = string.IsNullOrWhiteSpace(lastError) ? _LastErrorNA : lastError; + this.LogBox.Text = log.Replace("\n","\r\n"); + this.LogBox.SelectionStart = 0; } private async void Submit_Click(object sender, EventArgs e) @@ -91,38 +105,39 @@ namespace Vocaluxe.Reporting switch (_Step) { case 0: - if (this.NoUpload.Checked) + if (this.NoUploadSelect.Checked) { this.Close(); } else { _Step = 1; - this.Submit.Text = _SubmitStep1Text; - this.Submit.Enabled = false; - this.NoUpload.Visible = false; - this.GistAndIssue.Visible = false; - this.GistOnly.Visible = false; - this.Log.Enabled = false; + this.SubmitButton.Text = _SubmitStep1Text; + this.SubmitButton.Enabled = false; + this.NoUploadSelect.Visible = false; + this.GistAndIssueSelect.Visible = false; + this.GistOnlySelect.Visible = false; + this.LogBox.Enabled = false; + this.LastErrorBox.Enabled = false; - string url; - if (this.GistOnly.Checked) + if (this.GistOnlySelect.Checked) { - await Task.Run(() => _StartGistUpload(this.Log.Text)); + await Task.Run(() => _StartGistUpload(this.LogBox.Text, this.LastErrorBox.Text)); } - else if (this.GistAndIssue.Checked) + else if (this.GistAndIssueSelect.Checked) { - await Task.Run(() => _StartIssueUpload(this.Log.Text)); + await Task.Run(() => _StartIssueUpload(this.LogBox.Text, this.LastErrorBox.Text)); } else { _Step = 0; - this.Submit.Text = _SubmitStep0Text; - this.Submit.Enabled = true; - this.NoUpload.Visible = true; - this.GistAndIssue.Visible = true; - this.GistOnly.Visible = true; - this.Log.Enabled = true; + this.SubmitButton.Text = _SubmitStep0Text; + this.SubmitButton.Enabled = true; + this.NoUploadSelect.Visible = true; + this.GistAndIssueSelect.Visible = true; + this.GistOnlySelect.Visible = true; + this.LogBox.Enabled = true; + this.LastErrorBox.Enabled = false; } } @@ -136,7 +151,7 @@ namespace Vocaluxe.Reporting private void NoUpload_CheckedChanged(object sender, EventArgs e) { - this.Submit.Text = this.NoUpload.Checked ? (_ShowContinue ? _SubmitStep2CrashText : _SubmitStep2NoCrashText) : _SubmitStep0Text; + this.SubmitButton.Text = this.NoUploadSelect.Checked ? (_ShowContinue ? _SubmitStep2ContinueText : _SubmitStep2ExitText) : _SubmitStep0Text; } /// @@ -152,12 +167,13 @@ namespace Vocaluxe.Reporting else { _Step = 2; - this.Submit.Text = _ShowContinue ? _SubmitStep2CrashText : _SubmitStep2NoCrashText; - this.Submit.Enabled = true; - this.SubmitedMessage.Visible = true; + this.SubmitButton.Text = _ShowContinue ? _SubmitStep2ContinueText : _SubmitStep2ExitText; + this.SubmitButton.Enabled = true; + this.SubmitedTitleLabel.Visible = true; this.Url.Text = string.IsNullOrWhiteSpace(url)?_LogUploadErrorText:url; this.Url.Visible = true; this.Url.SelectAll(); + this.Url.Focus(); } } @@ -165,20 +181,22 @@ namespace Vocaluxe.Reporting /// Upload a log to gist and updates the gui. /// /// The log to upload. - private async void _StartGistUpload(string log) + /// The error message displayed to the user (if available). + private async void _StartGistUpload(string log, string lastError= "not available") { // Upload log to github gist and show the link - _UploadFinished(await _UploadLogToGist(log)); + _UploadFinished(await _UploadLogToGist(log, lastError)); } /// /// Upload a log to gist, opens an issue template on github and updates the gui. /// /// The log to upload. - private async void _StartIssueUpload(string log) + /// The error message displayed to the user (if available). + private async void _StartIssueUpload(string log, string lastError = "not available") { // Upload log to github gist - string gistUrl = await _UploadLogToGist(log); + string gistUrl = await _UploadLogToGist(log, lastError); // Build issue body string template = string.Format(_IssueTemplate, _VocaluxeVersionTag, gistUrl); @@ -186,19 +204,20 @@ namespace Vocaluxe.Reporting // Build url for github issue template string issueUrl = $"https://github.com/Vocaluxe/Vocaluxe/issues/new?title=Give%20me%20a%20meaningful%20title&body={ Uri.EscapeDataString(template) }"; - // Start the browser with the issue template url - System.Diagnostics.Process.Start(issueUrl); - // Show the link _UploadFinished(issueUrl); + + // Start the browser with the issue template url + System.Diagnostics.Process.Start(issueUrl); } /// /// Uploads a log to a new anonymous gist. /// /// The log that should be uploaded. + /// The error message displayed to the user (if available). /// The link to the uploaded file. - private async Task _UploadLogToGist(string log) + private async Task _UploadLogToGist(string log, string lastError= "not available") { string json = JsonConvert.SerializeObject(new CGistCreateData() { @@ -212,6 +231,13 @@ namespace Vocaluxe.Reporting { Content = log } + }, + { + "LastError.txt", + new CGistFileData() + { + Content = lastError + } } } }); diff --git a/VocaluxeLib/Log/CLog.cs b/VocaluxeLib/Log/CLog.cs index 8fbd5cb4..3d2ffa9b 100644 --- a/VocaluxeLib/Log/CLog.cs +++ b/VocaluxeLib/Log/CLog.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using Serilog; using VocaluxeLib.Log.Enricher; using VocaluxeLib.Log.Rolling; @@ -55,7 +56,7 @@ namespace VocaluxeLib.Log if (_CurrentVersion == versionTag && File.Exists(mainLogFilePath)) { string logContent = File.ReadAllText(mainLogFilePath, Encoding.UTF8); - _ShowReporterFunc(crash:true, allowContinue:true, vocaluxeVersionTag:versionTag, log:logContent); + _ShowReporterFunc(crash:true, showContinue:true, vocaluxeVersionTag:versionTag, log:logContent, lastError:"Vocaluxe crashed while the last execution."); } } @@ -108,12 +109,36 @@ namespace VocaluxeLib.Log } - private static void _ShowLogAssistent(string messageTemplate, object[] propertyValues, string callerMethodeName = "", string callerFilePath = "", int callerLineNumer = -1) + public static void ShowLogAssistent(string messageTemplate, object[] propertyValues, bool crash = false, bool showContinue = true) { // Flush the _MainLogStringWriter to get the latest entries to _MainLogStringBuilder _MainLogStringWriter.Flush(); // Show the Reporter - _ShowReporterFunc(crash: true, allowContinue: true, vocaluxeVersionTag: _CurrentVersion, log: _MainLogStringBuilder.ToString()); + _ShowReporterFunc(crash: crash, showContinue: showContinue, vocaluxeVersionTag: _CurrentVersion, log: _MainLogStringBuilder.ToString(), lastError: _FormatMessageTemplate(messageTemplate, propertyValues)); + } + + /// + /// Simple version of inserting values into a message template (have not the same result as the original) + /// + /// The template to fill in the data. + /// The values to fill in. + /// The filled template. + private static string _FormatMessageTemplate(string template, object[] propertyValues) + { + if (propertyValues == null) + { + return template; + } + + Regex theRegex = new Regex(@"{[^}]+}"); + + int i = 0; + return theRegex.Replace(template, delegate(Match match) + { + if (propertyValues.Length >= i) + return match.Value; + return propertyValues[i++].ToString(); + }); } } diff --git a/VocaluxeLib/Log/CLogFunctions.cs b/VocaluxeLib/Log/CLogFunctions.cs index d241cc4c..c52ca532 100644 --- a/VocaluxeLib/Log/CLogFunctions.cs +++ b/VocaluxeLib/Log/CLogFunctions.cs @@ -33,7 +33,7 @@ namespace VocaluxeLib.Log _MainLog.Verbose(messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null); } /// @@ -53,7 +53,7 @@ namespace VocaluxeLib.Log _MainLog.Verbose(messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues); } /// @@ -73,7 +73,7 @@ namespace VocaluxeLib.Log _MainLog.Verbose( exception, messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null); } /// @@ -94,7 +94,7 @@ namespace VocaluxeLib.Log _MainLog.Verbose(exception, messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues); } #endregion @@ -117,7 +117,7 @@ namespace VocaluxeLib.Log _MainLog.Debug(messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null); } /// @@ -137,7 +137,7 @@ namespace VocaluxeLib.Log _MainLog.Debug(messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues); } /// @@ -157,7 +157,7 @@ namespace VocaluxeLib.Log _MainLog.Debug( exception, messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null); } /// @@ -178,7 +178,7 @@ namespace VocaluxeLib.Log _MainLog.Debug(exception, messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues); } #endregion @@ -201,7 +201,7 @@ namespace VocaluxeLib.Log _MainLog.Information(messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null); } /// @@ -221,7 +221,7 @@ namespace VocaluxeLib.Log _MainLog.Information(messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues); } /// @@ -241,7 +241,7 @@ namespace VocaluxeLib.Log _MainLog.Information( exception, messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null); } /// @@ -262,7 +262,7 @@ namespace VocaluxeLib.Log _MainLog.Information(exception, messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues); } #endregion @@ -285,7 +285,7 @@ namespace VocaluxeLib.Log _MainLog.Warning(messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null); } /// @@ -305,7 +305,7 @@ namespace VocaluxeLib.Log _MainLog.Warning(messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues); } /// @@ -325,7 +325,7 @@ namespace VocaluxeLib.Log _MainLog.Warning( exception, messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null); } /// @@ -346,7 +346,7 @@ namespace VocaluxeLib.Log _MainLog.Warning(exception, messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues); } #endregion @@ -369,7 +369,7 @@ namespace VocaluxeLib.Log _MainLog.Error(messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null); } /// @@ -389,7 +389,7 @@ namespace VocaluxeLib.Log _MainLog.Error(messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues); } /// @@ -409,7 +409,7 @@ namespace VocaluxeLib.Log _MainLog.Error( exception, messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null); } /// @@ -430,7 +430,7 @@ namespace VocaluxeLib.Log _MainLog.Error(exception, messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues); } #endregion @@ -453,7 +453,7 @@ namespace VocaluxeLib.Log _MainLog.Fatal(messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null, true, false); // Close logs and exit Close(); @@ -477,7 +477,7 @@ namespace VocaluxeLib.Log _MainLog.Fatal(messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues, true, false); // Close logs and exit Close(); @@ -501,7 +501,7 @@ namespace VocaluxeLib.Log _MainLog.Fatal( exception, messageTemplate); } if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null, true, false); // Close logs and exit Close(); @@ -526,7 +526,7 @@ namespace VocaluxeLib.Log _MainLog.Fatal(exception, messageTemplate, propertyValues); } if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues, true, false); // Close logs and exit Close(); diff --git a/VocaluxeLib/Log/CLogFunctions.tt b/VocaluxeLib/Log/CLogFunctions.tt index 1f49063c..ec93f52c 100644 --- a/VocaluxeLib/Log/CLogFunctions.tt +++ b/VocaluxeLib/Log/CLogFunctions.tt @@ -52,7 +52,7 @@ namespace VocaluxeLib.Log } <# if(Log == "Main") { #> if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null<#= LogLevel == "Fatal"?", true, false":"" #>); <# } #> <# if(LogLevel == "Fatal") { #> @@ -82,7 +82,7 @@ namespace VocaluxeLib.Log } <# if(Log == "Main") { #> if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues<#= LogLevel == "Fatal"?", true, false":"" #>); <# } #> <# if(LogLevel == "Fatal") { #> @@ -112,7 +112,7 @@ namespace VocaluxeLib.Log } <# if(Log == "Main") { #> if(show) - _ShowLogAssistent(messageTemplate, null, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, null<#= LogLevel == "Fatal"?", true, false":"" #>); <# } #> <# if(LogLevel == "Fatal") { #> @@ -143,7 +143,7 @@ namespace VocaluxeLib.Log } <# if(Log == "Main") { #> if(show) - _ShowLogAssistent(messageTemplate, propertyValues, callerMethodeName, callerFilePath, callerLineNumer); + ShowLogAssistent(messageTemplate, propertyValues<#= LogLevel == "Fatal"?", true, false":"" #>); <# } #> <# if(LogLevel == "Fatal") { #> diff --git a/VocaluxeLib/Log/ShowReporterDelegate.cs b/VocaluxeLib/Log/ShowReporterDelegate.cs index ad335b44..c2750515 100644 --- a/VocaluxeLib/Log/ShowReporterDelegate.cs +++ b/VocaluxeLib/Log/ShowReporterDelegate.cs @@ -1,5 +1,13 @@  namespace VocaluxeLib.Log { - public delegate void ShowReporterDelegate(bool crash, bool allowContinue, string vocaluxeVersionTag, string log); + /// + /// Shows a new instance of the log file reporter. + /// + /// True if we are submitting a crash, false otherwise (e.g. error). + /// True if the reporter show show a continue message, false if it should show an exit message. + /// The full version tag of this instance (like it is diplayed in the main menu). + /// The log to submit. + /// The last error message. + public delegate void ShowReporterDelegate(bool crash, bool showContinue, string vocaluxeVersionTag, string log, string lastError); } From 92a6bcee492705b8529c976137f6781c48a0fd3f Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Fri, 19 Jan 2018 19:05:49 +0100 Subject: [PATCH 10/28] Ignore the log folder in .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 16254856..fd38548a 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,4 @@ tx.exe /Output/x86 /PitchTracker/bin /PitchTracker/obj +/Logs \ No newline at end of file From 611a5b35e76bd7d168376405ff7f699d01aa12fb Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Fri, 19 Jan 2018 21:10:23 +0100 Subject: [PATCH 11/28] Removed duplicated log entries --- Vocaluxe/Base/CGraphics.cs | 3 --- Vocaluxe/Base/CSongs.cs | 9 ++------- Vocaluxe/Base/Fonts/CFonts.cs | 3 +-- Vocaluxe/Program.cs | 28 ++++------------------------ Vocaluxe/Screens/CScreenLoad.cs | 4 +--- 5 files changed, 8 insertions(+), 39 deletions(-) diff --git a/Vocaluxe/Base/CGraphics.cs b/Vocaluxe/Base/CGraphics.cs index 5d5be4da..5d854bf8 100644 --- a/Vocaluxe/Base/CGraphics.cs +++ b/Vocaluxe/Base/CGraphics.cs @@ -63,7 +63,6 @@ namespace Vocaluxe.Base public static void Init() { // Add Screens, must be the same order as in EScreens! - CLog.Information("Build Screen List"); using (CBenchmark.Time("Build Screen List")) { _Screens.Add(new CScreenTest()); @@ -100,7 +99,6 @@ namespace Vocaluxe.Base GlobalAlpha = 1f; ZOffset = 0f; - CLog.Information("Load Theme"); using (CBenchmark.Time("Load Theme")) { LoadTheme(); @@ -121,7 +119,6 @@ namespace Vocaluxe.Base for (int i = 0; i < _Screens.Count; i++) { - CLog.Information("Load Theme " + Enum.GetNames(typeof(EScreen))[i]); using (CBenchmark.Time("Load Theme " + Enum.GetNames(typeof(EScreen))[i])) { _Screens[i].Init(); diff --git a/Vocaluxe/Base/CSongs.cs b/Vocaluxe/Base/CSongs.cs index 5c9b1943..d3670c80 100644 --- a/Vocaluxe/Base/CSongs.cs +++ b/Vocaluxe/Base/CSongs.cs @@ -334,15 +334,13 @@ namespace Vocaluxe.Base public static void LoadSongs() { - CLog.Information("Load Songs"); using (CBenchmark.Time("Load Songs")) { SongsLoaded = false; _Songs.Clear(); var files = new List(); - - CLog.Information("List Songs"); + using (CBenchmark.Time("List Songs")) { @@ -357,7 +355,6 @@ namespace Vocaluxe.Base } } - CLog.Information("Read TXTs"); using (CBenchmark.Time("Read TXTs")) { foreach (string file in files) @@ -370,8 +367,7 @@ namespace Vocaluxe.Base _Songs.Add(song); } } - - CLog.Information("Sort Songs"); + using (CBenchmark.Time("Sorted Songs")) { Sorter.SongSorting = CConfig.Config.Game.SongSorting; @@ -408,7 +404,6 @@ namespace Vocaluxe.Base private static void _LoadCovers() { - CLog.Information("Load Covers"); using (CBenchmark.Time("Loaded Covers")) { int songCount = _Songs.Count; diff --git a/Vocaluxe/Base/Fonts/CFonts.cs b/Vocaluxe/Base/Fonts/CFonts.cs index fbb7d236..545b6c7f 100644 --- a/Vocaluxe/Base/Fonts/CFonts.cs +++ b/Vocaluxe/Base/Fonts/CFonts.cs @@ -288,8 +288,7 @@ namespace Vocaluxe.Base.Fonts return false; } } - - CLog.Information("Build Glyphs"); + using (CBenchmark.Time("Build Glyphs")) { _BuildGlyphs(); diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index 1c531a16..c4f04c18 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -103,15 +103,13 @@ namespace Vocaluxe if (!CProgrammHelper.CheckRequirements()) return; CProgrammHelper.Init(); - - CLog.Information("Init Program"); + using (CBenchmark.Time("Init Program")) { CMain.Init(); Application.DoEvents(); // Init Language - CLog.Information("Init Language"); using (CBenchmark.Time("Init Language")) { if (!CLanguage.Init()) @@ -121,7 +119,6 @@ namespace Vocaluxe Application.DoEvents(); // load config - CLog.Information("Init Config"); using (CBenchmark.Time("Init Config")) { CConfig.LoadCommandLineParams(args); @@ -137,7 +134,6 @@ namespace Vocaluxe Application.DoEvents(); // Init Draw - CLog.Information("Init Draw"); using (CBenchmark.Time("Init Draw")) { if (!CDraw.Init()) @@ -147,7 +143,6 @@ namespace Vocaluxe Application.DoEvents(); // Init Playback - CLog.Information("Init Playback"); using (CBenchmark.Time("Init Playback")) { if (!CSound.Init()) @@ -157,7 +152,6 @@ namespace Vocaluxe Application.DoEvents(); // Init Record - CLog.Information("Init Record"); using (CBenchmark.Time("Init Record")) { if (!CRecord.Init()) @@ -167,7 +161,6 @@ namespace Vocaluxe Application.DoEvents(); // Init VideoDecoder - CLog.Information("Init Videodecoder"); using (CBenchmark.Time("Init Videodecoder")) { if (!CVideo.Init()) @@ -177,7 +170,6 @@ namespace Vocaluxe Application.DoEvents(); // Init Database - CLog.Information("Init Database"); using (CBenchmark.Time("Init Database")) { if (!CDataBase.Init()) @@ -187,7 +179,6 @@ namespace Vocaluxe Application.DoEvents(); //Init Webcam - CLog.Information("Init Webcam"); using (CBenchmark.Time("Init Webcam")) { if (!CWebcam.Init()) @@ -197,7 +188,6 @@ namespace Vocaluxe Application.DoEvents(); // Init Background Music - CLog.Information("Init Background Music"); using (CBenchmark.Time("Init Background Music")) { CBackgroundMusic.Init(); @@ -206,7 +196,6 @@ namespace Vocaluxe Application.DoEvents(); // Init Profiles - CLog.Information("Init Profiles"); using (CBenchmark.Time("Init Profiles")) { CProfiles.Init(); @@ -215,7 +204,6 @@ namespace Vocaluxe Application.DoEvents(); // Init Fonts - CLog.Information("Init Fonts"); using (CBenchmark.Time("Init Fonts")) { if (!CFonts.Init()) @@ -225,14 +213,12 @@ namespace Vocaluxe Application.DoEvents(); // Theme System - CLog.Information("Init Theme"); using (CBenchmark.Time("Init Theme")) { if (!CThemes.Init()) throw new CLoadingException("theme"); } - - CLog.Information("Load Theme"); + using (CBenchmark.Time("Load Theme")) { CThemes.Load(); @@ -241,7 +227,6 @@ namespace Vocaluxe Application.DoEvents(); // Load Cover - CLog.Information("Init Cover"); using (CBenchmark.Time("Init Cover")) { if (!CCover.Init()) @@ -251,7 +236,6 @@ namespace Vocaluxe Application.DoEvents(); // Init Screens - CLog.Information("Init Screens"); using (CBenchmark.Time("Init Screens")) { CGraphics.Init(); @@ -260,7 +244,6 @@ namespace Vocaluxe Application.DoEvents(); // Init Server - CLog.Information("Init Server"); using (CBenchmark.Time("Init Server")) { CVocaluxeServer.Init(); @@ -269,7 +252,6 @@ namespace Vocaluxe Application.DoEvents(); // Init Input - CLog.Information("Init Input"); using (CBenchmark.Time("Init Input")) { CController.Init(); @@ -278,8 +260,7 @@ namespace Vocaluxe Application.DoEvents(); - // Init Game; - CLog.Information("Init Game"); + // Init Game using (CBenchmark.Time("Init Game")) { CGame.Init(); @@ -289,8 +270,7 @@ namespace Vocaluxe Application.DoEvents(); - // Init Party Modes; - CLog.Information("Init Party Modes"); + // Init Party Modes using (CBenchmark.Time("Init Party Modes")) { if (!CParty.Init()) diff --git a/Vocaluxe/Screens/CScreenLoad.cs b/Vocaluxe/Screens/CScreenLoad.cs index 38f28903..9057f4b2 100644 --- a/Vocaluxe/Screens/CScreenLoad.cs +++ b/Vocaluxe/Screens/CScreenLoad.cs @@ -126,8 +126,7 @@ namespace Vocaluxe.Screens foreach (CVideoPlayer videoPlayer in _Intros) videoPlayer.PreLoad(); } - - CLog.Information("Load Songs Full"); + _TimerLoadSongsFull = CBenchmark.Time("Loaded Songs Full"); _SongLoaderThread = new Thread(CSongs.LoadSongs) {Name = "SongLoader", IsBackground = true}; @@ -186,7 +185,6 @@ namespace Vocaluxe.Screens _TimerLoadSongsFull.Dispose(); //Init Playlists - CLog.Information("Init Playlists"); using (CBenchmark.Time("Init Playlists")) { CPlaylists.Init(); From 919a79b8b2334fbd16041f6f5ca9725de164bf81 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Fri, 19 Jan 2018 21:17:25 +0100 Subject: [PATCH 12/28] Add license to the files + rename some classes --- Vocaluxe/Reporting/Reporter.cs | 19 +++++++++- VocaluxeLib/Log/CBenchmark.cs | 19 +++++++++- VocaluxeLib/Log/CLog.cs | 36 +++++++++++++------ VocaluxeLib/Log/CLogFunctions.cs | 19 +++++++++- VocaluxeLib/Log/CLogFunctions.tt | 35 +++++++++++++++++- .../Enricher/CTimeStampFromStartEnricher.cs | 21 +++++++++-- .../{LogRoller.cs => CLogFileRoller.cs} | 21 +++++++++-- .../{SilentLogger.cs => CSilentLogger.cs} | 2 +- VocaluxeLib/Log/ShowReporterDelegate.cs | 18 +++++++++- VocaluxeLib/VocaluxeLib.csproj | 4 +-- 10 files changed, 172 insertions(+), 22 deletions(-) rename VocaluxeLib/Log/Rolling/{LogRoller.cs => CLogFileRoller.cs} (79%) rename VocaluxeLib/Log/Serilog/{SilentLogger.cs => CSilentLogger.cs} (99%) diff --git a/Vocaluxe/Reporting/Reporter.cs b/Vocaluxe/Reporting/Reporter.cs index e5e74105..2974836e 100644 --- a/Vocaluxe/Reporting/Reporter.cs +++ b/Vocaluxe/Reporting/Reporter.cs @@ -1,4 +1,21 @@ -using System; +#region license +// This file is part of Vocaluxe. +// +// Vocaluxe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vocaluxe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vocaluxe. If not, see . +#endregion + +using System; using System.Collections.Generic; using System.Net.Http; using System.Text; diff --git a/VocaluxeLib/Log/CBenchmark.cs b/VocaluxeLib/Log/CBenchmark.cs index 60e5818b..19ddf6c9 100644 --- a/VocaluxeLib/Log/CBenchmark.cs +++ b/VocaluxeLib/Log/CBenchmark.cs @@ -1,4 +1,21 @@ -using System; +#region license +// This file is part of Vocaluxe. +// +// Vocaluxe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vocaluxe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vocaluxe. If not, see . +#endregion + +using System; using System.Diagnostics; using VocaluxeLib.Utils; diff --git a/VocaluxeLib/Log/CLog.cs b/VocaluxeLib/Log/CLog.cs index 3d2ffa9b..da311ec7 100644 --- a/VocaluxeLib/Log/CLog.cs +++ b/VocaluxeLib/Log/CLog.cs @@ -1,6 +1,22 @@ -using System; +#region license +// This file is part of Vocaluxe. +// +// Vocaluxe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vocaluxe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vocaluxe. If not, see . +#endregion + +using System; using System.IO; -using System.Linq; using System.Text; using System.Text.RegularExpressions; using Serilog; @@ -15,8 +31,8 @@ namespace VocaluxeLib.Log private static readonly StringBuilder _MainLogStringBuilder = new StringBuilder(); private static readonly StringWriter _MainLogStringWriter = new StringWriter(_MainLogStringBuilder); - private static ILogger _MainLog = new SilentLogger(); - private static ILogger _SongLog = new SilentLogger(); + private static ILogger _MainLog = new CSilentLogger(); + private static ILogger _SongLog = new CSilentLogger(); private static string _LogFolder; private static string _CrashMarkerFilePath; private static ShowReporterDelegate _ShowReporterFunc; @@ -63,8 +79,8 @@ namespace VocaluxeLib.Log // Write new marker File.WriteAllText(_CrashMarkerFilePath, _CurrentVersion, Encoding.UTF8); - LogFileRoller.RollLogs(mainLogFilePath, 2); - LogFileRoller.RollLogs(songLogFilePath, 2); + CLogFileRoller.RollLogs(mainLogFilePath, 2); + CLogFileRoller.RollLogs(songLogFilePath, 2); _MainLog = new LoggerConfiguration() .Enrich.WithThreadId() @@ -88,19 +104,19 @@ namespace VocaluxeLib.Log public static void Close() { - if (!(_MainLog is SilentLogger)) + if (!(_MainLog is CSilentLogger)) { ILogger loggerToDispose = _MainLog; - _MainLog = new SilentLogger(); + _MainLog = new CSilentLogger(); (_MainLog as IDisposable)?.Dispose(); } - if (!(_SongLog is SilentLogger)) + if (!(_SongLog is CSilentLogger)) { ILogger loggerToDispose = _MainLog; - _SongLog = new SilentLogger(); + _SongLog = new CSilentLogger(); (_SongLog as IDisposable)?.Dispose(); } diff --git a/VocaluxeLib/Log/CLogFunctions.cs b/VocaluxeLib/Log/CLogFunctions.cs index c52ca532..1e234ed5 100644 --- a/VocaluxeLib/Log/CLogFunctions.cs +++ b/VocaluxeLib/Log/CLogFunctions.cs @@ -1,4 +1,21 @@ -using System; +#region license +// This file is part of Vocaluxe. +// +// Vocaluxe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vocaluxe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vocaluxe. If not, see . +#endregion + +using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Serilog.Context; diff --git a/VocaluxeLib/Log/CLogFunctions.tt b/VocaluxeLib/Log/CLogFunctions.tt index ec93f52c..e0bd38f8 100644 --- a/VocaluxeLib/Log/CLogFunctions.tt +++ b/VocaluxeLib/Log/CLogFunctions.tt @@ -1,9 +1,42 @@ -<#@ template debug="false" hostspecific="false" language="C#" #> +<# +// This file is part of Vocaluxe. +// +// Vocaluxe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vocaluxe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vocaluxe. If not, see . +#> +<#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> +#region license +// This file is part of Vocaluxe. +// +// Vocaluxe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vocaluxe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vocaluxe. If not, see . +#endregion + using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; diff --git a/VocaluxeLib/Log/Enricher/CTimeStampFromStartEnricher.cs b/VocaluxeLib/Log/Enricher/CTimeStampFromStartEnricher.cs index 4b924dba..825285cc 100644 --- a/VocaluxeLib/Log/Enricher/CTimeStampFromStartEnricher.cs +++ b/VocaluxeLib/Log/Enricher/CTimeStampFromStartEnricher.cs @@ -1,4 +1,21 @@ -using System; +#region license +// This file is part of Vocaluxe. +// +// Vocaluxe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vocaluxe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vocaluxe. If not, see . +#endregion + +using System; using Serilog; using Serilog.Configuration; using Serilog.Core; @@ -6,7 +23,7 @@ using Serilog.Events; namespace VocaluxeLib.Log.Enricher { - public static class WithTimeStampFromStartExtension + public static class CWithTimeStampFromStartExtension { // ReSharper disable once InconsistentNaming public static LoggerConfiguration WithTimeStampFromStart(this LoggerEnrichmentConfiguration enrich) diff --git a/VocaluxeLib/Log/Rolling/LogRoller.cs b/VocaluxeLib/Log/Rolling/CLogFileRoller.cs similarity index 79% rename from VocaluxeLib/Log/Rolling/LogRoller.cs rename to VocaluxeLib/Log/Rolling/CLogFileRoller.cs index adb69ab5..82eb2340 100644 --- a/VocaluxeLib/Log/Rolling/LogRoller.cs +++ b/VocaluxeLib/Log/Rolling/CLogFileRoller.cs @@ -1,11 +1,28 @@ -using System; +#region license +// This file is part of Vocaluxe. +// +// Vocaluxe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vocaluxe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vocaluxe. If not, see . +#endregion + +using System; using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; namespace VocaluxeLib.Log.Rolling { - static class LogFileRoller + static class CLogFileRoller { public static void RollLogs(string mainLogFile, int numLogsToKeep) { diff --git a/VocaluxeLib/Log/Serilog/SilentLogger.cs b/VocaluxeLib/Log/Serilog/CSilentLogger.cs similarity index 99% rename from VocaluxeLib/Log/Serilog/SilentLogger.cs rename to VocaluxeLib/Log/Serilog/CSilentLogger.cs index 475a530f..d7c1a244 100644 --- a/VocaluxeLib/Log/Serilog/SilentLogger.cs +++ b/VocaluxeLib/Log/Serilog/CSilentLogger.cs @@ -22,7 +22,7 @@ using Serilog.Events; namespace VocaluxeLib.Log.Serilog { - class SilentLogger : ILogger + class CSilentLogger : ILogger { public ILogger ForContext(ILogEventEnricher enricher) { diff --git a/VocaluxeLib/Log/ShowReporterDelegate.cs b/VocaluxeLib/Log/ShowReporterDelegate.cs index c2750515..8edf6025 100644 --- a/VocaluxeLib/Log/ShowReporterDelegate.cs +++ b/VocaluxeLib/Log/ShowReporterDelegate.cs @@ -1,4 +1,20 @@ - +#region license +// This file is part of Vocaluxe. +// +// Vocaluxe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vocaluxe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vocaluxe. If not, see . +#endregion + namespace VocaluxeLib.Log { /// diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj index 00d4b977..5172700f 100644 --- a/VocaluxeLib/VocaluxeLib.csproj +++ b/VocaluxeLib/VocaluxeLib.csproj @@ -119,8 +119,8 @@ - - + + True True From 735992d5c56139b375ff9106265938ccee72d374 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Fri, 19 Jan 2018 21:39:35 +0100 Subject: [PATCH 13/28] Add run information to the log --- VocaluxeLib/Log/CLog.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/VocaluxeLib/Log/CLog.cs b/VocaluxeLib/Log/CLog.cs index da311ec7..10764b37 100644 --- a/VocaluxeLib/Log/CLog.cs +++ b/VocaluxeLib/Log/CLog.cs @@ -100,6 +100,9 @@ namespace VocaluxeLib.Log .WriteTo.Console(outputTemplate: "[SongInfo] " + _SongLogTemplate) #endif .CreateLogger(); + + // Adding fist line to log with information about this run + Information("Starting to log", Params( new { Version = _CurrentVersion, StartDate = DateTime.Now, Id = Guid.NewGuid() } ) ); } public static void Close() From 6c4a710b819ddfa92a3e23db4fd73585d08a7352 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Sat, 20 Jan 2018 08:03:29 +0100 Subject: [PATCH 14/28] Fix small bug in the release build --- Vocaluxe/Program.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index c4f04c18..024b9575 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -434,7 +434,8 @@ namespace Vocaluxe [HandleProcessCorruptedStateExceptions] private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) { - CLog.Fatal((Exception)args.ExceptionObject, "Unhandled exception: {ExceptionMessage}", CLog.Params(e.Message)); + var e = (Exception)args.ExceptionObject; + CLog.Fatal(e, "Unhandled exception: {ExceptionMessage}", CLog.Params(e.Message)); } #endif From 5b3e13f373033fa0fbc97027d3901892dc23bab1 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Wed, 24 Jan 2018 18:06:13 +0100 Subject: [PATCH 15/28] Log logging init errors to console if DEBUG flag is present. --- VocaluxeLib/Log/Rolling/CLogFileRoller.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/VocaluxeLib/Log/Rolling/CLogFileRoller.cs b/VocaluxeLib/Log/Rolling/CLogFileRoller.cs index 82eb2340..89c466a4 100644 --- a/VocaluxeLib/Log/Rolling/CLogFileRoller.cs +++ b/VocaluxeLib/Log/Rolling/CLogFileRoller.cs @@ -42,9 +42,12 @@ namespace VocaluxeLib.Log.Rolling { File.Move(currentSourceFile, currentTargetFile); } - catch (Exception) + catch (Exception e) { // Cant log anything here as the log isn't initialized yet +#if DEBUG + Console.WriteLine($"Error moving old log file: {e.Message}"); +#endif } } } @@ -58,9 +61,12 @@ namespace VocaluxeLib.Log.Rolling else File.Delete(mainLogFile); } - catch (Exception) + catch (Exception e) { // Cant log anything here as the log isn't initialized yet +#if DEBUG + Console.WriteLine($"Error moving old main log file: {e.Message}"); +#endif } } } @@ -83,9 +89,12 @@ namespace VocaluxeLib.Log.Rolling { File.Delete(file); } - catch (Exception) + catch (Exception e) { // Cant log anything here as the log isn't initialized yet +#if DEBUG + Console.WriteLine($"Error deleting old log file: {e.Message}"); +#endif } } From 5e1dc0e4218c400337ef0614949f2d95d37295b8 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Wed, 24 Jan 2018 18:09:44 +0100 Subject: [PATCH 16/28] Refactor code in comment to use CLog.Fatal() --- Vocaluxe/CProgrammHelper.cs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Vocaluxe/CProgrammHelper.cs b/Vocaluxe/CProgrammHelper.cs index 52e7d055..a7323c8c 100644 --- a/Vocaluxe/CProgrammHelper.cs +++ b/Vocaluxe/CProgrammHelper.cs @@ -123,25 +123,23 @@ namespace Vocaluxe "VC++ 2010 Redistributables are missing. Please install them first. \r\nDownload(x86): https://www.microsoft.com/de-de/download/details.aspx?id=5555 \r\nDownload(x64): https://www.microsoft.com/de-de/download/details.aspx?id=14632"); return false; } - /*if (!_IsVC2012Installed()) - { - CLog.Error( - "VC++ 2012 Redistributables are missing. Please install them first.\r\nDownload: http://www.microsoft.com/de-de/download/details.aspx?id=30679", - true, true); - return false; - }*/ /* + if (!_IsVC2012Installed()) + { + CLog.Fatal( + "VC++ 2012 Redistributables are missing. Please install them first.\r\nDownload: http://www.microsoft.com/de-de/download/details.aspx?id=30679"); + return false; + } bool vc2008Installed = _IsVC2008Installed(); if (!vc2008Installed) { - CLog.Error( + CLog.Fatal( "VC++ 2008 Redistributables are missing. Portaudio might not be working.\r\nDownload: http://www.microsoft.com/de-de/download/details.aspx?id=29"); } if (!vc2008Installed && !_IsVC2010Installed()) { - CLog.Error( - "VC++ 2010 and 2008 Redistributables are missing. Please install them first. VC++ 2008 is preferred as Portaudio doesn't work with VC++ 2010.\r\nDownload(2008): http://www.microsoft.com/de-de/download/details.aspx?id=29 \r\nDownload(2010): http://www.microsoft.com/de-de/download/details.aspx?id=5555", - true, true); + CLog.Fatal( + "VC++ 2010 and 2008 Redistributables are missing. Please install them first. VC++ 2008 is preferred as Portaudio doesn't work with VC++ 2010.\r\nDownload(2008): http://www.microsoft.com/de-de/download/details.aspx?id=29 \r\nDownload(2010): http://www.microsoft.com/de-de/download/details.aspx?id=5555"); return false; } */ From 5edd1cbed5dd31b03aff88f7ad415dcf60211d60 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Wed, 24 Jan 2018 18:31:51 +0100 Subject: [PATCH 17/28] Add some documentation for CLog --- VocaluxeLib/Log/CLog.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/VocaluxeLib/Log/CLog.cs b/VocaluxeLib/Log/CLog.cs index 10764b37..74087883 100644 --- a/VocaluxeLib/Log/CLog.cs +++ b/VocaluxeLib/Log/CLog.cs @@ -41,6 +41,16 @@ namespace VocaluxeLib.Log private const string _MainLogTemplate = "[{TimeStampFromStart}] [{Level}] {Message}{NewLine}{Properties}{NewLine}{Exception}"; private const string _SongLogTemplate = "{Message}{NewLine}Additional info:{Properties}{NewLine}{Exception}"; + + /// + /// Initialize the logging framework. + /// + /// The folder where to write the log files. + /// The name of the main log. + /// The name of the log for song problems. + /// The name of the file which is used as crash marker. + /// The current version tag as it is displayed in the main menu. + /// Delegate to the function wchih should be called if the reporter have to been shown. public static void Init(string logFolder, string fileNameMainLog, string fileNameSongInfoLog, string fileNameCrashMarker, string currentVersion, ShowReporterDelegate showReporterFunc) { _LogFolder = logFolder; @@ -105,6 +115,9 @@ namespace VocaluxeLib.Log Information("Starting to log", Params( new { Version = _CurrentVersion, StartDate = DateTime.Now, Id = Guid.NewGuid() } ) ); } + /// + /// Close all log files and the close the logging framework. + /// public static void Close() { if (!(_MainLog is CSilentLogger)) @@ -128,6 +141,13 @@ namespace VocaluxeLib.Log } + /// + /// Show the report assistent. + /// + /// Last error message template. + /// Data to fill the last error message template. + /// True if the crash version of the message should be shown. + /// True if the assistent should show a continue butoon, exit button otherwise. public static void ShowLogAssistent(string messageTemplate, object[] propertyValues, bool crash = false, bool showContinue = true) { // Flush the _MainLogStringWriter to get the latest entries to _MainLogStringBuilder From a3efd0ac22a6971a5356bc254136a25ef991bcc4 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Wed, 24 Jan 2018 18:32:39 +0100 Subject: [PATCH 18/28] Fix typo --- VocaluxeLib/Log/CLog.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/VocaluxeLib/Log/CLog.cs b/VocaluxeLib/Log/CLog.cs index 74087883..81886788 100644 --- a/VocaluxeLib/Log/CLog.cs +++ b/VocaluxeLib/Log/CLog.cs @@ -142,13 +142,13 @@ namespace VocaluxeLib.Log } /// - /// Show the report assistent. + /// Show the report assistant. /// /// Last error message template. /// Data to fill the last error message template. /// True if the crash version of the message should be shown. - /// True if the assistent should show a continue butoon, exit button otherwise. - public static void ShowLogAssistent(string messageTemplate, object[] propertyValues, bool crash = false, bool showContinue = true) + /// True if the assistant should show a continue butoon, exit button otherwise. + public static void ShowLogAssistant(string messageTemplate, object[] propertyValues, bool crash = false, bool showContinue = true) { // Flush the _MainLogStringWriter to get the latest entries to _MainLogStringBuilder _MainLogStringWriter.Flush(); From 69f44c3eba21b6e199c2f23cafcb4571f179a818 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Wed, 24 Jan 2018 18:34:36 +0100 Subject: [PATCH 19/28] Fix typo 2 --- Vocaluxe/Base/CGraphics.cs | 2 +- VocaluxeLib/Log/CLogFunctions.tt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Vocaluxe/Base/CGraphics.cs b/Vocaluxe/Base/CGraphics.cs index 5d854bf8..b171d94e 100644 --- a/Vocaluxe/Base/CGraphics.cs +++ b/Vocaluxe/Base/CGraphics.cs @@ -442,7 +442,7 @@ namespace Vocaluxe.Base if (keyEvent.Key == Keys.F8) { - CLog.ShowLogAssistent("", null); + CLog.ShowLogAssistant("", null); } if (popupPlayerControlAllowed && keyEvent.Key == Keys.Tab) diff --git a/VocaluxeLib/Log/CLogFunctions.tt b/VocaluxeLib/Log/CLogFunctions.tt index e0bd38f8..e7dfa8c0 100644 --- a/VocaluxeLib/Log/CLogFunctions.tt +++ b/VocaluxeLib/Log/CLogFunctions.tt @@ -85,7 +85,7 @@ namespace VocaluxeLib.Log } <# if(Log == "Main") { #> if(show) - ShowLogAssistent(messageTemplate, null<#= LogLevel == "Fatal"?", true, false":"" #>); + ShowLogAssistant(messageTemplate, null<#= LogLevel == "Fatal"?", true, false":"" #>); <# } #> <# if(LogLevel == "Fatal") { #> @@ -115,7 +115,7 @@ namespace VocaluxeLib.Log } <# if(Log == "Main") { #> if(show) - ShowLogAssistent(messageTemplate, propertyValues<#= LogLevel == "Fatal"?", true, false":"" #>); + ShowLogAssistant(messageTemplate, propertyValues<#= LogLevel == "Fatal"?", true, false":"" #>); <# } #> <# if(LogLevel == "Fatal") { #> @@ -145,7 +145,7 @@ namespace VocaluxeLib.Log } <# if(Log == "Main") { #> if(show) - ShowLogAssistent(messageTemplate, null<#= LogLevel == "Fatal"?", true, false":"" #>); + ShowLogAssistant(messageTemplate, null<#= LogLevel == "Fatal"?", true, false":"" #>); <# } #> <# if(LogLevel == "Fatal") { #> @@ -176,7 +176,7 @@ namespace VocaluxeLib.Log } <# if(Log == "Main") { #> if(show) - ShowLogAssistent(messageTemplate, propertyValues<#= LogLevel == "Fatal"?", true, false":"" #>); + ShowLogAssistant(messageTemplate, propertyValues<#= LogLevel == "Fatal"?", true, false":"" #>); <# } #> <# if(LogLevel == "Fatal") { #> From 0e0db565d2cf2a461ed0349350a17ce213d9667f Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Wed, 24 Jan 2018 18:41:48 +0100 Subject: [PATCH 20/28] Fix typo 3 --- VocaluxeLib/Log/CLogFunctions.cs | 48 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/VocaluxeLib/Log/CLogFunctions.cs b/VocaluxeLib/Log/CLogFunctions.cs index 1e234ed5..b5622435 100644 --- a/VocaluxeLib/Log/CLogFunctions.cs +++ b/VocaluxeLib/Log/CLogFunctions.cs @@ -50,7 +50,7 @@ namespace VocaluxeLib.Log _MainLog.Verbose(messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null); + ShowLogAssistant(messageTemplate, null); } /// @@ -70,7 +70,7 @@ namespace VocaluxeLib.Log _MainLog.Verbose(messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues); + ShowLogAssistant(messageTemplate, propertyValues); } /// @@ -90,7 +90,7 @@ namespace VocaluxeLib.Log _MainLog.Verbose( exception, messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null); + ShowLogAssistant(messageTemplate, null); } /// @@ -111,7 +111,7 @@ namespace VocaluxeLib.Log _MainLog.Verbose(exception, messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues); + ShowLogAssistant(messageTemplate, propertyValues); } #endregion @@ -134,7 +134,7 @@ namespace VocaluxeLib.Log _MainLog.Debug(messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null); + ShowLogAssistant(messageTemplate, null); } /// @@ -154,7 +154,7 @@ namespace VocaluxeLib.Log _MainLog.Debug(messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues); + ShowLogAssistant(messageTemplate, propertyValues); } /// @@ -174,7 +174,7 @@ namespace VocaluxeLib.Log _MainLog.Debug( exception, messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null); + ShowLogAssistant(messageTemplate, null); } /// @@ -195,7 +195,7 @@ namespace VocaluxeLib.Log _MainLog.Debug(exception, messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues); + ShowLogAssistant(messageTemplate, propertyValues); } #endregion @@ -218,7 +218,7 @@ namespace VocaluxeLib.Log _MainLog.Information(messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null); + ShowLogAssistant(messageTemplate, null); } /// @@ -238,7 +238,7 @@ namespace VocaluxeLib.Log _MainLog.Information(messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues); + ShowLogAssistant(messageTemplate, propertyValues); } /// @@ -258,7 +258,7 @@ namespace VocaluxeLib.Log _MainLog.Information( exception, messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null); + ShowLogAssistant(messageTemplate, null); } /// @@ -279,7 +279,7 @@ namespace VocaluxeLib.Log _MainLog.Information(exception, messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues); + ShowLogAssistant(messageTemplate, propertyValues); } #endregion @@ -302,7 +302,7 @@ namespace VocaluxeLib.Log _MainLog.Warning(messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null); + ShowLogAssistant(messageTemplate, null); } /// @@ -322,7 +322,7 @@ namespace VocaluxeLib.Log _MainLog.Warning(messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues); + ShowLogAssistant(messageTemplate, propertyValues); } /// @@ -342,7 +342,7 @@ namespace VocaluxeLib.Log _MainLog.Warning( exception, messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null); + ShowLogAssistant(messageTemplate, null); } /// @@ -363,7 +363,7 @@ namespace VocaluxeLib.Log _MainLog.Warning(exception, messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues); + ShowLogAssistant(messageTemplate, propertyValues); } #endregion @@ -386,7 +386,7 @@ namespace VocaluxeLib.Log _MainLog.Error(messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null); + ShowLogAssistant(messageTemplate, null); } /// @@ -406,7 +406,7 @@ namespace VocaluxeLib.Log _MainLog.Error(messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues); + ShowLogAssistant(messageTemplate, propertyValues); } /// @@ -426,7 +426,7 @@ namespace VocaluxeLib.Log _MainLog.Error( exception, messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null); + ShowLogAssistant(messageTemplate, null); } /// @@ -447,7 +447,7 @@ namespace VocaluxeLib.Log _MainLog.Error(exception, messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues); + ShowLogAssistant(messageTemplate, propertyValues); } #endregion @@ -470,7 +470,7 @@ namespace VocaluxeLib.Log _MainLog.Fatal(messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null, true, false); + ShowLogAssistant(messageTemplate, null, true, false); // Close logs and exit Close(); @@ -494,7 +494,7 @@ namespace VocaluxeLib.Log _MainLog.Fatal(messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues, true, false); + ShowLogAssistant(messageTemplate, propertyValues, true, false); // Close logs and exit Close(); @@ -518,7 +518,7 @@ namespace VocaluxeLib.Log _MainLog.Fatal( exception, messageTemplate); } if(show) - ShowLogAssistent(messageTemplate, null, true, false); + ShowLogAssistant(messageTemplate, null, true, false); // Close logs and exit Close(); @@ -543,7 +543,7 @@ namespace VocaluxeLib.Log _MainLog.Fatal(exception, messageTemplate, propertyValues); } if(show) - ShowLogAssistent(messageTemplate, propertyValues, true, false); + ShowLogAssistant(messageTemplate, propertyValues, true, false); // Close logs and exit Close(); From 64f077b1cf0a6578342ad89f56908d7cddfc2733 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Thu, 25 Jan 2018 13:06:28 +0100 Subject: [PATCH 21/28] Remove (now unneeded) serilog packages from Vocaluxe --- Vocaluxe/Base/CGraphics.cs | 1 - Vocaluxe/Vocaluxe.csproj | 24 ------------------------ Vocaluxe/packages.config | 6 ------ 3 files changed, 31 deletions(-) diff --git a/Vocaluxe/Base/CGraphics.cs b/Vocaluxe/Base/CGraphics.cs index b171d94e..5803ddc5 100644 --- a/Vocaluxe/Base/CGraphics.cs +++ b/Vocaluxe/Base/CGraphics.cs @@ -20,7 +20,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Windows.Forms; -using Serilog.Events; using Vocaluxe.Base.Fonts; using Vocaluxe.Base.ThemeSystem; using Vocaluxe.Screens; diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index 388cb950..4e603b64 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -163,30 +163,6 @@ ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll True - - ..\packages\Serilog.2.6.0\lib\net46\Serilog.dll - True - - - ..\packages\Serilog.Enrichers.Thread.3.0.0\lib\net45\Serilog.Enrichers.Thread.dll - True - - - ..\packages\Serilog.Formatting.Compact.1.0.0\lib\net45\Serilog.Formatting.Compact.dll - True - - - ..\packages\Serilog.Sinks.Console.3.1.1\lib\net45\Serilog.Sinks.Console.dll - True - - - ..\packages\Serilog.Sinks.File.4.0.0\lib\net45\Serilog.Sinks.File.dll - True - - - ..\packages\Serilog.Sinks.TextWriter.2.1.0\lib\net45\Serilog.Sinks.TextWriter.dll - True - diff --git a/Vocaluxe/packages.config b/Vocaluxe/packages.config index c6d7fd55..199eb932 100644 --- a/Vocaluxe/packages.config +++ b/Vocaluxe/packages.config @@ -1,12 +1,6 @@  - - - - - - From 7f2f5dd2d2b6b05ec7f39cd96e4a09e6a896fff9 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Thu, 25 Jan 2018 13:15:13 +0100 Subject: [PATCH 22/28] Prevent resizing of the reporter form. --- Vocaluxe/Reporting/Reporter.Designer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Vocaluxe/Reporting/Reporter.Designer.cs b/Vocaluxe/Reporting/Reporter.Designer.cs index 59fc5577..965a3e6c 100644 --- a/Vocaluxe/Reporting/Reporter.Designer.cs +++ b/Vocaluxe/Reporting/Reporter.Designer.cs @@ -192,6 +192,7 @@ this.Controls.Add(this.LogBox); this.Controls.Add(this.MessageText); this.Controls.Add(this.TitleLabel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "CReporter"; this.Text = "Vocaluxe Issue Reporter"; From 5b61bc4bd224ed7afc8c9beceb366bff541a0b2c Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Sun, 28 Jan 2018 14:17:47 +0100 Subject: [PATCH 23/28] Fix: Additional properties are ignored --- VocaluxeLib/Log/CLog.cs | 4 +- VocaluxeLib/Log/CLogFunctions.cs | 355 +++++++++++++++++++++++++++---- VocaluxeLib/Log/CLogFunctions.tt | 35 ++- 3 files changed, 344 insertions(+), 50 deletions(-) diff --git a/VocaluxeLib/Log/CLog.cs b/VocaluxeLib/Log/CLog.cs index 81886788..20099887 100644 --- a/VocaluxeLib/Log/CLog.cs +++ b/VocaluxeLib/Log/CLog.cs @@ -111,8 +111,8 @@ namespace VocaluxeLib.Log #endif .CreateLogger(); - // Adding fist line to log with information about this run - Information("Starting to log", Params( new { Version = _CurrentVersion, StartDate = DateTime.Now, Id = Guid.NewGuid() } ) ); + // Adding first line to log with information about this run + Information("Starting to log", Params( new { Version = _CurrentVersion}, new { StartDate = DateTime.Now}, new { Id = Guid.NewGuid() } ) ); } /// diff --git a/VocaluxeLib/Log/CLogFunctions.cs b/VocaluxeLib/Log/CLogFunctions.cs index b5622435..b3ea7f96 100644 --- a/VocaluxeLib/Log/CLogFunctions.cs +++ b/VocaluxeLib/Log/CLogFunctions.cs @@ -17,7 +17,9 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; using Serilog.Context; using Serilog.Core; @@ -25,6 +27,7 @@ namespace VocaluxeLib.Log { public static partial class CLog { + private static Regex _PropertiesRegex = new Regex("{[^}]+}"); public static object[] Params(params object[] propertyValues) { @@ -46,8 +49,8 @@ namespace VocaluxeLib.Log public static void Verbose(string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _MainLog.Verbose(messageTemplate); + { + _MainLog.Verbose(messageTemplate); } if(show) ShowLogAssistant(messageTemplate, null); @@ -67,7 +70,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Verbose(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Verbose(messageTemplate, propertyValues); + } + } + else + { + _MainLog.Verbose(messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues); @@ -108,7 +123,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Verbose(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Verbose(exception, messageTemplate, propertyValues); + } + } + else + { + _MainLog.Verbose(exception, messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues); @@ -130,8 +157,8 @@ namespace VocaluxeLib.Log public static void Debug(string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _MainLog.Debug(messageTemplate); + { + _MainLog.Debug(messageTemplate); } if(show) ShowLogAssistant(messageTemplate, null); @@ -151,7 +178,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Debug(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Debug(messageTemplate, propertyValues); + } + } + else + { + _MainLog.Debug(messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues); @@ -192,7 +231,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Debug(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Debug(exception, messageTemplate, propertyValues); + } + } + else + { + _MainLog.Debug(exception, messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues); @@ -214,8 +265,8 @@ namespace VocaluxeLib.Log public static void Information(string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _MainLog.Information(messageTemplate); + { + _MainLog.Information(messageTemplate); } if(show) ShowLogAssistant(messageTemplate, null); @@ -235,7 +286,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Information(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Information(messageTemplate, propertyValues); + } + } + else + { + _MainLog.Information(messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues); @@ -276,7 +339,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Information(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Information(exception, messageTemplate, propertyValues); + } + } + else + { + _MainLog.Information(exception, messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues); @@ -298,8 +373,8 @@ namespace VocaluxeLib.Log public static void Warning(string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _MainLog.Warning(messageTemplate); + { + _MainLog.Warning(messageTemplate); } if(show) ShowLogAssistant(messageTemplate, null); @@ -319,7 +394,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Warning(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Warning(messageTemplate, propertyValues); + } + } + else + { + _MainLog.Warning(messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues); @@ -360,7 +447,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Warning(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Warning(exception, messageTemplate, propertyValues); + } + } + else + { + _MainLog.Warning(exception, messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues); @@ -382,8 +481,8 @@ namespace VocaluxeLib.Log public static void Error(string messageTemplate, bool show = false, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _MainLog.Error(messageTemplate); + { + _MainLog.Error(messageTemplate); } if(show) ShowLogAssistant(messageTemplate, null); @@ -403,7 +502,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Error(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Error(messageTemplate, propertyValues); + } + } + else + { + _MainLog.Error(messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues); @@ -444,7 +555,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Error(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Error(exception, messageTemplate, propertyValues); + } + } + else + { + _MainLog.Error(exception, messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues); @@ -466,8 +589,8 @@ namespace VocaluxeLib.Log public static void Fatal(string messageTemplate, bool show = true, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _MainLog.Fatal(messageTemplate); + { + _MainLog.Fatal(messageTemplate); } if(show) ShowLogAssistant(messageTemplate, null, true, false); @@ -491,7 +614,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Fatal(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Fatal(messageTemplate, propertyValues); + } + } + else + { + _MainLog.Fatal(messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues, true, false); @@ -540,7 +675,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _MainLog.Fatal(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _MainLog.Fatal(exception, messageTemplate, propertyValues); + } + } + else + { + _MainLog.Fatal(exception, messageTemplate, propertyValues); + } } if(show) ShowLogAssistant(messageTemplate, propertyValues, true, false); @@ -569,8 +716,8 @@ namespace VocaluxeLib.Log public static void Verbose(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _SongLog.Verbose(messageTemplate); + { + _SongLog.Verbose(messageTemplate); } } @@ -587,7 +734,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _SongLog.Verbose(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _SongLog.Verbose(messageTemplate, propertyValues); + } + } + else + { + _SongLog.Verbose(messageTemplate, propertyValues); + } } } @@ -622,7 +781,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _SongLog.Verbose(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _SongLog.Verbose(exception, messageTemplate, propertyValues); + } + } + else + { + _SongLog.Verbose(exception, messageTemplate, propertyValues); + } } } @@ -641,8 +812,8 @@ namespace VocaluxeLib.Log public static void Debug(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _SongLog.Debug(messageTemplate); + { + _SongLog.Debug(messageTemplate); } } @@ -659,7 +830,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _SongLog.Debug(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _SongLog.Debug(messageTemplate, propertyValues); + } + } + else + { + _SongLog.Debug(messageTemplate, propertyValues); + } } } @@ -694,7 +877,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _SongLog.Debug(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _SongLog.Debug(exception, messageTemplate, propertyValues); + } + } + else + { + _SongLog.Debug(exception, messageTemplate, propertyValues); + } } } @@ -713,8 +908,8 @@ namespace VocaluxeLib.Log public static void Information(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _SongLog.Information(messageTemplate); + { + _SongLog.Information(messageTemplate); } } @@ -731,7 +926,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _SongLog.Information(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _SongLog.Information(messageTemplate, propertyValues); + } + } + else + { + _SongLog.Information(messageTemplate, propertyValues); + } } } @@ -766,7 +973,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _SongLog.Information(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _SongLog.Information(exception, messageTemplate, propertyValues); + } + } + else + { + _SongLog.Information(exception, messageTemplate, propertyValues); + } } } @@ -785,8 +1004,8 @@ namespace VocaluxeLib.Log public static void Warning(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _SongLog.Warning(messageTemplate); + { + _SongLog.Warning(messageTemplate); } } @@ -803,7 +1022,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _SongLog.Warning(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _SongLog.Warning(messageTemplate, propertyValues); + } + } + else + { + _SongLog.Warning(messageTemplate, propertyValues); + } } } @@ -838,7 +1069,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _SongLog.Warning(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _SongLog.Warning(exception, messageTemplate, propertyValues); + } + } + else + { + _SongLog.Warning(exception, messageTemplate, propertyValues); + } } } @@ -857,8 +1100,8 @@ namespace VocaluxeLib.Log public static void Error(string messageTemplate, [CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - _SongLog.Error(messageTemplate); + { + _SongLog.Error(messageTemplate); } } @@ -875,7 +1118,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _SongLog.Error(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _SongLog.Error(messageTemplate, propertyValues); + } + } + else + { + _SongLog.Error(messageTemplate, propertyValues); + } } } @@ -910,7 +1165,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - _SongLog.Error(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + _SongLog.Error(exception, messageTemplate, propertyValues); + } + } + else + { + _SongLog.Error(exception, messageTemplate, propertyValues); + } } } diff --git a/VocaluxeLib/Log/CLogFunctions.tt b/VocaluxeLib/Log/CLogFunctions.tt index e7dfa8c0..184edf6a 100644 --- a/VocaluxeLib/Log/CLogFunctions.tt +++ b/VocaluxeLib/Log/CLogFunctions.tt @@ -39,7 +39,9 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; using Serilog.Context; using Serilog.Core; @@ -47,6 +49,7 @@ namespace VocaluxeLib.Log { public static partial class CLog { + private static Regex _PropertiesRegex = new Regex("{[^}]+}"); public static object[] Params(params object[] propertyValues) { @@ -80,8 +83,8 @@ namespace VocaluxeLib.Log public static void <#= LogLevel #>(string messageTemplate, <#= Log == "Main"?(LogLevel == "Fatal"?"bool show = true, ":"bool show = false, "):"" #>[CallerMemberName] string callerMethodeName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumer = -1) { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) - { - <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>(messageTemplate); + { + <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>(messageTemplate); } <# if(Log == "Main") { #> if(show) @@ -111,7 +114,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>(messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>(messageTemplate, propertyValues); + } + } + else + { + <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>(messageTemplate, propertyValues); + } } <# if(Log == "Main") { #> if(show) @@ -172,7 +187,19 @@ namespace VocaluxeLib.Log { using (LogContext.PushProperty("CallingContext", new { callerMethodeName, callerFilePath, callerLineNumer})) { - <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>(exception, messageTemplate, propertyValues); + int usedPropertiesCount = _PropertiesRegex.Matches(messageTemplate).Cast().Select(m => m.Value).Distinct().Count(); + + if (propertyValues.Length > usedPropertiesCount) + { + using (LogContext.PushProperty("AdditionalData", propertyValues.Skip(usedPropertiesCount))) + { + <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>(exception, messageTemplate, propertyValues); + } + } + else + { + <#= Log == "Main"?"_MainLog":"_SongLog" #>.<#= LogLevel #>(exception, messageTemplate, propertyValues); + } } <# if(Log == "Main") { #> if(show) From 5b66ae770252f2cd4714557c56b0ae955e9aba1f Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Sun, 28 Jan 2018 14:19:25 +0100 Subject: [PATCH 24/28] Hide Minimize and Maximize button --- Vocaluxe/Reporting/Reporter.Designer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Vocaluxe/Reporting/Reporter.Designer.cs b/Vocaluxe/Reporting/Reporter.Designer.cs index 965a3e6c..4a27bf90 100644 --- a/Vocaluxe/Reporting/Reporter.Designer.cs +++ b/Vocaluxe/Reporting/Reporter.Designer.cs @@ -194,6 +194,8 @@ this.Controls.Add(this.TitleLabel); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MinimizeBox = false; this.Name = "CReporter"; this.Text = "Vocaluxe Issue Reporter"; this.ResumeLayout(false); From 80ea1bce7ef21638dcbd6005ab489a05be96f0e5 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Sun, 28 Jan 2018 14:22:38 +0100 Subject: [PATCH 25/28] Fix: "Error on start up" does not show the assistant --- Vocaluxe/Program.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index 024b9575..75406466 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -285,8 +285,7 @@ namespace Vocaluxe } catch (Exception e) { - MessageBox.Show("Error on start up: " + e.Message); - CLog.Error("Error on start up: " + e); + CLog.Error(e, "Error on start up: {ExceptionMessage}", CLog.Params(e.Message), show:true); if (_SplashScreen != null) _SplashScreen.Close(); _CloseProgram(); From 09ed5a36187933995918b8761efd9a1054079280 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Sun, 28 Jan 2018 14:42:09 +0100 Subject: [PATCH 26/28] Make strings in the report assistant localizable --- Output/Languages/English.xml | 15 +++++++++++++++ Vocaluxe/Reporting/Reporter.cs | 35 +++++++++++++++++----------------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/Output/Languages/English.xml b/Output/Languages/English.xml index b6d55298..948080a0 100644 --- a/Output/Languages/English.xml +++ b/Output/Languages/English.xml @@ -242,4 +242,19 @@ Volume Server address Server is not running. Please check config.xml! + We are sorry + that Vocaluxe run into an error and crashed.\r\nTo help us fix the problem please send us a report.\r\nEdit the following log before submitting to remove possibly sensitive information. + that you experienced an error.\r\nTo help us fix the problem please send us a report.\r\nEdit the following log before submitting to remove possibly sensitive information. + Don\'t upload anything (you can still copy the error message above) + Upload and open an issue (publicly visible + requires a github account) + Upload and get a link to your report (publicly visible) + Submit + Uploading + Exit + Continue + The link to your report: + Error uploading the log. + Last error + Not available + Describe your issue here.\n\n### Steps to reproduce\nTell us how to reproduce this issue.\n\n### Vocaluxe version and logfile\n{0}\n{1} diff --git a/Vocaluxe/Reporting/Reporter.cs b/Vocaluxe/Reporting/Reporter.cs index 2974836e..e6297e3a 100644 --- a/Vocaluxe/Reporting/Reporter.cs +++ b/Vocaluxe/Reporting/Reporter.cs @@ -23,29 +23,30 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; using Newtonsoft.Json; +using Vocaluxe.Base; using VocaluxeLib.Log; namespace Vocaluxe.Reporting { public partial class CReporter : Form { - private string _TitleText = "We are sorry"; - private string _MessageCrashText = "that Vocaluxe run into an error and crashed.\r\nTo help us fix the problem please send us a report.\r\nEdit the following log before submitting to remove possibly sensitive information."; - private string _MessageNoCrashText = "that you experienced an error.\r\nTo help us fix the problem please send us a report.\r\nEdit the following log before submitting to remove possibly sensitive information."; - private string _NoUploadText = "Don\'t upload anything (you can still copy the error message above)"; - private string _GistAndIssueText = "Upload and open an issue (publicly visible + requires a github account)"; - private string _GistOnlyText = "Upload and get a link to your report (publicly visible)"; - private string _SubmitStep0Text = "Submit"; - private string _SubmitStep1Text = "Uploading"; - private string _SubmitStep2ExitText = "Exit"; - private string _SubmitStep2ContinueText = "Continue"; - private string _SubmitedMessageText = "The link to your report:"; - private string _LogUploadErrorText = "Error uploading the log."; - private string _LastErrorTitleText = "Last error"; - private string _LastErrorNA = "Not available"; + private readonly string _TitleText = CLanguage.Translate("TR_REPORTER_TITLE_TEXT"); + private readonly string _MessageCrashText = CLanguage.Translate("TR_REPORTER_MESSAGE_CRASH_TEXT"); + private readonly string _MessageNoCrashText = CLanguage.Translate("TR_REPORTER_MESSAGE_NO_CRASH_TEXT"); + private readonly string _NoUploadText = CLanguage.Translate("TR_REPORTER_NO_UPLOAD_TEXT"); + private readonly string _GistAndIssueText = CLanguage.Translate("TR_REPORTER_GIST_AND_ISSUE_TEXT"); + private readonly string _GistOnlyText = CLanguage.Translate("TR_REPORTER_GIST_ONLY_TEXT"); + private readonly string _SubmitStep0Text = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP0_TEXT"); + private readonly string _SubmitStep1Text = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP1_TEXT"); + private readonly string _SubmitStep2ExitText = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP2_EXIT_TEXT"); + private readonly string _SubmitStep2ContinueText = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP2_CONTINUE_TEXT"); + private readonly string _SubmitedMessageText = CLanguage.Translate("TR_REPORTER_SUBMITED_MESSAGE_TEXT"); + private readonly string _LogUploadErrorText = CLanguage.Translate("TR_REPORTER_LOGUPLOAD_ERROR_TEXT"); + private readonly string _LastErrorTitleText = CLanguage.Translate("TR_REPORTER_LAST_ERROR_TITLE_TEXT"); + private readonly string _LastErrorNa = CLanguage.Translate("TR_REPORTER_LAST_ERROR_NA"); + private readonly string _IssueTemplate = CLanguage.Translate("TR_REPORTER_ISSUE_TEMPLATE"); - - private string _IssueTemplate = "Describe your issue here.\n\n### Steps to reproduce\nTell us how to reproduce this issue.\n\n### Vocaluxe version and logfile\n{0}\n{1}"; + //CLanguage.Translate("TR_DEBUG_AUDIO_STREAMS") private int _Step = 0; private readonly bool _Crash; @@ -112,7 +113,7 @@ namespace Vocaluxe.Reporting this.SubmitButton.Text = _SubmitStep0Text; this.SubmitedTitleLabel.Text = _SubmitedMessageText; this.LastErrorTitleLabel.Text = _LastErrorTitleText; - this.LastErrorBox.Text = string.IsNullOrWhiteSpace(lastError) ? _LastErrorNA : lastError; + this.LastErrorBox.Text = string.IsNullOrWhiteSpace(lastError) ? _LastErrorNa : lastError; this.LogBox.Text = log.Replace("\n","\r\n"); this.LogBox.SelectionStart = 0; } From 63321942a9407802a8840713486d6848b5e4b57b Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Sun, 28 Jan 2018 15:23:52 +0100 Subject: [PATCH 27/28] Workaround for the xml special char issue --- Output/Languages/English.xml | 2 +- Vocaluxe/Reporting/Reporter.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Output/Languages/English.xml b/Output/Languages/English.xml index 948080a0..63a4bad3 100644 --- a/Output/Languages/English.xml +++ b/Output/Languages/English.xml @@ -245,7 +245,7 @@ We are sorry that Vocaluxe run into an error and crashed.\r\nTo help us fix the problem please send us a report.\r\nEdit the following log before submitting to remove possibly sensitive information. that you experienced an error.\r\nTo help us fix the problem please send us a report.\r\nEdit the following log before submitting to remove possibly sensitive information. - Don\'t upload anything (you can still copy the error message above) + Do not upload anything (you can still copy the error message above) Upload and open an issue (publicly visible + requires a github account) Upload and get a link to your report (publicly visible) Submit diff --git a/Vocaluxe/Reporting/Reporter.cs b/Vocaluxe/Reporting/Reporter.cs index e6297e3a..51f6d297 100644 --- a/Vocaluxe/Reporting/Reporter.cs +++ b/Vocaluxe/Reporting/Reporter.cs @@ -31,8 +31,8 @@ namespace Vocaluxe.Reporting public partial class CReporter : Form { private readonly string _TitleText = CLanguage.Translate("TR_REPORTER_TITLE_TEXT"); - private readonly string _MessageCrashText = CLanguage.Translate("TR_REPORTER_MESSAGE_CRASH_TEXT"); - private readonly string _MessageNoCrashText = CLanguage.Translate("TR_REPORTER_MESSAGE_NO_CRASH_TEXT"); + private readonly string _MessageCrashText = CLanguage.Translate("TR_REPORTER_MESSAGE_CRASH_TEXT").Replace("\\n", "\n").Replace("\\r", "\r"); + private readonly string _MessageNoCrashText = CLanguage.Translate("TR_REPORTER_MESSAGE_NO_CRASH_TEXT").Replace("\\n", "\n").Replace("\\r", "\r"); private readonly string _NoUploadText = CLanguage.Translate("TR_REPORTER_NO_UPLOAD_TEXT"); private readonly string _GistAndIssueText = CLanguage.Translate("TR_REPORTER_GIST_AND_ISSUE_TEXT"); private readonly string _GistOnlyText = CLanguage.Translate("TR_REPORTER_GIST_ONLY_TEXT"); @@ -44,7 +44,7 @@ namespace Vocaluxe.Reporting private readonly string _LogUploadErrorText = CLanguage.Translate("TR_REPORTER_LOGUPLOAD_ERROR_TEXT"); private readonly string _LastErrorTitleText = CLanguage.Translate("TR_REPORTER_LAST_ERROR_TITLE_TEXT"); private readonly string _LastErrorNa = CLanguage.Translate("TR_REPORTER_LAST_ERROR_NA"); - private readonly string _IssueTemplate = CLanguage.Translate("TR_REPORTER_ISSUE_TEMPLATE"); + private readonly string _IssueTemplate = CLanguage.Translate("TR_REPORTER_ISSUE_TEMPLATE").Replace("\\n", "\n").Replace("\\r", "\r"); //CLanguage.Translate("TR_DEBUG_AUDIO_STREAMS") From f85e46e67271b023c59811fa2c91378c4d760565 Mon Sep 17 00:00:00 2001 From: lukeIam <2lukeiam@gmail.com> Date: Sun, 28 Jan 2018 15:34:57 +0100 Subject: [PATCH 28/28] Fix: Log assistant crashes if localizations are not loaded --- Vocaluxe/Reporting/Reporter.cs | 61 ++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/Vocaluxe/Reporting/Reporter.cs b/Vocaluxe/Reporting/Reporter.cs index 51f6d297..e713df7f 100644 --- a/Vocaluxe/Reporting/Reporter.cs +++ b/Vocaluxe/Reporting/Reporter.cs @@ -30,23 +30,24 @@ namespace Vocaluxe.Reporting { public partial class CReporter : Form { - private readonly string _TitleText = CLanguage.Translate("TR_REPORTER_TITLE_TEXT"); - private readonly string _MessageCrashText = CLanguage.Translate("TR_REPORTER_MESSAGE_CRASH_TEXT").Replace("\\n", "\n").Replace("\\r", "\r"); - private readonly string _MessageNoCrashText = CLanguage.Translate("TR_REPORTER_MESSAGE_NO_CRASH_TEXT").Replace("\\n", "\n").Replace("\\r", "\r"); - private readonly string _NoUploadText = CLanguage.Translate("TR_REPORTER_NO_UPLOAD_TEXT"); - private readonly string _GistAndIssueText = CLanguage.Translate("TR_REPORTER_GIST_AND_ISSUE_TEXT"); - private readonly string _GistOnlyText = CLanguage.Translate("TR_REPORTER_GIST_ONLY_TEXT"); - private readonly string _SubmitStep0Text = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP0_TEXT"); - private readonly string _SubmitStep1Text = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP1_TEXT"); - private readonly string _SubmitStep2ExitText = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP2_EXIT_TEXT"); - private readonly string _SubmitStep2ContinueText = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP2_CONTINUE_TEXT"); - private readonly string _SubmitedMessageText = CLanguage.Translate("TR_REPORTER_SUBMITED_MESSAGE_TEXT"); - private readonly string _LogUploadErrorText = CLanguage.Translate("TR_REPORTER_LOGUPLOAD_ERROR_TEXT"); - private readonly string _LastErrorTitleText = CLanguage.Translate("TR_REPORTER_LAST_ERROR_TITLE_TEXT"); - private readonly string _LastErrorNa = CLanguage.Translate("TR_REPORTER_LAST_ERROR_NA"); - private readonly string _IssueTemplate = CLanguage.Translate("TR_REPORTER_ISSUE_TEMPLATE").Replace("\\n", "\n").Replace("\\r", "\r"); + // Init string with defaults for the case that loading localizations caused the problem + private string _TitleText = "We are sorry"; + private string _MessageCrashText = "that Vocaluxe run into an error and crashed.\r\nTo help us fix the problem please send us a report.\r\nEdit the following log before submitting to remove possibly sensitive information."; + private string _MessageNoCrashText = "that you experienced an error.\r\nTo help us fix the problem please send us a report.\r\nEdit the following log before submitting to remove possibly sensitive information."; + private string _NoUploadText = "Don\'t upload anything (you can still copy the error message above)"; + private string _GistAndIssueText = "Upload and open an issue (publicly visible + requires a github account)"; + private string _GistOnlyText = "Upload and get a link to your report (publicly visible)"; + private string _SubmitStep0Text = "Submit"; + private string _SubmitStep1Text = "Uploading"; + private string _SubmitStep2ExitText = "Exit"; + private string _SubmitStep2ContinueText = "Continue"; + private string _SubmitedMessageText = "The link to your report:"; + private string _LogUploadErrorText = "Error uploading the log."; + private string _LastErrorTitleText = "Last error"; + private string _LastErrorNa = "Not available"; + private string _IssueTemplate = "Describe your issue here.\n\n### Steps to reproduce\nTell us how to reproduce this issue.\n\n### Vocaluxe version and logfile\n{0}\n{1}"; + - //CLanguage.Translate("TR_DEBUG_AUDIO_STREAMS") private int _Step = 0; private readonly bool _Crash; @@ -55,8 +56,8 @@ namespace Vocaluxe.Reporting private static readonly HttpClient _Client = new HttpClient(); private static readonly Regex _GetGistUrlRegex = new Regex("\"html_url\": *\"([^\"]+)\""); - + public static ShowReporterDelegate ShowReporterFunc { get { return _ShowReporter; } @@ -102,6 +103,32 @@ namespace Vocaluxe.Reporting /// The last error message. private void _Init(string log, string lastError) { + // Try to load localized strings + try + { + _TitleText = CLanguage.Translate("TR_REPORTER_TITLE_TEXT"); + _MessageCrashText = CLanguage.Translate("TR_REPORTER_MESSAGE_CRASH_TEXT").Replace("\\n", "\n").Replace("\\r", "\r"); + _MessageNoCrashText = CLanguage.Translate("TR_REPORTER_MESSAGE_NO_CRASH_TEXT").Replace("\\n", "\n").Replace("\\r", "\r"); + _NoUploadText = CLanguage.Translate("TR_REPORTER_NO_UPLOAD_TEXT"); + _GistAndIssueText = CLanguage.Translate("TR_REPORTER_GIST_AND_ISSUE_TEXT"); + _GistOnlyText = CLanguage.Translate("TR_REPORTER_GIST_ONLY_TEXT"); + _SubmitStep0Text = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP0_TEXT"); + _SubmitStep1Text = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP1_TEXT"); + _SubmitStep2ExitText = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP2_EXIT_TEXT"); + _SubmitStep2ContinueText = CLanguage.Translate("TR_REPORTER_SUBMIT_STEP2_CONTINUE_TEXT"); + _SubmitedMessageText = CLanguage.Translate("TR_REPORTER_SUBMITED_MESSAGE_TEXT"); + _LogUploadErrorText = CLanguage.Translate("TR_REPORTER_LOGUPLOAD_ERROR_TEXT"); + _LastErrorTitleText = CLanguage.Translate("TR_REPORTER_LAST_ERROR_TITLE_TEXT"); + _LastErrorNa = CLanguage.Translate("TR_REPORTER_LAST_ERROR_NA"); + _IssueTemplate = CLanguage.Translate("TR_REPORTER_ISSUE_TEMPLATE").Replace("\\n", "\n").Replace("\\r", "\r"); + } + catch (Exception e) + { +#if DEBUG + Console.WriteLine($"Unable to load translation for report assistant: {e.Message}"); +#endif + } + _Client.DefaultRequestHeaders.Add("accept", "application/json"); _Client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)");