public Log(string name, string prefix, params ILogOutput[] outputs) { _name = name; _prefix = prefix; _formattedPrefix = LogFormat.LogPrefix(prefix); _outputs = new List <ILogOutput>(outputs); }
public static bool TryFormat(object obj, out string formattedString) { if (obj is UnityEngine.Object) { formattedString = LogFormat.UnityObject(obj as UnityEngine.Object); } else if (obj is Exception) { formattedString = LogFormat.Exception(obj as Exception); } else if (obj is MemberInfo) { formattedString = LogFormat.MemberInfo(obj as MemberInfo); } else if (obj is Assembly) { formattedString = LogFormat.Assembly(obj as Assembly); } else if (obj is StackFrame) { formattedString = LogFormat.StackFrame(obj as StackFrame); } else { formattedString = null; return(false); } return(true); }
private static void AppDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args) { Logs.System.Write( "Assembly Loaded: {0} in AppDomain {1}", LogFormat.Assembly(args.LoadedAssembly), LogFormat.AppDomain(AppDomain.CurrentDomain)); }
/// <summary> /// Initializes a global logfile using the specified target directory and file name /// using default settings. If not specified otherwise, a Logs directory is used and /// the log file's name is derived from the current <see cref="DateTime"/>. /// </summary> public static TextWriterLogOutput InitGlobalLogFile(string directory = null, string fileName = null, TextLogOutputConfig config = null) { // In case someone calls this multiple times, shut down the old one ShutdownTextLog(); try { // Open a writable stream to the desired, default or fallback log file location string loggingPath; if (!TryCreateLogStream(directory, fileName, out _textFileLogWriter, out loggingPath)) { Logs.Default.WriteWarning("Text Logfile unavailable, because no logging location was accessible."); return(null); } config.LoggingPath = loggingPath; // Create, configure and register a log output using the log stream _textFileLogOutput = new TextWriterLogOutput(_textFileLogWriter, config); Logs.AddGlobalOutput(_textFileLogOutput); return(_textFileLogOutput); } catch (Exception e) { Logs.Default.WriteWarning("Failed to create text logfile: {0}", LogFormat.Exception(e)); return(null); } }
private static string FormatMessage(string format, object[] obj) { if (obj == null || obj.Length == 0) { return(format); } string msg; try { // Format unity objects, because their .ToString implementation isn't great // and if users do it manually, we lose the context object (because then it's a string). // // While we're at it, let's auto-transform some other objects with less-than-ideal // .ToString() implementation as well. object[] preFormatObj = new object[obj.Length]; for (int i = 0; i < preFormatObj.Length; i++) { string formattedObj; if (LogFormat.TryFormat(obj[i], out formattedObj)) { preFormatObj[i] = formattedObj; } else { preFormatObj[i] = obj[i]; } } // Format the actual message msg = string.Format(System.Globalization.CultureInfo.InvariantCulture, format, preFormatObj); } catch (Exception e) { // Don't allow log message formatting to throw unhandled exceptions, // because they would result in another log - and probably more exceptions. // Instead, embed format, arguments and the exception in the resulting // log message, so the user can retrieve all necessary information for // fixing his log call. msg = format + Environment.NewLine; if (obj != null) { try { msg += obj.ToString(", ") + Environment.NewLine; } catch (Exception) { msg += "(Error in ToString call)" + Environment.NewLine; } } msg += LogFormat.Exception(e); } return(msg); }
public virtual void Write(Log source, LogEntry entry, object context) { string[] lines = entry.Message.Split( new[] { '\n', '\r', '\0' }, StringSplitOptions.RemoveEmptyEntries); lock (_writerLock) { int totalPrefixLength = 0; StringBuilder builder = new StringBuilder(); for (int i = 0; i < lines.Length; i++) { builder.Length = 0; if (i == 0) { if (_config.WriteTimeStamps) { builder.Append(entry.TimeStamp.ToString(_config.TimeStampFormat)); builder.Append(' '); } if (_config.WriteFrameStamps) { builder.AppendFormat(_config.FrameStampFormat, entry.FrameStamp); builder.Append(' '); } if (source.FormattedPrefix != null) { builder.Append(source.FormattedPrefix); builder.Append(' '); } builder.Append(_config.UseStandardTypeFormat ? LogFormat.LogMessageTypeStandard(entry.Type) : LogFormat.LogMessageTypeShort(entry.Type)); builder.Append(": "); builder.Append(' ', _indent * 2); totalPrefixLength = builder.Length; } else { builder.Append(' ', totalPrefixLength); } builder.Append(lines[i]); lines[i] = builder.ToString(); WriteLine(entry, lines[i]); } } }
public static void WriteLoadedAssemblies(Log log) { try { log.Write( "Currently Loaded Assemblies:" + Environment.NewLine + "{0}", AppDomain.CurrentDomain.GetAssemblies() .ToString( assembly => string.Format(" {0}", LogFormat.Assembly(assembly)), Environment.NewLine)); } catch (Exception e) { log.WriteWarning("Error logging loaded assemblies: {0}", LogFormat.Exception(e)); } }
private static string RetrieveEditorContextInfo(object context) { // Do a stack trace in order to find one. Don't do this outside // the editor. It's too expensive and might not be supported on // some platforms // // We can skip two frames, since one is this one and the next // is definitely a Log method, since FindContext is private. System.Diagnostics.StackFrame stackFrame = null; try { System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(2); System.Diagnostics.StackFrame[] frames = trace.GetFrames(); for (int i = 0; i < frames.Length; i++) { System.Reflection.MethodBase method = frames[i].GetMethod(); Type type = method.DeclaringType; bool isLoggingType = !string.IsNullOrEmpty(type.Namespace) && type.Namespace.StartsWith(typeof(Log).Namespace); // Select the first stack frame that is not part of the // logging code, which is defined as everything in the // same namespace as the Log class if (!isLoggingType) { stackFrame = frames[i]; break; } } } catch (Exception) {} // Select what to display based on the kind of context provided if (stackFrame != null) { return(LogFormat.StackFrame(stackFrame)); } else if (context is UnityEngine.Object) { return(LogFormat.UnityObject(context as UnityEngine.Object)); } else { return(null); } }
static StaticLogHolder() { try { T initializer = new T(); Log = new Log( initializer.Name, initializer.Prefix, Logs.GlobalLogOutput.ToArray()); initializer.InitLog(Log); Logs._customGlobalLogs.Add(Log); } catch (Exception e) { Log = new Log(string.Empty, string.Empty); Logs.Default.WriteError( "Error initializing custom Log '{0}': {1}", LogFormat.Type(typeof(T)), LogFormat.Exception(e)); } }
public static void WriteMemoryUsage(Log log) { if (UnityEngine.Profiling.Profiler.supported) { log.Write("Memory Usage (Unity Profiler):" + Environment.NewLine + " Mono Heap Size: {0} MiB" + Environment.NewLine + " Mono Used Size: {1} MiB" + Environment.NewLine + " Total Allocated Memory: {2} MiB" + Environment.NewLine + " Total Reserved Memory: {3} MiB" + Environment.NewLine + " Unused Reserved Memory: {4} MiB" + Environment.NewLine + " Used Heap Size: {5} MiB", #if UNITY_5_6_OR_NEWER UnityEngine.Profiling.Profiler.GetMonoHeapSizeLong() / 1024L / 1024L, UnityEngine.Profiling.Profiler.GetMonoUsedSizeLong() / 1024L / 1024L, UnityEngine.Profiling.Profiler.GetTotalAllocatedMemoryLong() / 1024L / 1024L, UnityEngine.Profiling.Profiler.GetTotalReservedMemoryLong() / 1024L / 1024L, UnityEngine.Profiling.Profiler.GetTotalUnusedReservedMemoryLong() / 1024L / 1024L, UnityEngine.Profiling.Profiler.usedHeapSizeLong / 1024L / 1024L); #else UnityEngine.Profiling.Profiler.GetMonoHeapSize() / 1024L / 1024L, UnityEngine.Profiling.Profiler.GetMonoUsedSize() / 1024L / 1024L, UnityEngine.Profiling.Profiler.GetTotalAllocatedMemory() / 1024L / 1024L, UnityEngine.Profiling.Profiler.GetTotalReservedMemory() / 1024L / 1024L, UnityEngine.Profiling.Profiler.GetTotalUnusedReservedMemory() / 1024L / 1024L, UnityEngine.Profiling.Profiler.usedHeapSize / 1024L / 1024L); #endif } try { log.Write("Memory Usage (GC):" + Environment.NewLine + " Total Memory: {0} MiB" + Environment.NewLine + " Collections: {1} | {2} | {3}", GC.GetTotalMemory(false) / 1024L / 1024L, GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2)); } catch (Exception e) { log.WriteWarning("Error logging GC memory stats: {0}", LogFormat.Exception(e)); } }
public static void WriteEnvironmentVariables(Log log) { try { var machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine); var userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User); var processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process); log.Write( "Machine Variables:" + Environment.NewLine + "{0}", machineVars.Keys .OfType <string>() .Select(key => new KeyValuePair <string, object>(key, machineVars[key])) .ToString( pair => string.Format(" {0}: {1}", pair.Key, pair.Value), Environment.NewLine)); log.Write( "User Variables:" + Environment.NewLine + "{0}", userVars.Keys .OfType <string>() .Select(key => new KeyValuePair <string, object>(key, userVars[key])) .ToString( pair => string.Format(" {0}: {1}", pair.Key, pair.Value), Environment.NewLine)); log.Write( "Process Variables:" + Environment.NewLine + "{0}", processVars.Keys .OfType <string>() .Select(key => new KeyValuePair <string, object>(key, processVars[key])) .ToString( pair => string.Format(" {0}: {1}", pair.Key, pair.Value), Environment.NewLine)); } catch (Exception e) { log.WriteWarning("Error logging environment variables: {0}", LogFormat.Exception(e)); } }
static Logs() { AppDomain.CurrentDomain.ProcessExit += AppDomain_ProcessExit; AppDomain.CurrentDomain.DomainUnload += AppDomain_DomainUnload; AppDomain.CurrentDomain.AssemblyLoad += AppDomain_AssemblyLoad; // Normally, we'd use this hook to log all exceptions that end up uncaught, but // in testing, this never triggered. It is likely that Unity simply catches all // exceptions already, so this would be redundant with regular unity error log // forwarding, which is already in place. // AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException; _systemLog = new Log("System"); _defaultLog = new Log("Default"); _unityLog = new Log("Unity"); // Install a forwarder from Unity to our custom logs UnityLogIntegration.Init(); // Add a global log output that forwards to the regular Unity log try { UnityDebugLogOutput forwardToUnity = new UnityDebugLogOutput(); Logs.AddGlobalOutput(forwardToUnity); } catch (Exception e) { Logs.System.WriteWarning("Rerouting Logs to Unity Debug Logs failed: {0}", LogFormat.Exception(e)); } }
private static bool TryCreateLogStream(string path, out StreamWriter writer) { try { string directory = Path.GetDirectoryName(path); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } writer = new StreamWriter(path); writer.AutoFlush = true; Logs.Default.Write("Created log stream at path '{0}'.", path); return(true); } catch (Exception e) { Logs.Default.WriteWarning("Failed to create log stream at path '{0}': {1}", path, LogFormat.Exception(e)); writer = null; return(false); } }
private static bool TryCreateLogStream(string preferredDir, string preferredName, out StreamWriter writer, out string loggingPath) { if (preferredDir == null) { preferredDir = DefaultLogFileDirectory; } if (preferredName == null) { preferredName = string.Format(DefaultLogFileNameFormat, DateTime.UtcNow).Replace(':', '-'); } // Create a logfile at the desired path string logFilePath = Path.Combine(preferredDir, preferredName); if (TryCreateLogStream(logFilePath, out writer)) { loggingPath = logFilePath; return(true); } // If that fails and it was a non-default path, try the default logfile path string defaultLogFilePath = Path.Combine(DefaultLogFileDirectory, preferredName); if (defaultLogFilePath != logFilePath && TryCreateLogStream(defaultLogFilePath, out writer)) { loggingPath = defaultLogFilePath; return(true); } // If that didn't work - for example due to security / permission issues - fall back // to a logfile path in Unity's persistent data path. This should be a writable location // in any case. string unityDataDir; try { // Unity paths are using forward slashes. We'll use GetFullPath to get a // normalized version so we can safely combine it without mixing path separators. unityDataDir = UnityEngine.Application.persistentDataPath; unityDataDir = Path.GetFullPath(unityDataDir); } catch (Exception e) { Logs.Default.WriteWarning("Unable to retrieve Unity persistent data path: {0}", LogFormat.Exception(e)); loggingPath = null; return(false); } string altLogFilePath = Path.Combine(Path.Combine(unityDataDir, DefaultLogFileDirectory), preferredName); if (TryCreateLogStream(altLogFilePath, out writer)) { loggingPath = altLogFilePath; return(true); } // If all failed, we probably can't create any file at all for some reason. loggingPath = null; return(false); }
public static void WriteUnitySystemInfo(Log log) { try { log.Write( "Unity Device SystemInfo:" + Environment.NewLine + " Type: {0}" + Environment.NewLine + " Model: {1}" + Environment.NewLine + " Name: {2}" + Environment.NewLine + " UUID: {3}", UnityEngine.SystemInfo.deviceType, UnityEngine.SystemInfo.deviceModel, UnityEngine.SystemInfo.deviceName, UnityEngine.SystemInfo.deviceUniqueIdentifier); log.Write( "Unity Device Feature SystemInfo:" + Environment.NewLine + " Audio: {0}" + Environment.NewLine + " Gyroscope: {1}" + Environment.NewLine + " Location Service: {2}" + Environment.NewLine + " Vibration: {3}" + Environment.NewLine + " Accelerometer: {4}", UnityEngine.SystemInfo.supportsAudio, UnityEngine.SystemInfo.supportsGyroscope, UnityEngine.SystemInfo.supportsLocationService, UnityEngine.SystemInfo.supportsVibration, UnityEngine.SystemInfo.supportsAccelerometer); log.Write( "Unity Machine / OS SystemInfo:" + Environment.NewLine + " OS: {0}" + Environment.NewLine + " OS Family: {1}" + Environment.NewLine + " Processor Type: {2}" + Environment.NewLine + " Processor Count: {3}" + Environment.NewLine + " Processor Freq.: {4}" + Environment.NewLine + " Sys. Memory size: {5}", UnityEngine.SystemInfo.operatingSystem, UnityEngine.SystemInfo.operatingSystemFamily, UnityEngine.SystemInfo.processorType, UnityEngine.SystemInfo.processorCount, UnityEngine.SystemInfo.processorFrequency, UnityEngine.SystemInfo.systemMemorySize); log.Write( "Unity Graphics SystemInfo:" + Environment.NewLine + " Device Type: {0}" + Environment.NewLine + " Device Name: {1}" + Environment.NewLine + " Device ID: {2}" + Environment.NewLine + " Device Version: {3}" + Environment.NewLine + " Vendor: {4}" + Environment.NewLine + " Vendor ID: {5}", UnityEngine.SystemInfo.graphicsDeviceType, UnityEngine.SystemInfo.graphicsDeviceName, UnityEngine.SystemInfo.graphicsDeviceID, UnityEngine.SystemInfo.graphicsDeviceVersion, UnityEngine.SystemInfo.graphicsDeviceVendor, UnityEngine.SystemInfo.graphicsDeviceVendorID); Array textureFormats = Enum.GetValues(typeof(UnityEngine.TextureFormat)); HashSet <UnityEngine.TextureFormat> supportedTextureFormats = new HashSet <UnityEngine.TextureFormat>(); foreach (UnityEngine.TextureFormat format in textureFormats) { try { if (UnityEngine.SystemInfo.SupportsTextureFormat(format)) { supportedTextureFormats.Add(format); } } catch (Exception) {} } Array renderTextureFormats = Enum.GetValues(typeof(UnityEngine.RenderTextureFormat)); HashSet <UnityEngine.RenderTextureFormat> supportedRenderTextureFormats = new HashSet <UnityEngine.RenderTextureFormat>(); foreach (UnityEngine.RenderTextureFormat format in renderTextureFormats) { try { if (UnityEngine.SystemInfo.SupportsRenderTextureFormat(format)) { supportedRenderTextureFormats.Add(format); } } catch (Exception) {} } log.Write( "Unity Graphics Feature SystemInfo:" + Environment.NewLine + " GPU Memory Size: {0}" + Environment.NewLine + " Shader Level: {1}" + Environment.NewLine + " Compute Shaders: {2}" + Environment.NewLine + " Multi-Threaded: {3}" + Environment.NewLine + " Reversed Z-Buffer: {4}" + Environment.NewLine + " Max. Texture Size: {5}" + Environment.NewLine + " NPOT Textures: {6}" + Environment.NewLine + " CopyTexture: {7}" + Environment.NewLine + " RenderToCubemap: {8}" + Environment.NewLine + " RenderTarget Count: {9}" + Environment.NewLine + " 2D Array Textures: {10}" + Environment.NewLine + " Cube Array Textures: {11}" + Environment.NewLine + " 3D Textures: {12}" + Environment.NewLine + " Sparse Textures: {13}" + Environment.NewLine + " Instancing: {14}" + Environment.NewLine + " Image Effects: {15}" + Environment.NewLine + " Motion Vectors: {16}" + Environment.NewLine + " Raw Shadow Depth: {17}" + Environment.NewLine + " Shadows: {18}" + Environment.NewLine + " RenderTex Formats: {19}" + Environment.NewLine + " Texture Formats: {20}", UnityEngine.SystemInfo.graphicsMemorySize, UnityEngine.SystemInfo.graphicsShaderLevel, UnityEngine.SystemInfo.supportsComputeShaders, UnityEngine.SystemInfo.graphicsMultiThreaded, UnityEngine.SystemInfo.usesReversedZBuffer, UnityEngine.SystemInfo.maxTextureSize, UnityEngine.SystemInfo.npotSupport, UnityEngine.SystemInfo.copyTextureSupport, UnityEngine.SystemInfo.supportsRenderToCubemap, UnityEngine.SystemInfo.supportedRenderTargetCount, UnityEngine.SystemInfo.supports2DArrayTextures, UnityEngine.SystemInfo.supportsCubemapArrayTextures, UnityEngine.SystemInfo.supports3DTextures, UnityEngine.SystemInfo.supportsSparseTextures, UnityEngine.SystemInfo.supportsInstancing, UnityEngine.SystemInfo.supportsImageEffects, UnityEngine.SystemInfo.supportsMotionVectors, UnityEngine.SystemInfo.supportsRawShadowDepthSampling, UnityEngine.SystemInfo.supportsShadows, supportedRenderTextureFormats.ToString(", "), supportedTextureFormats.ToString(", ")); } catch (Exception e) { log.WriteWarning("Error logging Unity system info: {0}", LogFormat.Exception(e)); } }
public static void WriteUnitySpecs(Log log) { try { log.Write( "Unity Application paths:" + Environment.NewLine + " Persistent data: {0}" + Environment.NewLine + " Application Data: {1}" + Environment.NewLine + " Temp data: {2}" + Environment.NewLine + " Streaming Assets: {3}", UnityEngine.Application.persistentDataPath, UnityEngine.Application.dataPath, UnityEngine.Application.temporaryCachePath, UnityEngine.Application.streamingAssetsPath); log.Write( "Unity Screen info:" + Environment.NewLine + " Output size: {0}x{1}" + Environment.NewLine + " Fullscreen: {2}" + Environment.NewLine + " Screen Resolution: {3}" + Environment.NewLine + " Screen Orientation: {4}" + Environment.NewLine + " Screen DPI: {5}", UnityEngine.Screen.width, UnityEngine.Screen.height, UnityEngine.Screen.fullScreen, UnityEngine.Screen.currentResolution, UnityEngine.Screen.orientation, UnityEngine.Screen.dpi); log.Write("Unity Display info:"); log.PushIndent(); for (int i = 0; i < UnityEngine.Display.displays.Length; i++) { UnityEngine.Display display = UnityEngine.Display.displays[i]; log.Write( "Display #{0} {1}" + Environment.NewLine + " Native size: {2}x{3}" + Environment.NewLine + " Rendering size: {4}x{5}", i, display == UnityEngine.Display.main ? "(main)" : "", display.systemWidth, display.systemHeight, display.renderingWidth, display.renderingHeight); } log.PopIndent(); UnityEngine.AudioConfiguration audioConfig = UnityEngine.AudioSettings.GetConfiguration(); log.Write( "Unity Audio Device info:" + Environment.NewLine + " Driver Caps: {0}" + Environment.NewLine + " Speaker Mode: {1}" + Environment.NewLine + " Output Sample Rate: {2}" + Environment.NewLine + " DSP Buffer Size: {3}" + Environment.NewLine + " # Real Voices: {4}" + Environment.NewLine + " # Virtual Voices: {5}", UnityEngine.AudioSettings.driverCapabilities, UnityEngine.AudioSettings.speakerMode, UnityEngine.AudioSettings.outputSampleRate, audioConfig.dspBufferSize, audioConfig.numRealVoices, audioConfig.numVirtualVoices); } catch (Exception e) { log.WriteWarning("Error logging Unity specs: {0}", LogFormat.Exception(e)); } }