Unity3D 开发工具系列 日志系统:核心模块LoggingUnity3D 开发工具系列 日志系统:调用封装LogUnity3D 开发工具系列 日志系统:输出模块ConsoleAppenderUnity3D 开发工具系列 日志系统:输出模块FileAppenderUnity3D 开发工具系列 日志系统:输出模块GUIAppenderUnity3D 开发工具系列 日志系统:输出模块MobileGUIAppenderUnity3D 开发工具系列 日志系统:输出模块WindowAppenderUnity3D 开发工具系列 日志系统:配置模块LogConfig


日志核心模块

  • 日志概述
  • 需求分析
  • 源代码


日志概述

在项目中,程序的信息输出是必不可少的;但Unity自带的Debug输出不易控制,大量的输出除了会影响性能外,还会照成输出混乱,很难找到有效的信息;因此集成Epitome.LogSystem功能插件,有效过滤输出类型和类别,帮助定位信息位置,并有效的同时处理大量输出造成的降帧率的情况;本文描述了Unity日志核心模块Logging具体实现过程;

需求分析

源代码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using UnityEngine;

namespace Epitome.LogSystem
{
    public delegate void LogCall(string logLevel, string message);

    public enum LogLevel
    {
        ALL,   // 最低等级的,用于打开所有日志记录;
        TRACE, // 比DEBUG更详细信息事件,很低的日志级别,一般不会使用;
        DEBUG, // 主要用于开发过程中打印一些运行信息;
        INFO,  // 打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息;
        WARN,  // 表明会出现潜在错误的情形,给开发人员一些提示;
        ERROR, // 虽然发生错误事件,但仍然不影响系统的继续运行,打印错误和异常信息;
        FATAL, // 指出每个严重的错误事件将会导致应用程序的退出,重大错误,可以直接停止程序;
        OFF,   // 最高等级的,用于关闭所有日志记录;
    }

    public struct LogData
    {
        public string logID;        // 日志ID
        public LogLevel logLevel;   // 日志级别
        public DateTime logTime;    // 日志时间
        public object logMessage;   // 日志消息
        public string logBasicData; // 日志基础数据
        public string logTrack;     // 日志堆栈信息
    }

    public interface ILogAppender
    {
        void Log(LogData data);
    }

    public class AsyncTask<T> : MonoSingleton<T> where T : AsyncTask<T>
    {
        private void Start()
        {
            Run();
        }

        protected override void OnDestroy()
        {
            Close();

            base.OnDestroy();
        }

        public virtual void Close() { }

        public virtual void Run() { }
    }

    public enum AppenderType
    {
        File,          // 文件输出
        GUI,           // GUI输出(用于电脑端)
        MobileGUI,     // 移动GUI输出(用于手机端)
        Console,       // 控制台输出
        Window,        // 窗口输出
    }

    public class Logging : Singleton<Logging>
    {
        private Dictionary<AppenderType, ILogAppender> allAppender;

        private List<ILogAppender> appenders;

        public bool EnableLog { get; set; }

        public LogLevel logLevel { get; set; }

        public LogSystemAsset logSystemAsset { get; private set; }

        private List<LogLevel> ignoreLevel;

        private Logging()
        {
            allAppender = new Dictionary<AppenderType, ILogAppender>();

            appenders = new List<ILogAppender>();

            ignoreLevel = new List<LogLevel>();

            logSystemAsset = Resources.Load<LogSystemAsset>("LogSystemAsset");
        }

        private StackTrace StackTrace
        {
            get { return new StackTrace(3, true); }
        }

        public void LoadAppenders(ILogAppender appender)
        {
            if (appenders.Contains(appender)) return;

            appenders.Add(appender);
        }

        public void UnloadAppenders(ILogAppender appender)
        {
            if (!appenders.Contains(appender)) return;

            appenders.Remove(appender);
        }

        public void UnloadAppenders(AppenderType type)
        {
            if (!allAppender.ContainsKey(type)) return;

            UnloadAppenders(allAppender.GetValue(type));
        }

        public void SetLogCall(LogCall logCall) { }

        /// <summary>
        /// For these added level, the log does not do output.(Except for error output)
        /// </summary>
        /// <param name="level"></param>
        public void IgnoreLevel(LogLevel level)
        {
            if (!ignoreLevel.Contains(level))
                ignoreLevel.Add(level);
        }
        public void UnIgnoreLevel(LogLevel level)
        {
            if (ignoreLevel.Contains(level))
                ignoreLevel.Remove(level);
        }

        private bool IsOutputLog(LogLevel level)
        {
            if (!EnableLog || (int)logLevel > (int)level || ignoreLevel.Contains(level)) return false;
            return true;
        }

        private string GetParameters(System.Reflection.MethodBase methodBase)
        {
            StringBuilder builder = new StringBuilder(1);
            foreach (var item in methodBase.GetParameters())
            {
                builder.Append(item.ParameterType.Name);
            }
            return builder.ToString();
        }

        private string GetRelativePath(string path)
        {
            path = path.Substring(path.IndexOf("Assets"));
            path = path.Replace('\\', '/');
            return path;
        }

        private string GetBriefnessTrack(StackFrame frame)
        {
            return string.Format("{0}:{1}({2}) (at {3}:{4,3})",
                frame.GetMethod().DeclaringType.Name,
                frame.GetMethod().Name,
                GetParameters(frame.GetMethod()),
                GetRelativePath(frame.GetFileName()),
                frame.GetFileLineNumber());
        }

        private string TrackFormatting(StackTrace stackTrace)
        {
            StringBuilder builder = new StringBuilder(120);
            foreach (var item in stackTrace.GetFrames())
            {
                builder.Append(string.Format(GetBriefnessTrack(item))).Append("\r\n");
            }
            return builder.ToString();
        }

        private string GetExceptionTrack(Exception e)
        {
            StringBuilder builder = new StringBuilder(120);
            builder.Append("Error:" + e.Message).Append("\r\n");
            if (!string.IsNullOrEmpty(e.StackTrace))
            {
                builder.Append(e.StackTrace);
            }
            return builder.ToString();
        }

        private void LogRecord(LogLevel level, object message, string track = "")
        {
            if (!IsOutputLog(level)) return;
            LogData data = new LogData();
            data.logTime = DateTime.Now;
            data.logLevel = level;
            data.logMessage = message;
            data.logBasicData = GetBriefnessTrack(StackTrace.GetFrame(1));
            data.logTrack = track;

            for (int i = 0; i < appenders.Count; i++)
            {
                appenders[i].Log(data);
            }
        }

        public void Trace(object message)
        {
            LogRecord(LogLevel.TRACE, message, TrackFormatting(StackTrace));
        }
        public void Trace(string format, params object[] args)
        {
            LogRecord(LogLevel.TRACE, string.Format(format, args), TrackFormatting(StackTrace));
        }

        public void Debug(object message)
        {
            LogRecord(LogLevel.DEBUG, message, TrackFormatting(StackTrace));
        }
        public void Debug(string format, params object[] args)
        {
            LogRecord(LogLevel.DEBUG, string.Format(format, args), TrackFormatting(StackTrace));
        }
        public void Debug(object message, string track)
        {
            LogRecord(LogLevel.DEBUG, message, track);
        }
        public void Debug(object message, Exception e)
        {
            LogRecord(LogLevel.DEBUG, message, GetExceptionTrack(e));
        }

        public void Info(object message)
        {
            LogRecord(LogLevel.INFO, message);
        }
        public void Info(string format, params object[] args)
        {
            LogRecord(LogLevel.INFO, string.Format(format, args), TrackFormatting(StackTrace));
        }
        public void Info(object message, string track)
        {
            LogRecord(LogLevel.INFO, message, track);
        }
        public void Info(object message, Exception e)
        {
            LogRecord(LogLevel.INFO, message, GetExceptionTrack(e));
        }

        public void Warn(object message)
        {
            LogRecord(LogLevel.WARN, message, TrackFormatting(StackTrace));
        }
        public void Warn(string format, params object[] args)
        {
            LogRecord(LogLevel.WARN, string.Format(format, args), TrackFormatting(StackTrace));
        }
        public void Warn(object message, string track)
        {
            LogRecord(LogLevel.WARN, message, track);
        }
        public void Warn(object message, Exception e)
        {
            LogRecord(LogLevel.WARN, message, GetExceptionTrack(e));
        }

        public void Error(object message)
        {
            LogRecord(LogLevel.ERROR, message, TrackFormatting(StackTrace));
        }
        public void Error(string format, params object[] args)
        {
            LogRecord(LogLevel.ERROR, string.Format(format, args), TrackFormatting(StackTrace));
        }
        public void Error(object message, string track)
        {
            LogRecord(LogLevel.ERROR, message, track);
        }
        public void Error(object message, Exception e)
        {
            LogRecord(LogLevel.ERROR, message, GetExceptionTrack(e));
        }

        public void Fatal(object message)
        {
            LogRecord(LogLevel.FATAL, message, TrackFormatting(StackTrace));
        }
        public void Fatal(string format, params object[] args)
        {
            LogRecord(LogLevel.FATAL, string.Format(format, args), TrackFormatting(StackTrace));
        }
        public void Fatal(object message, string track)
        {
            LogRecord(LogLevel.FATAL, message, track);
        }
        public void Fatal(object message, Exception e)
        {
            LogRecord(LogLevel.FATAL, message, GetExceptionTrack(e));
        }
    }
}