From b0e1eafaf7455134e686d3004e0741a23c09f6d0 Mon Sep 17 00:00:00 2001 From: "jiannan.guo@yingxiong.com" Date: Fri, 25 Jul 2025 17:14:05 +0800 Subject: [PATCH] 11 --- server/Sog/Core/Log/BattleLogConfig.cs | 28 ++ server/Sog/Core/Log/BattleLogger.cs | 149 +++++++++++ server/Sog/Core/Log/LogDef.cs | 86 +++++++ server/Sog/Core/Log/LogWriteThread.cs | 344 +++++++++++++++++++++++++ server/Sog/Core/Log/Logger.cs | 194 ++++++++++++++ server/Sog/Core/Log/ServerStat.cs | 339 ++++++++++++++++++++++++ server/Sog/Core/Log/TraceLog.cs | 343 ++++++++++++++++++++++++ server/Sog/Core/Log/UserLogConfig.cs | 24 ++ server/Sog/Core/Log/UserLogger.cs | 137 ++++++++++ 9 files changed, 1644 insertions(+) create mode 100644 server/Sog/Core/Log/BattleLogConfig.cs create mode 100644 server/Sog/Core/Log/BattleLogger.cs create mode 100644 server/Sog/Core/Log/LogDef.cs create mode 100644 server/Sog/Core/Log/LogWriteThread.cs create mode 100644 server/Sog/Core/Log/Logger.cs create mode 100644 server/Sog/Core/Log/ServerStat.cs create mode 100644 server/Sog/Core/Log/TraceLog.cs create mode 100644 server/Sog/Core/Log/UserLogConfig.cs create mode 100644 server/Sog/Core/Log/UserLogger.cs diff --git a/server/Sog/Core/Log/BattleLogConfig.cs b/server/Sog/Core/Log/BattleLogConfig.cs new file mode 100644 index 00000000..6162aeeb --- /dev/null +++ b/server/Sog/Core/Log/BattleLogConfig.cs @@ -0,0 +1,28 @@ +using System.Runtime.Serialization; + +namespace Sog.Log +{ + public class BattleLogConfig + { + public string logname { get; set; } + + /// + /// UserLog的等级 + /// + public string loglevel { get; set; } + + /// + /// 是否log全部User的日志 + /// + public bool userlogAll { get; set; } + + /// + /// User配置数组 + /// + public long[] userlogSome { get; set; } + + public bool logByBattleId { get; set; } + + public string logpath { get; set; } + } +} \ No newline at end of file diff --git a/server/Sog/Core/Log/BattleLogger.cs b/server/Sog/Core/Log/BattleLogger.cs new file mode 100644 index 00000000..bfa85923 --- /dev/null +++ b/server/Sog/Core/Log/BattleLogger.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; + +namespace Sog.Log +{ + public class BattleLogger + { + private static Dictionary m_allUser = new Dictionary(); + private static Dictionary m_allBattle = new Dictionary(); + + public int m_logLevel; + private string m_logPath; + private string m_logFileName; + + // 是否log所有玩家,外网请关闭 + private bool m_logAllUser; + + // 这个加了后就不会删,只是占用点内存,无所谓了 + private long[] m_logSomeUser; + + private bool m_logByBattleId; + + public void Init(string path, string filename, int logLevel, + bool logAllUser, long[] logSomeUser, bool logByBattleId) + { + m_logLevel = logLevel; + m_logPath = path; + m_logFileName = filename; + + m_logAllUser = logAllUser; + m_logSomeUser = logSomeUser; + m_logByBattleId = logByBattleId; + + m_allUser.Clear(); + m_allBattle.Clear(); + } + + public int GetLogLevel() + { + return m_logLevel; + } + + public bool NeedLogUser(long uid) + { + if(m_logAllUser) + { + return true; + } + + if(m_logSomeUser == null) + { + return false; + } + + for (int i = 0; i < m_logSomeUser.Length; i++) + { + if (m_logSomeUser[i] == uid) + { + return true; + } + } + + return false; + } + + + public void LogByLevel(long uid, ulong battleId, int frame, int logLevel, string strFormat, params object[] argvList) + { + //先判断等级 + if (logLevel < m_logLevel) + { + return; + } + + //这个uid是否需要log + if (! NeedLogUser(uid)) + { + return; + } + + Logger logger; + + if (m_logByBattleId) + { + if (false == m_allBattle.TryGetValue(battleId, out logger)) + { + logger = new Logger(m_logPath, m_logFileName + "_bt_" + battleId + ".log", m_logLevel); + m_allBattle.Add(battleId, logger); + } + } + else + { + if (false == m_allUser.TryGetValue(uid, out logger)) + { + logger = new Logger(m_logPath, m_logFileName + "_uid_" + uid + ".log", m_logLevel); + m_allUser.Add(uid, logger); + } + } + + if (OSUtils.IsWindows() == false) + { + // 在服务器上运行时,log不要影响逻辑 + try + { + string strLog = string.Format(strFormat, argvList); + logger.WriteLogNoTime(logLevel, frame, strLog); + } + catch (Exception ex) + { + logger.WriteLog(logLevel, ex.Message); + logger.WriteLog(logLevel, ex.Source); + logger.WriteLog(logLevel, ex.StackTrace); + } + } + else + { + //windows版本,为了发现问题,log错误直接异常,不继续处理了 + string strLog = string.Format(strFormat, argvList); + logger.WriteLogNoTime(logLevel, frame, strLog); + } + + } + + public void LogTraceDetail(long uid, ulong battleId, int frameCount, string strFormat, params object[] argvList) + { + LogByLevel(uid, battleId, frameCount, LogLevel.TraceDetail, strFormat, argvList); + } + + public void LogTrace(long uid, ulong battleId, int frameCount, string strFormat, params object[] argvList) + { + LogByLevel(uid, battleId, frameCount, LogLevel.Trace, strFormat, argvList); + } + + public void LogDebug(long uid, ulong battleId, int frameCount, string strFormat, params object[] argvList) + { + LogByLevel(uid, battleId, frameCount, LogLevel.Debug, strFormat, argvList); + } + + public void LogError(long uid, ulong battleId, int frameCount, string strFormat, params object[] argvList) + { + LogByLevel(uid, battleId, frameCount, LogLevel.Error, strFormat, argvList); + } + + public void LogFatal(long uid, ulong battleId, int frameCount, string strFormat, params object[] argvList) + { + LogByLevel(uid, battleId, frameCount, LogLevel.Fatal, strFormat, argvList); + } + } +} \ No newline at end of file diff --git a/server/Sog/Core/Log/LogDef.cs b/server/Sog/Core/Log/LogDef.cs new file mode 100644 index 00000000..6b7efad0 --- /dev/null +++ b/server/Sog/Core/Log/LogDef.cs @@ -0,0 +1,86 @@ +/* + Sog 游戏基础库 + 2016 by zouwei + */ + +namespace Sog.Log +{ + /// + /// 日志等级定义和辅助方法 + /// + public static class LogLevel + { + public const int Any = 0; + public const int TraceDetail = 1; + public const int Trace = 2; + public const int Debug = 3; + public const int Error = 4; + public const int Fatal = 5; + public const int None = 10; + + public static string LevelToString(int logLevel) + { + switch (logLevel) + { + case LogLevel.TraceDetail: + return "L"; + case LogLevel.Trace: + return "T"; + case LogLevel.Debug: + return "D"; + case LogLevel.Error: + return "E"; + case LogLevel.Fatal: + return "F"; + } + + return ""; + } + + public static int ParseFromString(string strLevel) + { + if(string.IsNullOrEmpty(strLevel)) + { + return LogLevel.Debug; + } + + string lowStrLevel = strLevel.ToLower(); + + if (lowStrLevel == "tracedetail") + { + return LogLevel.TraceDetail; + } + else if (lowStrLevel == "trace") + { + return LogLevel.Trace; + } + else if(lowStrLevel == "debug") + { + return LogLevel.Debug; + } + else if(lowStrLevel == "error") + { + return LogLevel.Error; + } + else if(lowStrLevel == "fatal") + { + return LogLevel.Fatal; + } + else if(lowStrLevel == "none") + { + return LogLevel.None; + } + + return LogLevel.Debug; + } + } + + + //改成static,允许修改 + public class LogDefaultParam + { + public static int ShiftFileSize = 1024 * 1024 * 50; // 10M file shift + public static int ShiftFileCount = 5; //5 file + public static int CloseTimeoutLogFileTime = 300; // 多少秒后关闭没有再写请求的文件,释放句柄 + } +} \ No newline at end of file diff --git a/server/Sog/Core/Log/LogWriteThread.cs b/server/Sog/Core/Log/LogWriteThread.cs new file mode 100644 index 00000000..900b8bac --- /dev/null +++ b/server/Sog/Core/Log/LogWriteThread.cs @@ -0,0 +1,344 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.IO; + + +namespace Sog.Log +{ + internal class LogWriteRecord + { + public string FilePathName; + public string LogMessage; + public bool ShiftFile; + } + + internal class LogFileCache + { + public bool OpenFailed; + public string FilePathName; + public long FileLength; + public long LastWriteTime; + public StreamWriter Writer; + } + + internal class LogWriteThread : Singleton , IDisposable + { + /// + /// 写队列 + /// 线程安全锁,注意,log本身是IO操作,比较费时间 + /// 优化:开启单独线程写log,其他线程写log先写到内存队列,写log线程取出写磁盘 + /// 这么做唯一的缺点是如果程序core了,有可能部分log写磁盘不成功,不过C#不容易整个进程挂掉,似乎可行 + /// + private Queue m_logQueue = new Queue(); + + //写文件的cache + //filePathName ,LogFileCache + private Dictionary m_fileCache = new Dictionary(); + + private Thread m_writeThread; + + private long m_lastCheckTimeoutTime; + + private bool m_isClosed = false; + + private object locker = new object(); + + /// + /// 等所有文件写完后释放 + /// + public void Dispose() + { + ForceClose(); + } + + /// + /// 开启 + /// + public void Start() + { + m_isClosed = false; + + if (m_writeThread == null) + { + m_writeThread = new Thread(this.WorkThreadFun); + //设置成后台线程,如果不是后台线程,线程不是主动关闭,则会一直运行,那怕进程主线程退出也没用 + m_writeThread.IsBackground = true; + m_writeThread.Start(); + } + } + + /// + /// 强制关闭,一般用不到 + /// + public void ForceClose() + { + m_isClosed = true; + + if (m_writeThread == null) + { + return; + } + + while(m_writeThread.IsAlive) + { + Thread.Sleep(1); + } + + m_writeThread = null; + + WriteAllLogInQueue(); + + CloseAllFile(); + } + + public void WriteLog(string filepathname, string message, bool shiftFile) + { + //有可能没开启 + //有可能关闭了 + if(m_isClosed) + { + return; + } + + LogWriteRecord record = new LogWriteRecord(); + record.FilePathName = filepathname; + record.LogMessage = message; + record.ShiftFile = shiftFile; + + lock (locker) + { + m_logQueue.Enqueue(record); + } + } + + private void WorkThreadFun() + { + while (!m_isClosed) + { + try + { + //写文件 + int iCount = WriteAllLogInQueue(); + if(iCount == 0) + { + Thread.Sleep(10); + //continue; + } + + //关闭长时间不写的文件 + TickCloseTimeoutFile(); + } + catch (Exception) + { + + } + } + } + + private int WriteAllLogInQueue() + { + int writeRecordCount = 0; + + while(m_logQueue.Count > 0) + { + LogWriteRecord record; + lock (locker) + { + record = m_logQueue.Dequeue(); + } + + writeRecordCount++; + + WriteLogRecord(record); + + } + + return writeRecordCount; + } + + private void WriteLogRecord(LogWriteRecord record) + { + LogFileCache fileCache; + if(false == m_fileCache.TryGetValue(record.FilePathName, out fileCache)) + { + fileCache = new LogFileCache(); + fileCache.FilePathName = record.FilePathName; + fileCache.FileLength = 0; + fileCache.OpenFailed = false; + + m_fileCache.Add(record.FilePathName, fileCache); + } + + OpenFile(fileCache); + if(record.ShiftFile) + { + ShiftFile(fileCache); + } + + if(fileCache.Writer != null) + { + fileCache.Writer.Write(record.LogMessage); + fileCache.FileLength += record.LogMessage.Length; + + //flush可以优化吗 + fileCache.Writer.Flush(); + fileCache.LastWriteTime = AppTime.GetNowSysSecond(); + } + + } + + /// + /// 打开文件,有可能失败 + /// + /// + private void OpenFile(LogFileCache fileCache) + { + if (fileCache.OpenFailed == false && fileCache.Writer == null) + { + if (! File.Exists(fileCache.FilePathName)) + { + // Create a file to write to. + using (File.Create(fileCache.FilePathName)) + { + } + } + + FileStream fileStream = new FileStream(fileCache.FilePathName, FileMode.Append, FileAccess.Write, FileShare.Read); + fileCache.Writer = new StreamWriter(fileStream); + FileInfo fileinfo = new FileInfo(fileCache.FilePathName); + fileCache.FileLength = fileinfo.Length; + + if (fileCache.Writer == null) + { + fileCache.OpenFailed = true; + } + } + } + + + /// + /// shift to multi file + /// 滚动文件 + /// + private void ShiftFile(LogFileCache fileCache) + { + if (fileCache.OpenFailed == true || fileCache.Writer == null) + { + return; + } + + //为0表示关闭shift功能 + if (LogDefaultParam.ShiftFileSize == 0) + { + return; + } + + + if (fileCache.FileLength > LogDefaultParam.ShiftFileSize) + { + //先关闭文件 + fileCache.Writer.Dispose(); + fileCache.Writer = null; + // + try + { + for (int i = LogDefaultParam.ShiftFileCount; i > 0; i--) + { + string shiftDst = fileCache.FilePathName; + if (i > 0 && i < 10) + { + shiftDst += ".0" + i.ToString(); + } + else + { + shiftDst += "." + i.ToString(); + } + string shiftSrc = fileCache.FilePathName; + int srcIndex = i - 1; + if (srcIndex > 0 && srcIndex < 10) + { + shiftSrc += ".0" + srcIndex.ToString(); + } + else if(srcIndex >= 10) + { + shiftSrc += "." + srcIndex.ToString(); + } + if (File.Exists(shiftSrc)) + { + if (File.Exists(shiftDst)) + { + File.Delete(shiftDst); + } + File.Move(shiftSrc, shiftDst); + } + + } + } + catch + { + } + fileCache.FileLength = 0; + OpenFile(fileCache); + } + } + + + /// + /// 关闭超时文件 + /// + private void TickCloseTimeoutFile() + { + long nowSecond = AppTime.GetNowSysSecond(); + + //一分钟检查一次 + if(nowSecond - m_lastCheckTimeoutTime < 60) + { + return; + } + + m_lastCheckTimeoutTime = nowSecond; + + List needCloseFile = new List(); + foreach(var item in m_fileCache.Values) + { + //5分钟超时 + if (nowSecond - item.LastWriteTime >= LogDefaultParam.CloseTimeoutLogFileTime) + { + if (item.Writer != null) + { + item.Writer.Flush(); + item.Writer.Dispose(); + } + + item.Writer = null; + + needCloseFile.Add(item.FilePathName); + } + } + + if(needCloseFile.Count > 0) + { + foreach(var filename in needCloseFile) + { + m_fileCache.Remove(filename); + } + } + } + + private void CloseAllFile() + { + foreach (var item in m_fileCache.Values) + { + if (item.Writer != null) + { + item.Writer.Flush(); + item.Writer.Dispose(); + item.Writer = null; + } + } + + m_fileCache.Clear(); + } + } +} diff --git a/server/Sog/Core/Log/Logger.cs b/server/Sog/Core/Log/Logger.cs new file mode 100644 index 00000000..0015a932 --- /dev/null +++ b/server/Sog/Core/Log/Logger.cs @@ -0,0 +1,194 @@ +/* + Sog 游戏基础库 + 2016 by zouwei + */ + +using System; +using System.IO; +using System.Text; + + + +namespace Sog.Log +{ + /// + /// 日志文件读写操作 + /// + public class Logger + { + private string m_filePathName; + private string m_LogPath; + private string m_LogFileName; + private int m_logLevel; + + //是否滚动文件,缺省滚动,运营日志可关闭 + private bool m_shiftFile = true; + + public Logger(string logPath, string logFileName, int logLevel) + { + SetPathName(logPath, logFileName); + + m_logLevel = logLevel; + } + + public void SetPathName(string logPath, string logFileName) + { + m_LogPath = logPath; + m_LogFileName = logFileName; + m_filePathName = m_LogPath + "/" + logFileName; + + try + { + //不存在则创建目录 + if (!Directory.Exists(logPath)) + { + Directory.CreateDirectory(logPath); + } + } + catch(Exception) + { + + } + + } + + public void SetFileName(string logFileName) + { + m_LogFileName = logFileName; + m_filePathName = m_LogPath + "/" + logFileName; + } + + public int GetLevel() + { + return m_logLevel; + } + + public void SetLevel(int logLevel) + { + m_logLevel = logLevel; + } + + public void CloseShift() + { + m_shiftFile = false; + } + + /// + /// 不是绝对路径 + /// + /// + public string GetFileName() + { + return m_LogFileName; + } + + internal void WriteLog(int logLevel, string strLog) + { + string logmessage = "[" + AppTime.ServerAppTime.GetDateTime().ToString("yyyy-MM-dd HH:mm:ss.fff") + "]" + LogLevel.LevelToString(logLevel) + "|" + strLog; + //补上一个回车 + if(logmessage[logmessage.Length-1] != '\n') + { + logmessage += '\n'; + } + LogWriteThread.Instance.WriteLog(m_filePathName, logmessage, m_shiftFile); + } + + internal void WriteLogNoTime(int logLevel, int frame, string strLog) + { + string logmessage = string.Format("[{0}]{1}|{2}", frame, LogLevel.LevelToString(logLevel), strLog); + //补上一个回车 + if (logmessage[logmessage.Length - 1] != '\n') + { + logmessage += '\n'; + } + LogWriteThread.Instance.WriteLog(m_filePathName, logmessage, m_shiftFile); + } + + + /// + /// bill使用,写日志的时间由外部传入 + /// 支持线程安全 + /// + /// + /// + public void WriteLogWithTime(DateTime time, string strLog) + { + string logmessage = time.ToString("yyyy-MM-dd HH:mm:ss") + "|" + strLog + "\n"; + + LogWriteThread.Instance.WriteLog(m_filePathName, logmessage, m_shiftFile); + } + + /// + /// TALog使用,写文件不需要打印时间 + /// + /// + public void WriteTALog(string strLog) + { + string logmessage = strLog + "\r\n"; + LogWriteThread.Instance.WriteLog(m_filePathName, logmessage, m_shiftFile); + } + + public void LogByLevel(int logLevel, string strFormat, params object[] argvList) + { + if (logLevel >= m_logLevel) + { + if(OSUtils.IsWindows() == false) + { + //外网版本,log错误不影响逻辑 + try { + string strLog = string.Format(strFormat, argvList); + WriteLog(logLevel, strLog); + } + catch(Exception ex) + { + WriteLog(logLevel, ex.Message); + WriteLog(logLevel, ex.Source); + WriteLog(logLevel, ex.StackTrace); + } + } + else + { + //windows版本,为了发现问题,log错误直接异常,不继续处理了 + string strLog = string.Format(strFormat, argvList); + WriteLog(logLevel, strLog); + } + } + } + + public void LogByLevelNoFormat(int logLevel, string strMessage) + { + if (logLevel >= m_logLevel) + { + WriteLog(logLevel, strMessage); + } + } + public void LogTraceDetail(string strFormat, params object[] argvList) + { + LogByLevel(LogLevel.TraceDetail, strFormat, argvList); + } + public void LogTrace(string strFormat, params object[] argvList) + { + LogByLevel(LogLevel.Trace, strFormat, argvList); + } + public void LogDebug(string strFormat, params object[] argvList) + { + LogByLevel(LogLevel.Debug, strFormat, argvList); + } + public void LogError(string strFormat, params object[] argvList) + { + LogByLevel(LogLevel.Error, strFormat, argvList); + } + public void LogFatal(string strFormat, params object[] argvList) + { + LogByLevel(LogLevel.Fatal, strFormat, argvList); + } + + + public void LogDebugNoFormat(string strMessage) + { + LogByLevelNoFormat(LogLevel.Debug, strMessage); + } + + + } +} \ No newline at end of file diff --git a/server/Sog/Core/Log/ServerStat.cs b/server/Sog/Core/Log/ServerStat.cs new file mode 100644 index 00000000..a4951413 --- /dev/null +++ b/server/Sog/Core/Log/ServerStat.cs @@ -0,0 +1,339 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Sog.Log +{ + public class NetMsgStat + { + public int MsgID; + //发送数量 + public int SendNum; + //发送的数据长度,字节 + public long SendLength; + + //接收数量 + public int RecvNum; + //接收的数据长度,字节 + public long RecvLength; + //接受消息总耗时 + public long RecvTotalTime; + //接受消息最大耗时 + public long RecvMaxTime; + //接受消息最小耗时 + public long RecvMinTime; + public void Clear() + { + SendNum = 0; + SendLength = 0; + RecvNum = 0; + RecvLength = 0; + RecvTotalTime = 0; + RecvMaxTime = 0; + RecvMinTime = 0; + } + } + + public class StatIDValue + { + public string ID; + public long Value; + + //log后不清空,缺省都清空 + public bool NotClearAfterLog; + } + + /// + /// 服务器统计类 + /// + public class ServerStat : Singleton + { + //字符串为索引 + private SortedDictionary m_statIDValuesMap; + + //每个消息发送,接收信息 + private SortedDictionary m_statNetMsgMap; + + private int TotalSendNum; + private int TotalRecvNum; + private long TotalSendLength; + private long TotalRecvLength; + + private long m_lastLogTime; + private int m_logInterval; + + + + private object m_threadLock = new object(); + + public ServerStat() + { + //缺省60秒log一次 + m_logInterval = 60*1000; + + m_statIDValuesMap = new SortedDictionary(); + + m_statNetMsgMap = new SortedDictionary(); + } + + public void Tick(long tNowMs) + { + if(m_lastLogTime == 0) + { + m_lastLogTime = tNowMs; + } + + if (tNowMs < m_lastLogTime + m_logInterval) + { + return; + } + + m_lastLogTime = tNowMs; + + LogHead(); + + LogContent(); + + LogTail(); + + + } + + private void LogContent_imp() + { + LogNetMsg(); + LogIDValues(); + LogGCMonitor(); + } + + private void LogContent() + { + lock (m_threadLock) + { + LogContent_imp(); + } + } + + //搞个头,看起来方便点 + private void LogHead() + { + TraceLog.Stat("ServerStat begin ================================================================="); + } + + private void LogTail() + { + TraceLog.Stat("ServerStat end.\n"); + } + + private void LogIDValues() + { + //没有就算了 + if(m_statIDValuesMap.Count == 0) + { + return; + } + + TraceLog.Stat(" ID Value"); + + foreach(var idvalue in m_statIDValuesMap.Values) + { + TraceLog.Stat("{0,-40}{1,-20}", idvalue.ID, idvalue.Value); + + //log完后清空这项数值 + if (idvalue.NotClearAfterLog == false) + { + idvalue.Value = 0; + } + } + /* + var keys = m_statIDValuesMap.Keys; + var keyarray = keys.ToArray(); + + foreach (string id in keyarray) + { + StatIDValue idvalue = m_statIDValuesMap[id]; + + TraceLog.Stat("{0,-40}{1,-20}",id, idvalue.Value); + + //log完后清空这项数值 + if (idvalue.NotClearAfterLog == false) + { + idvalue.Value = 0; + } + }*/ + } + + /// + /// 给id项目加value + /// + /// + /// + private void AddValue_imp(string id,long value, bool clearAfterLog = true) + { + if (m_statIDValuesMap.ContainsKey(id) == false) + { + StatIDValue idvalue = new StatIDValue(); + idvalue.ID = id; + idvalue.Value = value; + idvalue.NotClearAfterLog = !clearAfterLog; + + m_statIDValuesMap.Add(id, idvalue); + } + else + { + StatIDValue idvalue = m_statIDValuesMap[id]; + idvalue.Value += value; + idvalue.NotClearAfterLog = !clearAfterLog; + } + + } + + public void AddValue(string id, long value, bool clearAfterLog = true) + { + lock (m_threadLock) + { + AddValue_imp(id, value, clearAfterLog); + } + } + + + /// + /// 设置id项目为value + /// + /// + /// + private void SetValue_imp(string id, long value, bool clearAfterLog = true) + { + if (m_statIDValuesMap.ContainsKey(id) == false) + { + StatIDValue idvalue = new StatIDValue(); + idvalue.ID = id; + idvalue.Value = value; + idvalue.NotClearAfterLog = !clearAfterLog; + + m_statIDValuesMap.Add(id, idvalue); + } + else + { + StatIDValue idvalue = m_statIDValuesMap[id]; + idvalue.Value = value; + idvalue.NotClearAfterLog = !clearAfterLog; + } + } + + public void SetValue(string id, long value, bool clearAfterLog = true) + { + lock (m_threadLock) + { + SetValue_imp(id, value, clearAfterLog); + } + } + + + //如果不存在则创建 + private NetMsgStat GetNetMsgStatByID(int iMsgID) + { + if(!m_statNetMsgMap.ContainsKey(iMsgID)) + { + NetMsgStat stat = new NetMsgStat(); + stat.MsgID = iMsgID; + + m_statNetMsgMap.Add(iMsgID, stat); + } + + return m_statNetMsgMap[iMsgID]; + } + + public void OnNetSend(int iMsgID, int iDataLength) + { + lock (m_threadLock) + { + NetMsgStat stat = GetNetMsgStatByID(iMsgID); + + stat.SendLength += iDataLength; + stat.SendNum += 1; + + TotalSendLength += iDataLength; + TotalSendNum += 1; + } + } + + + public void OnNetRecv(int iMsgID, int iDataLength, long time) + { + lock (m_threadLock) + { + NetMsgStat stat = GetNetMsgStatByID(iMsgID); + + stat.RecvLength += iDataLength; + stat.RecvNum += 1; + stat.RecvTotalTime += time; + stat.RecvMaxTime = stat.RecvMaxTime < time ? time : stat.RecvMaxTime; + if (stat.RecvNum > 1) //不是第一次 + { + stat.RecvMinTime = stat.RecvNum > 0 ? time : stat.RecvMinTime; + } + else + { + stat.RecvMinTime = time; + } + + + TotalRecvLength += iDataLength; + TotalRecvNum += 1; + + + } + + } + + + //网络消息,接受,发送数量和长度 + private void LogNetMsg() + { + TraceLog.Stat("TotalSendNum:{0,-10} TotalRecvNum:{1,-10} TotalSendLength:{2,-12} TotalRecvLength:{3,-12}", + TotalSendNum, TotalRecvNum, TotalSendLength, TotalRecvLength); + + //清空 + TotalRecvLength = 0; + TotalRecvNum = 0; + TotalSendLength = 0; + TotalSendNum = 0; + + TraceLog.Stat("MsgID SendNum RecvNum SendLength RecvLength RecvTotalTime RecvMaxTime RecvMinTime RecvAvgTime"); + + foreach(var item in m_statNetMsgMap.Values) + { + long RecvAvgTime = item.RecvNum <= 0 ? 0 : item.RecvTotalTime / item.RecvNum; + TraceLog.Stat("{0,-10}{1,-10}{2,-10}{3,-15}{4,-15}{5,-18}{6,-16}{7,-16}{8,-16}", + item.MsgID, item.SendNum, item.RecvNum, item.SendLength, item.RecvLength, item.RecvTotalTime, item.RecvMaxTime, item.RecvMinTime, RecvAvgTime); + + //log完后清空这项数值 + item.Clear(); + } + + /* + var keys = m_statNetMsgMap.Keys; + var keyarray = keys.ToArray(); + + foreach (int id in keyarray) + { + NetMsgStat stat = m_statNetMsgMap[id]; + + TraceLog.Stat("{0,-10}{1,-10}{2,-10}{3,-15}{4,-15}", id, stat.SendNum,stat.RecvNum,stat.SendLength,stat.RecvLength); + + //log完后清空这项数值 + stat.Clear(); + } + */ + + } + + + private void LogGCMonitor() + { + GCMonitor.Instance.LogStat(); + } + } +} diff --git a/server/Sog/Core/Log/TraceLog.cs b/server/Sog/Core/Log/TraceLog.cs new file mode 100644 index 00000000..d953dadb --- /dev/null +++ b/server/Sog/Core/Log/TraceLog.cs @@ -0,0 +1,343 @@ +/* + Sog 游戏基础库 + 2016 by zouwei + */ + + +using System; +using Sog.Log; + +namespace Sog +{ + /// + /// 日志操作类,对日志,错误日志,状态日志进行简单的管理,如果需要复杂逻辑,请自己实现 + /// + /// + /// + /// public class Program + /// { + /// static void Main() + /// { + /// TraceLog.Trace("Hello {0}", "Sog") + /// TraceLog.Error("Error {0}", "error test.") + /// } + /// } + /// + /// + public static class TraceLog + { + /// + /// 缺省打开这个log + /// + private static Logger s_logger = new Logger("./", "trace.log", LogLevel.Trace); + private static Logger s_errorLogger; + private static Logger s_statLogger; + private static UserLogger s_userLogger; + private static BattleLogger s_battleLogger; + + private static int[] s_skipLogMsgID; + + public static void SetLogPathName(string strPath,string strName) + { + + if (s_logger == null) + { + s_logger = new Logger(strPath, strName, LogLevel.Trace); + } + + if (s_errorLogger == null) + { + s_errorLogger = new Logger(strPath, strName, LogLevel.Error); + } + + if(s_statLogger == null) + { + s_statLogger = new Logger(strPath, strName, LogLevel.Debug); + } + + s_logger.SetPathName(strPath, strName+".log"); + s_errorLogger.SetPathName(strPath, strName + ".error"); + s_statLogger.SetPathName(strPath, strName + ".stat"); + + //开始写线程 + LogWriteThread.Instance.Start(); + } + + public static void InitUserLog(string path, string filename, int logLevel, + bool logAllUser, long[] logSomeUser) + { + if(s_userLogger == null) + { + s_userLogger = new UserLogger(); + } + + s_userLogger.Init(path, filename, logLevel, logAllUser, logSomeUser); + } + + public static void InitBattleLog(string path, string filename, int logLevel, + bool logAllUser, long[] logSomeUser, bool logByBattleId) + { + if (s_battleLogger == null) + { + s_battleLogger = new BattleLogger(); + } + + s_battleLogger.Init(path, filename, logLevel, logAllUser, logSomeUser, logByBattleId); + } + + public static int GetLogLevel() + { + return s_logger.GetLevel(); + } + + public static void SetLogLevel(int level) + { + s_logger.SetLevel(level); + + if (level >= LogLevel.Error) + { + s_errorLogger.SetLevel(level); + } + else + { + s_errorLogger.SetLevel(LogLevel.Error); + } + } + + public static void SetSkipLogMsgID(int[] msgIDs) + { + s_skipLogMsgID = msgIDs; + } + + public static bool IsSkipLogMsgID(int iMsgID) + { + if(s_skipLogMsgID == null) + { + return false; + } + + for(int i=0; i< s_skipLogMsgID.Length; i++) + { + if(iMsgID == s_skipLogMsgID[i]) + { + return true; + } + } + return false; + } + + public static void ForceCloseLogFile() + { + LogWriteThread.Instance.ForceClose(); + } + + public static void Error(string strFormat, params object[] argvList) + { + s_logger.LogError(strFormat, argvList); + if (s_errorLogger != null) + { + s_errorLogger.LogError(strFormat, argvList); + } + } + + public static void Trace(string strFormat, params object[] argvList) + { + s_logger.LogTrace(strFormat, argvList); + } + + public static void TraceDetail(string strFormat, params object[] argvList) + { + s_logger.LogTraceDetail(strFormat, argvList); + } + + public static void Debug(string strFormat, params object[] argvList) + { + s_logger.LogDebug(strFormat, argvList); + } + + public static void DebugNoFormat(string strMessage) + { + s_logger.LogDebugNoFormat(strMessage); + } + + //写错误日志并抛出异常 + public static void Assert(string strFormat, params object[] argvList) + { + string strLog = string.Format(strFormat, argvList); + Error(strLog); + throw new System.Exception(strLog); + } + + /// + /// 写异常信息 + /// + /// + /// 是否打印到console窗口 + public static void Exception(Exception ex, bool bWriteConsole = false) + { + try + { + Error("catch one exception"); + Error("{0}", ex.Message); + + if (bWriteConsole) + { + Console.WriteLine(ex.Message); + } + + Error("{0}", ex.Source); + + if (bWriteConsole) + { + Console.WriteLine(ex.Source); + } + + Error("{0}", ex.StackTrace); + + if (bWriteConsole) + { + Console.WriteLine(ex.StackTrace); + } + + if (ex.InnerException != null) + { + Error("{0}", ex.InnerException); + + if (bWriteConsole) + { + Console.WriteLine(ex.InnerException); + } + } + } + catch(Exception innerEx) + { + //打exception又抛出异常就算了 + Error("TraceLog.Exception throw a Exception {0}", innerEx.Message); + } + } + + /// + /// 写统计日志,xxx.stat + /// + /// + /// + public static void Stat(string strFormat, params object[] argvList) + { + if (s_statLogger != null) + { + s_statLogger.LogDebug(strFormat, argvList); + } + } + + /// + /// 玩家日志 + /// + /// + /// + public static void UserError(long uid, string strFormat, params object[] argvList) + { + if (s_userLogger != null && uid > 0) + { + s_userLogger.LogError(uid, strFormat, argvList); + } + s_logger.LogError(strFormat, argvList); + + if (s_errorLogger != null) + { + s_errorLogger.LogError(strFormat, argvList); + } + } + + + public static void UserTraceDetail(long uid, string strFormat, params object[] argvList) + { + if (s_userLogger != null && uid > 0) + { + s_userLogger.LogTraceDetail(uid, strFormat, argvList); + } + s_logger.LogTraceDetail(strFormat, argvList); + } + + public static void UserTrace(long uid, string strFormat, params object[] argvList) + { + if (s_userLogger != null && uid > 0) + { + s_userLogger.LogTrace(uid, strFormat, argvList); + } + s_logger.LogTrace(strFormat, argvList); + } + + public static void UserDebug(long uid, string strFormat, params object[] argvList) + { + if (s_userLogger != null && uid > 0) + { + s_userLogger.LogDebug(uid, strFormat, argvList); + } + + s_logger.LogDebug(strFormat, argvList); + } + + public static bool NeedLogUserTraceLevel(long uid) + { + if(s_userLogger.GetLogLevel() > (int)LogLevel.TraceDetail) + { + return false; + } + + return s_userLogger.NeedLogUser(uid); + } + + + public static void BattleTraceDetail(long uid, ulong battleId, int seq, string strFormat, params object[] argvList) + { + if (s_logger != null) + { + s_logger.LogByLevel(LogLevel.TraceDetail, strFormat, argvList); + } + + if (s_battleLogger != null) + { + s_battleLogger.LogTraceDetail(uid, battleId, seq, strFormat, argvList); + } + } + + public static void BattleTrace(long uid, ulong battleId, int seq, string strFormat, params object[] argvList) + { + if (s_logger != null) + { + s_logger.LogTrace(strFormat, argvList); + } + + if (s_battleLogger != null) + { + s_battleLogger.LogTrace(uid, battleId, seq, strFormat, argvList); + } + } + + public static void BattleDebug(long uid, ulong battleId, int seq, string strFormat, params object[] argvList) + { + if (s_logger != null) + { + s_logger.LogDebug(strFormat, argvList); + } + + if (s_battleLogger != null) + { + s_battleLogger.LogDebug(uid, battleId, seq, strFormat, argvList); + } + } + + public static void BattleError(long uid, ulong battleId, int seq, string strFormat, params object[] argvList) + { + if (s_logger != null) + { + s_logger.LogError(strFormat, argvList); + } + + if (s_battleLogger != null) + { + s_battleLogger.LogError(uid, battleId, seq, strFormat, argvList); + } + } + } +} \ No newline at end of file diff --git a/server/Sog/Core/Log/UserLogConfig.cs b/server/Sog/Core/Log/UserLogConfig.cs new file mode 100644 index 00000000..c2b11a8e --- /dev/null +++ b/server/Sog/Core/Log/UserLogConfig.cs @@ -0,0 +1,24 @@ +using System.Runtime.Serialization; + +namespace Sog.Log +{ + public class UserLogConfig + { + public string userlogname { get; set; } + + /// + /// UserLog的等级 + /// + public string userloglevel { get; set; } + + /// + /// 是否log全部User的日志 + /// + public bool userlogAll { get; set; } + + /// + /// User配置数组 + /// + public long[] userlogSome { get; set; } + } +} diff --git a/server/Sog/Core/Log/UserLogger.cs b/server/Sog/Core/Log/UserLogger.cs new file mode 100644 index 00000000..c1364745 --- /dev/null +++ b/server/Sog/Core/Log/UserLogger.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Sog.Log +{ + public class UserLogger + { + private int m_logLevel; + + /// + /// 是否log所有玩家,外网请关闭 + /// + private bool m_logAllUser; + + private long[] m_logSomeUser; + + private string m_logPath; + private string m_logFileName; + + //这个加了后就不会删,只是占用点内存,无所谓了 + private Dictionary m_allUser = new Dictionary(); + + public void Init(string path, string filename, int logLevel, bool logAllUser,long[] logSomeUser) + { + m_logAllUser = logAllUser; + m_logLevel = logLevel; + m_logSomeUser = logSomeUser; + + m_logPath = path; + m_logFileName = filename; + + m_allUser.Clear(); + } + + public int GetLogLevel() + { + return m_logLevel; + } + + public bool NeedLogUser(long uid) + { + if(m_logAllUser) + { + return true; + } + + if(m_logSomeUser == null) + { + return false; + } + + for(int i=0; i< m_logSomeUser.Length; i++) + { + if(m_logSomeUser[i] == uid) + { + return true; + } + } + + return false; + } + + + public void LogByLevel(long uid, int logLevel, string strFormat, params object[] argvList) + { + //先判断等级 + if (logLevel < m_logLevel) + { + return; + } + + //这个uid是否需要log + if (!NeedLogUser(uid)) + { + return; + } + + + Logger logger; + if (false == m_allUser.TryGetValue(uid, out logger)) + { + logger = new Logger(m_logPath, m_logFileName + "_" + uid.ToString() + ".log", m_logLevel); + m_allUser.Add(uid, logger); + } + + if (OSUtils.IsWindows() == false) + { + //外网版本,log错误不影响逻辑 + try + { + string strLog = string.Format(strFormat, argvList); + logger.WriteLog(logLevel, strLog); + } + catch (Exception ex) + { + logger.WriteLog(logLevel, ex.Message); + logger.WriteLog(logLevel, ex.Source); + logger.WriteLog(logLevel, ex.StackTrace); + } + } + else + { + //windows版本,为了发现问题,log错误直接异常,不继续处理了 + string strLog = string.Format(strFormat, argvList); + logger.WriteLog(logLevel, strLog); + } + + //string strLog = string.Format(strFormat, argvList); + + //logger.WriteLog(logLevel, strLog); + } + + public void LogTraceDetail(long uid, string strFormat, params object[] argvList) + { + LogByLevel(uid, LogLevel.TraceDetail, strFormat, argvList); + } + + public void LogTrace(long uid,string strFormat, params object[] argvList) + { + LogByLevel(uid,LogLevel.Trace, strFormat, argvList); + } + public void LogDebug(long uid, string strFormat, params object[] argvList) + { + LogByLevel(uid,LogLevel.Debug, strFormat, argvList); + } + public void LogError(long uid, string strFormat, params object[] argvList) + { + LogByLevel(uid,LogLevel.Error, strFormat, argvList); + } + public void LogFatal(long uid, string strFormat, params object[] argvList) + { + LogByLevel(uid,LogLevel.Fatal, strFormat, argvList); + } + } +}