ro-webgl/Assets/Src/Core/Log/DebugHelper.cs
2021-12-21 09:40:39 +08:00

626 lines
16 KiB
C#

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<ILogger> s_loggerWnds = new List<ILogger>();
//日志开关,外部可以通过这个控制日志的开启与关闭
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<string> sLogTxt = new List<string>();
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;
}
}