using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using log4net; using log4net.Config; using log4net.Layout; using log4net.Appender; using log4net.Core; using log4net.ObjectRenderer; using log4net.Util; using Perforce.P4; namespace P4Connect { public static class Logger { static Log4netTraceListener p4Listener = null; public static void Initialize() { try { UnityEngine.Debug.Log("Initialize Logger"); ConfigureAllLogging(); p4Listener = new Log4netTraceListener(); // Write the initial trace message to the console trace listener. p4Listener.WriteLine(DateTime.Now.ToString() + " - Logger Initialized."); // Add the new console trace listener to the collection of trace listeners. Trace.Listeners.Add(p4Listener); // Hook the Bridge logging calls up using the P4ConnectLog delegate Perforce.P4.LogFile.SetLoggingFunction(P4ConnectLog); //Trace.WriteLine("Trace.WriteLine"); //Debug.WriteLine("Debug.WriteLine"); Trace.Flush(); } catch (Exception e) { UnityEngine.Debug.Log("Exception in Logger.Initialize: " + e.Message); UnityEngine.Debug.Log(" src: " + e.Source); UnityEngine.Debug.Log(" stack: " + e.StackTrace); Disable(); } } public static void Enable() { Initialize(); } public static void Disable() { if (p4Listener != null) { p4Listener.Dispose(); p4Listener = null; } } /// /// Configure logging to write to default log and the Unity console output. /// public static void ConfigureAllLogging() { var patternLayout = new PatternLayout { ConversionPattern = "%date %-5level %logger.%method - %message%newline" }; patternLayout.ActivateOptions(); // setup the appender that writes to the logfile var fileAppender = new log4net.Appender.RollingFileAppender { AppendToFile = true, File = Config.LogPath, Layout = patternLayout, MaxSizeRollBackups = 5, MaximumFileSize = "1GB", RollingStyle = RollingFileAppender.RollingMode.Size, StaticLogFileName = true }; fileAppender.ActivateOptions(); var unityLogger = new UnityAppender { Layout = patternLayout }; unityLogger.ActivateOptions(); // See if logging.xml is available as an asset string ConfigPath = Main.DataPath + "\\P4Connect\\Editor\\logging.xml"; UnityEngine.Debug.Log("Looking for logging config at " + ConfigPath); if (System.IO.File.Exists(ConfigPath)) { UnityEngine.Debug.Log("Reading log4net configuration asset"); using (System.IO.Stream fs = System.IO.File.OpenRead(ConfigPath)) { log4net.Config.XmlConfigurator.Configure(fs); // Customized configuration from asset } } else { UnityEngine.Debug.Log("Using log4net defaults"); BasicConfigurator.Configure(unityLogger, fileAppender); // "Wired-in" default configuration ILog log = LogManager.GetLogger(typeof(Logger)); var repo = LogManager.GetRepository(); // Add custom renderers repo.RendererMap.Put(typeof(FileSpec), new FileSpecRenderer()); repo.RendererMap.Put(typeof(Engine.FilesAndOp), new FileAndOpRenderer()); repo.RendererMap.Put(typeof(System.Collections.IList), new FileSpecListRenderer()); repo.RendererMap.Put(typeof(System.Collections.IList), new FilesAndOpListRenderer()); repo.RendererMap.Put(typeof(System.Collections.IList), new FileAndMetaListRenderer()); } } /// An appender which logs to the unity console. private class UnityAppender : AppenderSkeleton { /// protected override void Append(log4net.Core.LoggingEvent loggingEvent) { string message = RenderLoggingEvent(loggingEvent); LogLevel l = GetLogLevelFromLog4NetLevel(loggingEvent.Level); if (l <= Config.ConsoleLogLevel) // Filter Log output to console { // Write to the console if (Level.Compare(loggingEvent.Level, Level.Error) >= 0) { // everything above or equal to error is an error UnityEngine.Debug.LogError(message); } else if (Level.Compare(loggingEvent.Level, Level.Warn) >= 0) { // everything that is a warning up to error is logged as warning UnityEngine.Debug.LogWarning(message); } else { // everything else we'll just log normally UnityEngine.Debug.Log(message); } } } } public class Log4netTraceListener : System.Diagnostics.TraceListener { private readonly log4net.ILog _log; public Log4netTraceListener() { _log = log4net.LogManager.GetLogger("trace"); } public Log4netTraceListener(log4net.ILog log) { _log = log; } public override void Write(string message) { if (_log != null) { _log.Debug(message); } } public override void WriteLine(string message) { if (_log != null) { _log.Debug(message); } } } /// Log_Level. From P4API The lower the level, the more severe the issue /// 0 fatal /// 1 error /// 2 warning /// 3 information /// 4+ debugging messages /// public enum LogLevel { Fatal, Error, Warn, Info, Debug }; // Translate a Log4Net Level enum into a Logger.LogLevel enum public static LogLevel GetLogLevelFromLog4NetLevel(Level l) { if (l >= Level.Debug) return LogLevel.Debug; if (l >= Level.Info) return LogLevel.Info; if (l >= Level.Warn) return LogLevel.Warn; if (l >= Level.Error) return LogLevel.Error; return LogLevel.Fatal; } public static void P4ConnectLog(int log_level, String source, String message) { log4net.ILog _log = log4net.LogManager.GetLogger(source); LogLevel level = (LogLevel)log_level; switch (level) { case LogLevel.Fatal: _log.Fatal(message); break; case LogLevel.Error: _log.Error(message); break; case LogLevel.Warn: _log.Warn(message); break; case LogLevel.Info: _log.Info(message); break; case LogLevel.Debug: _log.Debug(message); break; } } private static string ARRAY_DELIMITER = ", "; private static string ARRAY_BRACES = "[{0}]"; private static string LEFT_BRACE = "["; private static string RIGHT_BRACE = "]"; private static string NULL_STRING = "null"; public static string StringArrayToString(string[] a) { if (a == null) return NULL_STRING; return string.Format(ARRAY_BRACES, string.Join(ARRAY_DELIMITER, a)); } public static string FilesAndOpListToString(List a) { if (a == null) return NULL_STRING; StringBuilder sb = new StringBuilder(); sb.Append(LEFT_BRACE); bool first = true; foreach(Engine.FilesAndOp fao in a) { if (!first) sb.Append(ARRAY_DELIMITER); sb.AppendFormat("op: {0} file: {1} meta: {2} move: {3}", fao.FileOp, fao.File, fao.Meta, fao.MoveToFile); first = false; } sb.Append(RIGHT_BRACE); return sb.ToString(); } public static string FileSpecListToString(IList a) { if (a == null) return NULL_STRING; StringBuilder sb = new StringBuilder(); sb.Append(LEFT_BRACE); bool first = true; foreach (Perforce.P4.FileSpec spec in a) { if (!first) sb.Append(ARRAY_DELIMITER); sb.AppendFormat("Depot: {0} Client: {1} Local: {2} Version: {3}", spec.DepotPath, spec.ClientPath, spec.LocalPath, spec.Version); first = false; } sb.Append(RIGHT_BRACE); return sb.ToString(); } public static string FileAndMetaListToString(List a) { if (a == null) return NULL_STRING; StringBuilder sb = new StringBuilder(); sb.Append(LEFT_BRACE); bool first = true; foreach (FileAndMeta fam in a) { if (!first) sb.Append(ARRAY_DELIMITER); sb.AppendFormat("file: {0} meta: {1}", fam.File, fam.Meta); first = false; } sb.Append(RIGHT_BRACE); return sb.ToString(); } public class FileSpecListRenderer : IObjectRenderer { public void RenderObject(RendererMap map, object obj, TextWriter writer) { var myList = obj as IList; if (myList != null) { try { FileSpecRenderer rend = new FileSpecRenderer(); foreach (FileSpec spec in myList) { rend.RenderObject(map, spec, writer); } } catch (NullReferenceException ex) { writer.Write(SystemInfo.NullText); } } else { new DefaultRenderer().RenderObject(map, obj, writer); } } } public class FileSpecRenderer : IObjectRenderer { public void RenderObject(RendererMap map, object obj, TextWriter writer) { FileSpec spec = obj as FileSpec; writer.WriteLine(string.Format("Depot: {0} Client: {1} Local: {2} Version: {3}", spec.DepotPath, spec.ClientPath, spec.LocalPath, spec.Version)); } } public class FilesAndOpListRenderer : IObjectRenderer { public void RenderObject(RendererMap map, object obj, TextWriter writer) { var myList = obj as IList; if (myList != null) { try { FileAndOpRenderer rend = new FileAndOpRenderer(); foreach (Engine.FilesAndOp spec in myList) { rend.RenderObject(map, spec, writer); } } catch (NullReferenceException ex) { writer.Write(SystemInfo.NullText); } } else { new DefaultRenderer().RenderObject(map, obj, writer); } } } public class FileAndOpRenderer : IObjectRenderer { public void RenderObject(RendererMap map, object obj, TextWriter writer) { Engine.FilesAndOp fao = (Engine.FilesAndOp) obj; writer.WriteLine(string.Format("op: {0} file: {1} meta: {2} move: {3}", fao.FileOp.ToString(), fao.File, fao.Meta, fao.MoveToFile)); } } public class FileAndMetaListRenderer : IObjectRenderer { public void RenderObject(RendererMap map, object obj, TextWriter writer) { var myList = obj as IList; if (myList != null) { try { FileAndMetaRenderer rend = new FileAndMetaRenderer(); foreach (FileAndMeta fam in myList) { rend.RenderObject(map, fam, writer); } } catch (NullReferenceException ex) { writer.Write(SystemInfo.NullText); } } else { new DefaultRenderer().RenderObject(map, obj, writer); } } } public class FileAndMetaRenderer : IObjectRenderer { public void RenderObject(RendererMap map, object obj, TextWriter writer) { FileAndMeta fam = (FileAndMeta) obj; writer.WriteLine(string.Format("file: {0} meta: {1}", fam.File, fam.Meta)); } } } }