Unity應用架構設計(13)——日志組件的實施


對於應用程序而言,日志是非常重要的功能,通過日志,我們可以跟蹤應用程序的數據狀態,記錄Crash的日志可以幫助我們分析應用程序崩潰的原因,我們甚至可以通過日志來進行性能的監控。總之,日志的好處很多,特別是對Release之后的線上版本進行異常的跟蹤。

日志存儲的分類

在平常開發時,我們通常喜歡在Debug模式下進行調試,通過斷點,可以跟蹤數據的變化。除了調試,另一種直觀的方式是使用控制台輸出,比如Java的system.out.println(),.NET的Console.WriteLine(),Swift的print()等等。在Untiy中,為我們提供了Debug.Log()方式來記錄。

而對於線上的版本,上述兩種調試都不行,那我們怎么來跟蹤數據呢?

從日志的存儲分類上來看,可以分為四類:控制台,文件系統,數據庫,第三方平台

  • 控制台:本地開發時使用,記錄數據和跟蹤執行過程,方便直觀
  • 文件系統:可以是一些用戶行為性的日志,這些文件可以被用來監控執行時間,進行性能的分析,如果用戶同意,則將這些日志傳到服務器上
  • 數據庫:記錄了一些異常日志,也就是Catch了之后的行為,每次用戶登錄時,傳到服務器,幫助分析原因
  • 第三方平台:比如友盟等,當應用閃退時,Crash原因會記錄在友盟中,可以通過DashBoard查看

日志組件的設計

為了可以更加靈活的跟蹤線上的變化,可以使用第三方的Analysis,也可以自建日志組件。我偏向於混合使用,所以接下來,談談一個日志組件的基本設計理念,如下圖所示:

從上圖可以看出,整個入口由工廠LogFactory來創建LogStrategy子類實例,LogStrategy是個抽象的模板類,定義了公共的處理方法,但並不知道怎樣寫日志(比如是寫入到數據庫呢還是到文件),寫日志的行為交給子類去完成。

日志組件的實施

有了日志組件的設計圖,接下來就是將理念落實到行動,讓我們來實現它吧!

LogFactory是一個簡單工廠,封裝創建LogStrategy對象的代碼。

public class LogFactory
{
    public static LogFactory Instance=new LogFactory();
    private LogFactory(){}
    private readonly Dictionary<string,LogStrategy> _strategies=new Dictionary<string, LogStrategy>()
    {
        {typeof(ConsoleLogStrategy).Name,new ConsoleLogStrategy() },
        {typeof(FileLogStrategy).Name,new FileLogStrategy() },
        {typeof(DatabaseLogStrategy).Name,new DatabaseLogStrategy() }
    }; 
    public LogStrategy Resolve<T>() where T:LogStrategy
    {
        return _strategies[typeof(T).Name];
    }
}

LogFactory內部定義了一個字典,Key為LogStrategy子類的類名,Value為具體的LogStrategy對象。通過一個公共接口Resolve<T>來獲取相關對象。

使用字典比switch..case更直觀,也更加容易擴展其他選項。更重要的是,不會對公共接口Resolve<T>進行修改。

LogStrategy是一個抽象類,即模板類。

它定義了一個公共的API,即Log。在方法Log中,定義了一些對內容的公共操作,因為對於日志來說,不管是記錄在數據庫還是文件系統,都將對內容拼接上設備類型、設備名稱、操作系統、創建時間等基本信息。

同時還定義了一個抽象方法RecordMessage,對於需要寫入的類型(文件系統Or數據庫Or控制台)延遲到子類決定。

public abstract class LogStrategy
{
    private readonly StringBuilder _messageBuilder=new StringBuilder();
    protected IContentWriter Writer { get; set; }

    /// <summary>
    ///     模板方法
    /// </summary>
    protected abstract void RecordMessage(string message);

    protected abstract void SetContentWriter();

    /// <summary>
    ///     公共的API
    /// </summary>
    public void Log(string message,bool verbose=false)
    {
        if (verbose)
        {
            //公共方法
            RecordDateTime();
            RecordDeviceModel();
            RecordDeviceName();
            RecordOperatingSystem();
        }
        //抽象方法,交由子類實現
        RecordMessage(_messageBuilder.AppendLine(string.Format("Message:{0}", message)).ToString());
    }

    private void RecordDateTime()
    {
        _messageBuilder.AppendLine(string.Format("DateTime:{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
    }

    private void RecordDeviceModel()
    {
        _messageBuilder.AppendLine(string.Format("Device Model:{0}",SystemInfo.deviceModel));
    }

    private void RecordDeviceName()
    {
        _messageBuilder.AppendLine(string.Format("Device Name:{0}", SystemInfo.deviceName));
    }

    private void RecordOperatingSystem()
    {
        _messageBuilder
            .AppendLine(string.Format("Operating System:{0}", SystemInfo.operatingSystem))
            .AppendLine();
    }

模板方法模式:在一個方法中定義算法的骨架,而將一些步驟延遲到子類。模板方法使得子類可以在不改變算法的結構下,重新定義算法中的某些步驟。

當在控制台Debug時,我們其實不需要設備類型,設備名稱等信息,故公共接口Log提供了一個開關verbose來開啟是否需要詳細信息,默認為false,即關閉狀態。

繼承LogStrategy,創建自定義的日志策略

比如實現FileLogStrategy,除了override了 RecordMessage方法之外,還需要提供一個實現了IContentWriter接口的類,你可以直接在RecordMessage方法中寫入日志,但可能有一些公共的操作,比如在異步線程,批量將10條數據寫到文件或者數據庫中,所以提供一個IContentWriter更加容易擴展。

public class FileLogStrategy:LogStrategy
{
    public FileLogStrategy()
    {
        SetContentWriter();
    }
    protected sealed override void SetContentWriter()
    {
        Writer = new FileContentWriter(); 
    }
    protected override void RecordMessage(string message)
    {
        Writer.Write(message);
    }

}

創建一個BaseContentWriter,提供了公共的寫入方法,比如為了提高性能,文件的IO並不是馬上寫入文件,而是批量Flush。同樣數據庫記錄日志也是一樣,像Unit Of Work那樣,批量向數據庫寫入數據,提高它的吞吐率。

根據需求使用不同的日志類

LogFactory.Instance.Resolve<FileLogStrategy>().Log("Welcome");

小結

不同於服務器端的日志組件,比如Log4J,只需要將日志寫在本地文件系統中,客戶端的日志相對來說復雜點,因為記錄的日志是發生在用戶的客戶端,所以你必須要想辦法把日志傳到服務器,比如一些Crash的異常。既然要把日志發回來,在應用閃退時,必須能夠持久化到本地,故我們會將日志寫到文件系統或者數據庫,然后在合適的時候將日志發送到服務器進行分析。當然,你也可以使用第三方的服務,比如友盟或者 Unity Analytics 來分析數據。
源代碼托管在Github上,點擊此了解

我的博客即將搬運同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM