using System; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Threading; using JD.Core.ProjectManaging; using JD.LogService; using Packager.Tasks; using TeaBox; using Directory = System.IO.Directory; using File = System.IO.File; using LogCategory = JD.LogService.LogCategory; namespace Packager { /// /// Message logging mechanism for the tool /// public class MessageLogger : ILogger { public delegate void MessageEvent(MessageLogger sender, string message); private readonly object _lock = new object(); public string LogFilePath { get; } public string LoggedObjectName { get; } public MessageLogger(string loggedObjectName) { LoggedObjectName = loggedObjectName; var logFileDirectory = Path.Combine(DataPath.Value, "output", "temp", nameof(Packager)); if (!Directory.Exists(logFileDirectory)) { Directory.CreateDirectory(logFileDirectory); } Thread.Sleep(1); // avoid colliding file names var logFileName = $"{DateTime.Now.ToString("yyyyMMdd_HHmmss_fff", CultureInfo.InvariantCulture)}" + $"_pid_{Process.GetCurrentProcess().Id}_{loggedObjectName}.txt"; LogFilePath = Path.Combine(logFileDirectory, logFileName); LogInfo("Log file created."); // delete log files older than 48 hours var existingLogFiles = Directory.EnumerateFiles(logFileDirectory, "*.txt", SearchOption.TopDirectoryOnly); var logsToDelete = existingLogFiles.Select(f => new System.IO.FileInfo(f)) .Where(fi => fi.LastAccessTime < DateTime.Now.AddHours(-48)); foreach (var logPath in logsToDelete.Select(f => f.FullName)) { LogInfo($"Deleting old log file at \"{logPath}\""); File.Delete(logPath); } } public void Log(LogEvent @event) { switch (@event.Category) { case LogCategory.Message: LogInfo(@event.Message); break; case LogCategory.Error: LogError(@event.Message); break; case LogCategory.Warning: LogWarning(@event.Message); break; default: var ex = new ArgumentOutOfRangeException(typeof(LogCategory).Name, @event.Category, "Value of log category was unhandled"); LogException(ex); throw ex; } } public event MessageEvent OnInfoLogged; public event MessageEvent OnWarningLogged; public event MessageEvent OnErrorLogged; /// /// Makes the current MessageLogger able to catches log events from the given cookerWrapper. /// public void AttachCookerWrapper(cookerWrapper cookerWrapper) { cookerWrapper.onLogCook += OnCookerWrapperLog; cookerWrapper.onProgress += OnCookerWrapperProgress; cookerWrapper.onErrorCook += OnCookerWrapperCookingError; cookerWrapper.onLogError += OnCookerWrapperError; cookerWrapper.onLogFatal += OnCookerWrapperFatalError; } /// /// Stop catching log events from the given cookerWrapper. /// public void DetachCookerWrapper(cookerWrapper cookerWrapper) { cookerWrapper.onLogCook += OnCookerWrapperLog; cookerWrapper.onProgress += OnCookerWrapperProgress; cookerWrapper.onErrorCook += OnCookerWrapperCookingError; cookerWrapper.onLogError += OnCookerWrapperError; cookerWrapper.onLogFatal += OnCookerWrapperFatalError; } private void OnCookerWrapperFatalError(string progress) { this.LogError("Cooker wrapper encoutered a FATAL ERROR: " + progress); } private void OnCookerWrapperError(string progress) { this.LogError("Cooker wrapper encoutered an error: " + progress); } private void OnCookerWrapperCookingError(string text, bool errordescribed) { this.LogError("Cooker wrapper encoutered an error while cooking: " + text); } private void OnCookerWrapperProgress(float progress) { this.LogInfo(progress.ToString(CultureInfo.InvariantCulture)); } private void OnCookerWrapperLog(string progress) { this.LogInfo(progress); } public void LogInfo(string message) { var text = $"\n{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff", CultureInfo.InvariantCulture)} INFO: {message}"; Console.WriteLine(text); WriteToLogFile(text); OnInfoLogged?.Invoke(this, message); } public void LogWarning(string message) { var text = $"\n{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff", CultureInfo.InvariantCulture)} WARNING: {message}"; Console.WriteLine(text); WriteToLogFile(text); OnWarningLogged?.Invoke(this, message); } public void LogError(string message) { var text = $"\n{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff", CultureInfo.InvariantCulture)} ERROR: {message}"; Console.WriteLine(text); WriteToLogFile(text); OnErrorLogged?.Invoke(this, message); } public void LogException(Exception exception) { var text = $"\n{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff", CultureInfo.InvariantCulture)} EXCEPTION: {exception.Message}"; text += "\n" + exception.StackTrace; Console.Error.WriteLine(text); WriteToLogFile(text); } public void LogTaskMessage(Task task, string message, LogCategory messageCategory) { switch (messageCategory) { case LogCategory.Message: LogInfo($"[{task.InstanceName}] {message}"); break; case LogCategory.Error: LogError($"[{task.InstanceName}] {message}"); break; case LogCategory.Warning: LogWarning($"[{task.InstanceName}] {message}"); break; default: var ex = new ArgumentOutOfRangeException(typeof(LogCategory).Name, messageCategory, "Value of log category was unhandled"); LogError(ex.Message); throw ex; } } public void LogEndedTask(Task task) { var time = task.ElapsedTime; var timeString = (time.Minutes > 0 ? $"{time.Minutes} minute{(time.Minutes > 1 ? "s" : "")} " : "") + $"{time.Seconds} second{(time.Seconds > 1 ? "s" : "")}"; string endTaskMessage = $"[COMPLETED] (took {timeString})"; LogTaskMessage(task, endTaskMessage, LogCategory.Message); } public void LogStartedTask(Task task) { var startTaskMessage = $"[STARTED]"; LogTaskMessage(task, startTaskMessage, LogCategory.Message); } private void WriteToLogFile(string text) { lock (_lock) { File.AppendAllText(LogFilePath, text); } } } }