using UnityEngine; using System.Collections; using System.Collections.Generic; using System.IO; using System; using System.Diagnostics; using Debug = UnityEngine.Debug; using System.Runtime.InteropServices; using System.Net.Sockets; using System.Net; //adb logcat -s Unity //adb shell dumpsys meminfo com.tencent.tmgp.sgame //adb shell top -m 10 -s cpu //adb forward tcp:54999 localabstract:Unity-com.tencent.tmgp.sgame public enum SLogTypeDef { LogType_None, //不输出log LogType_System, //系统默认方式 LogType_Custom, //自定义方式 }; public enum SLogCategory{ Normal, //普通log Max }; public enum LogLevel { Error, Warning, Info, } public class LogInfo { public LogType type; public string content; public string stackTree; public string channel; public LogInfo(string content_,string stackTree_,LogType type_) { content = content_; type = type_; stackTree = stackTree_; if(type == LogType.Log) { channel = "Log"; }else if(type == LogType.Warning) { channel = "Warning"; }else { channel = "Error"; } } } public class DebugHelper : MonoBehaviour { public static string Tag_BHY = "BHY:"; public static DebugHelper instance = null; public static LogLevel LogLevel = LogLevel.Warning; public static SLogTypeDef logMode = SLogTypeDef.LogType_None; //是否写入到文件中 static SLogObj[] s_logers = new SLogObj[(int)SLogCategory.Max]; static List s_loggerWnds = new List(); //日志开关,外部可以通过这个控制日志的开启与关闭 public static bool enableLog = true; static long logCnt = 1; static string s_path = ""; public static void AddLoggerWnd(ILogger logger) { lock (s_loggerWnds) { if (!s_loggerWnds.Contains(logger)) { s_loggerWnds.Add(logger); } } } public static void OpenLoger(SLogCategory logType, string logFile) { var idx = (int)logType; s_logers[idx].Flush(); s_logers[idx].Close(); s_logers[idx].TargetPath = logFile; } public static void CloseLoger(SLogCategory logType) { var idx = (int)logType; s_logers[idx].Flush(); s_logers[idx].Close(); s_logers[idx].TargetPath = null; } public static void BeginLogs() { ClearLogs(180); CloseLogs(); if (string.IsNullOrEmpty(s_path)) { string folder = logRootPath; string dt = DateTime.Now.ToString("yyyyMMdd_HHmmss"); s_path = string.Format("{0}/{1}_normal.log", folder, dt); } OpenLoger(SLogCategory.Normal, string.Format("{0}_{1}",s_path,logCnt)); logMode = SLogTypeDef.LogType_Custom; logCnt++; } public static void CloseLogs() { logMode = SLogTypeDef.LogType_None; CloseLoger(SLogCategory.Normal); } public static void ClearLogs(int passedMinutes = 60) { DateTime now = DateTime.Now; DirectoryInfo dirInfo = new DirectoryInfo(logRootPath); if (dirInfo.Exists) { string[] files = Directory.GetFiles(dirInfo.FullName, "*.log", SearchOption.TopDirectoryOnly); for (int i = 0; i < files.Length; ++i) { try { string filePath = files[i]; FileInfo fi = new FileInfo(filePath); if (fi.Exists && (now - fi.CreationTime).TotalMinutes > passedMinutes) { File.Delete(filePath); } } catch { } } } } public static SLogObj GetLoger(SLogCategory logType) { return s_logers[(int)logType]; } public static string GetLogerPath(SLogCategory logType) { return s_logers[(int)logType].LastTargetPath; } static public void EditorAssert(bool InCondition, string InFormat = null, params object[] InParameters) { Assert(InCondition, InFormat, InParameters); } static public void Assert(bool InCondition) { Assert(InCondition, null, null); } static public void Assert(bool InCondition, string InFormat ) { Assert(InCondition, InFormat, null); } static public void Assert(bool InCondition, string InFormat, params object[] InParameters ) { // 虽然有了Conditional了 但是保险起见 还是用宏包一层 双管齐下 双保险 #if UNITY_EDITOR || UNITY_STANDALONE || UNITY_ANDROID || FORCE_LOG || UNITY_IPHONE if (!InCondition) { try { string failedMessage = null; if (!string.IsNullOrEmpty(InFormat)) { try { if (InParameters != null) { failedMessage = string.Format(InFormat, InParameters); } else { failedMessage = InFormat; } } catch (Exception) { } } #if UNITY_ANDROID || UNITY_IPHONE else { failedMessage = string.Format(" no assert detail, stacktrace is :{0}", Environment.StackTrace); } #endif #if UNITY_EDITOR || UNITY_STANDALONE if (failedMessage != null) { Debug.LogError("Assert failed! " + failedMessage); } else { Debug.LogError("Assert failed!"); } string msg = "Assert failed! "; if (!string.IsNullOrEmpty(failedMessage)) { msg += failedMessage; } var trace = new System.Diagnostics.StackTrace(); var frames = trace.GetFrames(); for (int i = 0; i < frames.Length; ++i) { msg += frames[i].ToString(); } try { LogInternal(SLogCategory.Normal, msg); } catch (Exception) { } #else if (failedMessage != null) { var str = "Assert : " + failedMessage; LogInternal(SLogCategory.Normal, str); } else { Debug.LogWarning("Assert failed!"); } #endif } catch (Exception) { // ignore all exception in logger. } } #endif } public static void LogInternal(SLogCategory logType, string logmsg) { if (enableLog && DebugHelper.logMode == SLogTypeDef.LogType_Custom) { long curFileSize = s_logers[(int)logType].GetFileSize(); #if UNITY_EDITOR if (curFileSize > 200000) #else if(curFileSize > 200) #endif { CloseLogs(); BeginLogs(); } string timeLogMsg = string.Format("{0}:{1}", DateTime.Now.ToString("yyyy.MM.dd-HH:mm:ss:ffff"),logmsg); s_logers[(int)logType].Log(timeLogMsg); } } private static string CachedLogRootPath; public static string logRootPath { get { if( CachedLogRootPath == null ) { #if UNITY_EDITOR string folder = string.Format("{0}/../../Log/", Application.dataPath); #elif UNITY_STANDALONE string folder = string.Format("{0}/../Log/", Application.dataPath); #else string folder = string.Format("{0}/Log/", Application.persistentDataPath); #endif if (!Directory.Exists(folder)) { Directory.CreateDirectory(folder); } CachedLogRootPath = folder; } return CachedLogRootPath; } } public static void Log(string logmsg) { if (enableLog && LogLevel >= LogLevel.Info) { Debug.Log(logmsg); } } public static void Log(object msg, params object[] args) { Log(_format(msg, args)); } public static void LogError(string errmsg) { if (enableLog && LogLevel >= LogLevel.Error) { Debug.LogError(errmsg); } } public static void LogError(object msg, params object[] args) { LogError(_format(msg, args)); } public static void LogWarning(string warmsg) { if (enableLog && LogLevel >= LogLevel.Warning) { Debug.LogWarning(warmsg); } } public static void LogWarning(object msg, params object[] args) { LogWarning(_format(msg, args)); } public static void LogException(Exception ex) { if (enableLog && LogLevel >= LogLevel.Error) { Debug.LogException(ex); } } private void Awake() { DebugHelper.Assert(instance == null); instance = this; int cnt = (int)SLogCategory.Max; for (int i = 0; i < cnt; i++) { s_logers[i] = new SLogObj(); } Application.logMessageReceivedThreaded += OnLogReceived; } protected void OnDestroy() { int cnt = (int)SLogCategory.Max; for (int i = 0; i < cnt; i++) { s_logers[i].Flush(); s_logers[i].Close(); } BackgroundWorker.DestroyInstance(); Application.logMessageReceivedThreaded -= OnLogReceived; } void Update() { int cnt = (int)SLogCategory.Max; for (int i = 0; i < cnt; i++) { if (s_logers[i]!=null) { s_logers[i].Flush(); } } } private void OnLogReceived(string condition, string stackTrace, LogType type) { bool canLogged = false; switch (type) { case LogType.Assert: case LogType.Exception: canLogged = true; break; case LogType.Error: canLogged = LogLevel >= LogLevel.Error; break; case LogType.Warning: canLogged = LogLevel >= LogLevel.Warning; break; case LogType.Log: default: canLogged = LogLevel >= LogLevel.Info; break; } s_loggerWnds.RemoveAll(l => l == null); //s_loggerWnds.ForEach(l => l.Log(condition,stackTrace,type)); if (canLogged) { try { // 如果是异常的话,加上堆栈信息 if (type == LogType.Exception) { condition = string.Format("{0}\r\n {1}", condition, stackTrace.Replace("\n", "\r\n ")); } LogInternal(SLogCategory.Normal, condition); } catch (System.Exception ex) { Debug.LogException(ex); // this should at least print to Unity Editor (but may skip the file writing due to earlier writing failure) } } } private static string _format(object msg, params object[] args) { string fmt = msg as string; if (args == null || args.Length == 0 || string.IsNullOrEmpty(fmt)) { return msg.ToString(); } else { return string.Format(fmt, args); } } //#if UNITY_STANDALONE_WIN || UNITY_EDITOR || FORCE_LOG // private static Socket _socket = null; // private static IPEndPoint _remotePoint = null; // private static string _remoteHost = null; // private static uint _sequence = 0; // public static void LogToRemote(string logMsg, string remoteHost = "localhost") // { // try // { // if (null == _socket || remoteHost != _remoteHost) // { // _remoteHost = remoteHost; // if (null != _socket) _socket.Close(); // IPHostEntry remoteEntry = Dns.GetHostEntry(_remoteHost); // IPAddress remoteAddr = null; // for (int i = 0; i < remoteEntry.AddressList.Length; ++i) // { // if (remoteEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork) // { // remoteAddr = remoteEntry.AddressList[i]; // break; // } // } // if (null == remoteAddr) // { // Log("IPV4 network not found!"); // return; // } // _remotePoint = new IPEndPoint(remoteAddr, 11557); // _socket = new Socket(remoteAddr.AddressFamily, SocketType.Dgram, ProtocolType.Udp); // _sequence = 0; // } // byte[] msg = System.Text.Encoding.UTF8.GetBytes((++_sequence) + ":" + logMsg); // _socket.SendTo(msg, SocketFlags.None, _remotePoint); // } // catch (Exception ecp) // { // Log(ecp.Message); // } // } //#endif } public class SLogObj { StreamWriterProxy streamWriter = null; string filePath = null; List sLogTxt = new List(); string targetPath = null; string lastTargetPath = null; public string TargetPath { get { return targetPath; } set { targetPath = value; if (!string.IsNullOrEmpty(targetPath)) lastTargetPath = targetPath; filePath = null; } } public string LastTargetPath { get { return lastTargetPath; } } bool Begin() { if (streamWriter != null) return true; bool createNew = false; if (TargetPath != null) { if (filePath != TargetPath) { filePath = targetPath; createNew = true; } } else { if (filePath == null) { createNew = true; //string dt = DateTime.Now.ToString("yyyyMMdd_HHmmss"); string dt = "debug"; filePath = string.Format("{0}/../ygame_{1}.txt", Application.dataPath, dt); } } try { streamWriter = new StreamWriterProxy(filePath, createNew); } catch (Exception) { return false; } return true; } void End() { streamWriter.Close(); streamWriter = null; } public void Close() { Flush(); if (streamWriter != null) { streamWriter.Close(); } streamWriter = null; filePath = null; } public void Log(string str) { sLogTxt.Add(str); } public void Flush() { if (sLogTxt.Count > 0) { if (!Begin()) return; for (int i = 0; i < sLogTxt.Count; ++i) { streamWriter.WriteLine(sLogTxt[i]); } sLogTxt.Clear(); End(); } } public long GetFileSize() { if (string.IsNullOrEmpty(targetPath)) return 0; FileInfo info = new FileInfo(targetPath); if (info != null && info.Exists) { return info.Length / 1024; } return 0; } }