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; using UnityEditor; using UnityEngine; namespace P4Connect { public static class Logger { static Log4netTraceListener p4Listener = null; public static void Initialize() { if (!Config.EnableLog) { Disable(); return; } if (p4Listener != null) return; 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 Init: P4Connect " + Version.PerforceReleaseString + "-" + Version.Build); // Add the new console trace listener to the collection of trace listeners. Trace.Listeners.Add(p4Listener); // Hook the Bridge logging calls up using the .NetLog delegate Perforce.P4.LogFile.SetLoggingFunction(NetLog); // pass along exceptions with level greater than MinThrowLevel Perforce.P4.P4Exception.MinThrowLevel = Perforce.P4.ErrorSeverity.E_EMPTY; // how much noise p4api.net makes 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 = Path.Combine(Application.dataPath, "P4Connect\\Editor\\logging.xml").Replace('/', System.IO.Path.DirectorySeparatorChar); ; //UnityEngine.Debug.Log("Checking for logging config at " + ConfigPath); if (System.IO.File.Exists(ConfigPath)) { UnityEngine.Debug.Log("Found 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(); } } /// 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 NetLog(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 ToStringNullSafe(this object value) { return(value ?? "null").ToString(); } 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 AssetStatusListToString(List a) { if (a == null) return NULL_STRING; StringBuilder sb = new StringBuilder(); sb.Append(LEFT_BRACE); bool first = true; foreach (AssetStatusCache.AssetStatus stat in a) { if (!first) sb.Append(ARRAY_DELIMITER); sb.Append(stat.ToString()); first = false; } sb.Append(RIGHT_BRACE); return sb.ToString(); } public static string FileAndOpToString(Engine.FilesAndOp fao) { return string.Format("op: {0} file: {1} meta: {2} move: {3}", fao.FileOp, fao.File, fao.Meta, fao.MoveToFile); } 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.Append(FileAndOpToString(fao)); 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); if (spec == null) sb.AppendFormat(NULL_STRING); else sb.AppendFormat("Depot: {0} Client: {1} Local: {2} Version: {3}", spec.DepotPath.ToStringNullSafe(), spec.ClientPath.ToStringNullSafe(), spec.LocalPath.ToStringNullSafe(), spec.Version.ToStringNullSafe()); 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(); } /// /// Debug function to create a compact string description of a FileMetaData element /// /// /// public static string FileMetaDataToString(FileMetaData m) { if (m == null) return NULL_STRING; StringBuilder sb = new StringBuilder(); sb.AppendFormat("Action: {0} ", m.Action.ToStringNullSafe()); sb.AppendFormat("Mapped: {0} ", m.IsMapped); sb.AppendFormat("Shelved: {0} ", m.Shelved); sb.AppendFormat("HeadAction: {0} ", m.HeadAction.ToStringNullSafe()); sb.AppendFormat("HeadChange: {0} ", m.HeadChange.ToStringNullSafe()); sb.AppendFormat("Storage: {0} ", m.Type.ToStringNullSafe()); sb.AppendFormat("Depot: {0} Client: {1} Local: {2} ", ToStringNullSafe(m.DepotPath), ToStringNullSafe(m.ClientPath), ToStringNullSafe(m.LocalPath)); return sb.ToString(); } /// /// Debug function to create a compact string description of a FileMetaData List /// /// /// public static string FileMetaDataListToString(IList a) { if (a == null) return NULL_STRING; StringBuilder sb = new StringBuilder(); sb.Append(LEFT_BRACE); bool first = true; foreach (Perforce.P4.FileMetaData m in a) { if (!first) sb.Append(ARRAY_DELIMITER); sb.Append(FileMetaDataToString(m)); first = false; } sb.Append(RIGHT_BRACE); return sb.ToString(); } } }