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; } } /// <summary> /// Configure logging to write to default log and the Unity console output. /// </summary> 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(); } } /// <summary> An appender which logs to the unity console. </summary> private class UnityAppender : AppenderSkeleton { /// <inheritdoc /> 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 LEFT_PAREN = "("; private static string RIGHT_PAREN = ")"; 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)); } /// <summary> /// Create a string describing an enumeration /// </summary> /// <param name="strings">string collection</param> /// <returns>"flattened" string enumeration</returns> public static string StringEnumerationToString(IEnumerable<string> strings) { StringBuilder sb = new StringBuilder(); sb.Append(LEFT_PAREN); bool first = true; foreach (string s in strings) { if (!first) sb.Append(ARRAY_DELIMITER); sb.Append(s); first = false; } sb.Append(RIGHT_PAREN); return sb.ToString(); } public static string AssetStatusListToString(List<AssetStatus> a) { if (a == null) return NULL_STRING; StringBuilder sb = new StringBuilder(); sb.Append(LEFT_BRACE); bool first = true; foreach (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<Engine.FilesAndOp> 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<FileSpec> 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<FileAndMeta> 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(); } /// <summary> /// Debug function to create a compact string description of a FileMetaData element /// </summary> /// <param name="m"></param> /// <returns></returns> 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(); } /// <summary> /// Debug function to create a compact string description of a FileMetaData List /// </summary> /// <param name="a"></param> /// <returns></returns> public static string FileMetaDataListToString(IList<FileMetaData> 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(); } /// <summary> /// Debug function to create a compact string description of a ReconcileEntry /// </summary> /// <param name="e">Entry to print</param> /// <returns>string description</returns> public static string ReconcileEntryToString(ReconcileEntry e) { if (e == null) return NULL_STRING; StringBuilder sb = new StringBuilder(); sb.AppendFormat("Action: {0} ", e.Action.ToStringNullSafe()); sb.AppendFormat("Type: {0} ", e.Type.ToStringNullSafe()); sb.AppendFormat("Depot: {0} Client: {1} Local: {2} ", ToStringNullSafe(e.DepotPath), ToStringNullSafe(e.ClientPath), ToStringNullSafe(e.LocalPath)); return sb.ToString(); } /// <summary> /// Debug function to create a compact string description of a ReconcileEntry List /// </summary> /// <param name="a"></param> /// <returns>big string</returns> public static string ReconcileEntryListToString(IList<ReconcileEntry> a) { if (a == null) return NULL_STRING; StringBuilder sb = new StringBuilder(); sb.Append(LEFT_BRACE); bool first = true; foreach (Perforce.P4.ReconcileEntry e in a) { if (!first) sb.Append(ARRAY_DELIMITER); sb.Append(ReconcileEntryToString(e)); first = false; } sb.Append(RIGHT_BRACE); return sb.ToString(); } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 21095 | artofcode |
Populate -o //guest/perforce_software/p4connect/... //guest/artofcode/dev/p4connect/.... |
||
//guest/perforce_software/p4connect/main/src/P4Connect/P4Connect/P4Connect.Logger.cs | |||||
#3 | 20275 | Norman Morse |
Update source to match 2016.2 patch 2 release Much thrashing of source during refactor. Proper caching of asset statuses, reducing fstats issued. Some bug fixes by Liz Lam Cleanup of Pending changes, rework of initialization and play/stop play transitions |
||
#2 | 18942 | Norman Morse | 2016.1 Patch 2 changes | ||
#1 | 16209 | Norman Morse | Move entire source tree into "main" branch so workshop code will act correctly. | ||
//guest/perforce_software/p4connect/src/P4Connect/P4Connect/P4Connect.Logger.cs | |||||
#10 | 15298 | Norman Morse | Fix Version Stamping to not use UpdateVersion.exe for personal (workshop) builds. | ||
#9 | 15266 | Norman Morse |
Integrated "UpdateVersion" tool to update the VersionInfo and the DLL properties with information from "Version" EC generates the Version file for us in builds. Workshop users need to generate their own Release number with two zeros (like 2015.2.0.0) which will have the last two numbers replaced with change ID. |
||
#8 | 15079 | Norman Morse |
Rewrote AssetStatusCache to Cache AssetStatuses and FileMetaData Fixed Edge conditions on Engine Operations Change Debug output defaults. Will now Checkout files which request to be "added" but which already exist in perforce. Output P4Connect version to log on initialization. |
||
#7 | 14232 | Norman Morse | GA.8 release | ||
#6 | 14193 | Norman Morse |
GA.7 release Refactor Pending Changes Resolve Submit issues. Fixed Menu entries. Handle mismatched file and meta states. |
||
#5 | 13864 | Norman Morse | Final fixes for GA.5 release. | ||
#4 | 13824 | Norman Morse |
Changes to have fstat return true client paths. Remove versions, fixes problems with "local" paths sneaking into results. |
||
#3 | 12553 | Norman Morse |
integrate from internal main Build fixes for EC. Major changes to Configuration and re-initialization code. Bug fixes |
||
#2 | 12512 | Norman Morse | Integrate from Dev branch, preparing for Beta3 release | ||
#1 | 12362 | Norman Morse |
Added Debug Logging for p4log Fixed some path comparison issues. Created a CaseSensitivity test |