#region Disclaimer / License // Copyright (C) 2011, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Linq; using System.Text; namespace Duplicati.Library.Logging { /// /// Writes log messages to a file, file is opened, appended and closed each write /// public class AppendLog : ILog, IDisposable { // Using a colon followed by a ETX will make this easier to parse, yet still readable public static string Separator = ":" + (char)3; // Use the round-trip date formatter public const string DateFormat = "o"; // The name of the file public string LogFileName { get; set; } // Just a little extra to put in the messages private string itsNote = string.Empty; /// /// Constructs a new log destination, writing to the Log database /// /// The file to write to public AppendLog(string aFileName, string aNote) { LogFileName = aFileName; itsNote = aNote; } /// /// Returns a DateTime given a log date or DateTime.MaxValue if can not parse /// /// Log date /// DateTime or DateTime.MaxValue if can not parse public static DateTime ParseLogDate(string aLogDate) { DateTime Result = DateTime.MaxValue; DateTime.TryParse(aLogDate, null, System.Globalization.DateTimeStyles.RoundtripKind, out Result); return Result; } /// /// Represents a log entry as a class /// public class LogEntry { public DateTime Date { get; set; } public string Job { get; set; } public Duplicati.Library.Logging.LogMessageType Type { get; set; } public string Message { get; set; } public string ExMessage { get; set; } public LogEntry(string aLine) { string[] Parts = aLine.Split(new string[] { Duplicati.Library.Logging.AppendLog.Separator }, StringSplitOptions.None); if (Parts.Length < 5) return; Date = Duplicati.Library.Logging.AppendLog.ParseLogDate(Parts[0]); Job = Parts[1]; Type = (Duplicati.Library.Logging.LogMessageType)System.Enum.Parse(typeof(Duplicati.Library.Logging.LogMessageType), Parts[2]); Message = Parts[3]; ExMessage = Parts[4]; } } /// /// Convert a log file into a XML /// /// Log file name /// XML text public static string LogFileToXML(string aLogFile) { var Parser = from string Line in System.IO.File.ReadAllLines(aLogFile) select new LogEntry(Line); System.Xml.Linq.XElement XML = new System.Xml.Linq.XElement("Log", from LogEntry qL in Parser select new System.Xml.Linq.XElement("Log", new System.Xml.Linq.XAttribute("File", System.IO.Path.GetFileName(aLogFile)), new System.Xml.Linq.XElement("Date", qL.Date), new System.Xml.Linq.XElement("Job", qL.Job), new System.Xml.Linq.XElement("Type", qL.Type), new System.Xml.Linq.XElement("Message", qL.Message), new System.Xml.Linq.XElement("ExMessage", qL.ExMessage) )); return XML.ToString(); } /// /// A bindable list of log entries /// public class LogList : System.ComponentModel.BindingList { public LogList(string[] aContents) : base((from string qS in aContents select new Duplicati.Library.Logging.AppendLog.LogEntry(qS)).ToArray()) { } } #region ILog Members /// /// The function called when a message is logged /// /// The message logged /// The type of message logged /// An exception, may be null public virtual void WriteMessage(string aMessage, Duplicati.Library.Logging.LogMessageType aType, Exception aException) { // Never, I don't care what if (aType == LogMessageType.Profiling) return; if (aMessage.Contains("is partial from byte offset")) return; // Silly message confuses users // Multiple messages may be sent separated by newline foreach(string Line in aMessage.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)) { string Message = string.Join(Separator, new string[] { DateTime.Now.ToString(DateFormat), itsNote, aType.ToString(), Line, aException == null ? string.Empty : aException.ToString() }); bool OK = false; try { System.IO.File.AppendAllText(LogFileName, Message + "\r\n"); // Just for notepad... OK = true; } catch (Exception Ex) { Console.WriteLine(LogFileName+":"+Ex.ToString()); } if (!OK) Console.WriteLine(Message); } if (EventLogged != null) EventLogged(aMessage, aType, aException); } /// /// An event that is raised when a message is logged /// public event EventLoggedDelgate EventLogged; #endregion #region IDisposable Members /// /// Frees up all internally held resources /// public void Dispose() { } #endregion } }