自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

前端日志管理模塊的設(shè)計(jì)與實(shí)現(xiàn)

開發(fā) 前端
給團(tuán)隊(duì)封裝一個(gè)簡單統(tǒng)一的日志管理工具/模塊,來替換掉項(xiàng)目中野蠻生長的 console.log()吧!

一、問題背景

在項(xiàng)目中,我們會(huì)頻繁用到 ??console.log()?? 來輸出一些關(guān)鍵信息到控制臺(tái)中,有助于開發(fā)調(diào)試,以及問題的排查,待項(xiàng)目上線后,這些調(diào)試日志又得及時(shí)清除。

同時(shí)在前端質(zhì)量要求下,我們會(huì)做“前端埋點(diǎn)”,用于遠(yuǎn)程上報(bào)一些關(guān)鍵行為信息,用于在出問題時(shí)還原用戶的操作路徑,復(fù)現(xiàn) BUG,從而解決問題,而各種各樣的上報(bào)若是能在業(yè)務(wù)開發(fā)中抹平差異,也有助于研發(fā)提效。

因此,有必要在團(tuán)隊(duì)中封裝日志工具(Logger),用于統(tǒng)一管理日志輸出和格式化上報(bào),降低開發(fā)者對(duì)多平臺(tái)上報(bào)差異的心智負(fù)擔(dān)。

二、需求概述

預(yù)期日志管理工具(Logger)需要有如下能力:

  1. 支持區(qū)分??info???、??warn???、??error?? 三種本地調(diào)試類型日志
  2. 支持遠(yuǎn)程上報(bào)自定義日志??report()??
  3. 支持設(shè)置 namespace,用于區(qū)分代碼執(zhí)行的 scope
  4. 支持鏈?zhǔn)讲僮?/li>
  5. 區(qū)分生產(chǎn)環(huán)境和開發(fā)環(huán)境,生產(chǎn)環(huán)境禁止輸出日志到控制臺(tái)
  6. 支持功能可擴(kuò)展

三、方案設(shè)計(jì)

在閱讀完 Axios 的源碼后,個(gè)人認(rèn)為 Axios 里對(duì)于設(shè)計(jì)模式的應(yīng)用是非常靈活,同理,一個(gè)好的日志工具也應(yīng)當(dāng)遵守著一定的軟件設(shè)計(jì)模式原則。

作為項(xiàng)目中用到的日志工具,單例模式應(yīng)當(dāng)是更適合的選擇!

Logger 的打印輸出能力,本質(zhì)上還是借助了 ??window.console?? 對(duì)象中的方法:

圖片Console 對(duì)象

在面向?qū)ο缶幊讨?,我們可以認(rèn)為 ??console?? 是一個(gè)已經(jīng)初始化的實(shí)例,同時(shí)也是一個(gè)單例,因?yàn)樗侨治ㄒ弧?/p>

而單例模式的最大好處就是全局唯一,對(duì)于做日志統(tǒng)一管理有著天然的友好支持基礎(chǔ)。

四、實(shí)現(xiàn)細(xì)節(jié) ??

接下來通過具體的代碼,來逐一實(shí)現(xiàn)并完善我們的 Logger 日志工具類。

1、ES Module 下的單例模式

在 ESM 規(guī)范下,我們可以直接通過直接導(dǎo)出實(shí)例方式(??export default new ClassName()??),來實(shí)現(xiàn)單例模式。

Logger 的基礎(chǔ)結(jié)構(gòu)就有了:

/**
* 日志打印工具,統(tǒng)一管理日志輸出&上報(bào)
*/
class Logger {
/** 命名空間(scope),用于區(qū)分所在執(zhí)行文件 */
private namespace: string

constructor(namespace = 'unknown') {
this.namespace = namespace
}
}
export default new Logger()

2、可擴(kuò)展的單例模式

參考 Axios 的設(shè)計(jì)[1],因此我們還提供 ??create()?? 方法,為創(chuàng)建新實(shí)例留一個(gè)入口方法。

/**
* 創(chuàng)建新的 Logger 實(shí)例
*
* @param namespace 命名空間
* @returns Logger
*/
public create(namespace = 'unknown') {
return new Logger(namespace);
}

當(dāng)需要重新定義一個(gè) logger 實(shí)例時(shí),就可以參考如下方式:

import logger from '@/utils/logger'
const newLogger = logger.create('custom')
logger.info(newLogger === logger) // [unknown] false

3、定義“打印”類日志方法

需要區(qū)分 ??info???、??warn???、??error?? 三種類型的日志,實(shí)現(xiàn)如下:

定義日志枚舉類型:

const enum LogLevel {
/** 普通日志 */
Log,
/** 警告日志 */
Warning,
/** 錯(cuò)誤日志 */
Error,
}

const Styles = ['color: green;', 'color: orange;', 'color: red;']
const Methods = ['info', 'warn', 'error'] as const
private _log(level: LogLevel, args: unknown[]) {
if (!__DEV__) return
console[Methods[level]](`%c${this.namespace}`, Styles[level], ...args)
}
/**
* 打印輸出信息 ??
*
* @param args 任意參數(shù)
*/
public info(...args: unknown[]) {
this._log(LogLevel.Log, args)
return this
}
/**
* 打印輸出警告信息 ?
*
* @param args 任意參數(shù)
*/
public warn(...args: unknown[]) {
this._log(LogLevel.Warning, args)
return this
}
/**
* 打印輸出錯(cuò)誤信息 ?
*
* @param args 任意參數(shù)
*/
public error(...args: unknown[]) {
this._log(LogLevel.Error, args)
return this
}

在 ??_log()??? 方法中,通過 ??__DEV__?? 環(huán)境變量區(qū)分“生產(chǎn)”和“開發(fā)”:

if (!__DEV__) return

這種變量可以理解為“開關(guān)”:

生產(chǎn)環(huán)境則控制臺(tái)不輸出信息,在實(shí)際應(yīng)用中,可以擴(kuò)展“是否輸出信息”的變量,來針對(duì)性擴(kuò)展,例如線上需要通過特定參數(shù)展示調(diào)試日志,用于線上定位問題,那么就可以綜合多個(gè)條件來決定是否輸出控制臺(tái),畢竟編程最核心的問題是解決需求。

在開發(fā)模式下,針對(duì)不同的信息類型,會(huì)標(biāo)注不同的顏色:

圖片

圖片Chrome 瀏覽器下的效果

與此同時(shí),在每個(gè)“輸出”方法中都返回了 ??this??(當(dāng)前實(shí)例),因而便可以為鏈?zhǔn)秸{(diào)用方法提供了使用基礎(chǔ)。

4、支持修改 namespace

namespace 最重要的作用是:區(qū)分在不同組件或文件下的日志,便于問題定位排查。

由于 ??Logger??? 將所有的輸出集中到了統(tǒng)一文件,在 ??console.log()??? 中文件定位永遠(yuǎn)是 ??Logger?? 類定義實(shí)現(xiàn)所在文件,因此需要 namespace 來區(qū)分。

新增 ??setNamespace()?? 方法:

/**
* 設(shè)置命名空間(日志前綴)
* @param namespace
*/
public setNamespace(namespace = '') {
this.namespace = `[${namespace}]`
return this
}

在 TypeScript 環(huán)境下,會(huì)提供代碼提示,例如某個(gè)文件下輸出錯(cuò)誤信息的方式:

圖片

而 ??setNamespace()?? 方法,并不是每次都需要調(diào)用的,只需在文件中調(diào)用一次即可。

5、埋點(diǎn)遠(yuǎn)程上報(bào)

在一些關(guān)鍵時(shí)機(jī),例如進(jìn)入頁面、點(diǎn)擊“付費(fèi)按鈕”等一些關(guān)鍵操作上,一般會(huì)加上一些上報(bào)到遠(yuǎn)程,用于記錄用戶操作路徑,以此便于在出現(xiàn)問題后,復(fù)現(xiàn) BUG 并“對(duì)癥下藥”。

而埋點(diǎn)上報(bào)一般有三類:代碼埋點(diǎn)、可視化埋點(diǎn)、無痕埋點(diǎn)。

我們這里通過給 Logger 增加遠(yuǎn)程上報(bào)的方式就是代碼埋點(diǎn)。

一般情況下,埋點(diǎn)上報(bào)屬于“前端監(jiān)控”方面,前端監(jiān)控是一個(gè)獨(dú)立的管理系統(tǒng),它的職能是負(fù)責(zé)前端項(xiàng)目的監(jiān)控、異常報(bào)警等,因此通常會(huì)有用于項(xiàng)目集成的前端 SDK

有了 Logger 實(shí)例,我們可以在 Logger 中直接統(tǒng)一集成“前端監(jiān)控 SDK”的主動(dòng)上報(bào)方法即可!

在 Logger 類中新增三個(gè)方法:

  • ??reportLog()??:上報(bào)日志。
  • ??reportEvent()??:上報(bào)事件。
  • ??reportException()??:上報(bào)異常。
/**
* 遠(yuǎn)程上報(bào)
* TODO: 根據(jù)基建環(huán)境自定義擴(kuò)展
*/
public reportLog() {
this.info() // 用于在本地輸出
}
public reportEvent() {
this.info()
}
public reportException() {
this.error()
}

至于為什么添加著兩個(gè)方法,實(shí)際是根據(jù)“前端監(jiān)控 SDK”提供的 API 來決定

例如常見的 “Sentry - 應(yīng)用監(jiān)控錯(cuò)誤溯源[2]” 平臺(tái),針對(duì)主動(dòng)上報(bào),提供了三種方法,通常為了保持一致性,降低心智負(fù)擔(dān),因此新增對(duì)應(yīng)的三個(gè)上報(bào)方法。

具體的上報(bào)參數(shù)和邏輯,則需要大家根據(jù)自己的業(yè)務(wù)區(qū)擴(kuò)展。

五、Logger 的可擴(kuò)展性 ??

從上面 Logger 類的實(shí)現(xiàn),可以發(fā)現(xiàn)一個(gè)明顯的問題,如果業(yè)務(wù)需要擴(kuò)展功能,則需要修改 Logger 類內(nèi)部的方法,Logger 類中的方法和邏輯,我們可以理解為是所有業(yè)務(wù)都通用的,業(yè)務(wù)定制化的功能應(yīng)該通過額外擴(kuò)展方式來完善。

那有沒有什么辦法,可以實(shí)現(xiàn)不修改方法,而擴(kuò)展 Logger 的功能吶?

1、擴(kuò)展方案

有幾個(gè)方案:

  1. 繼承 Logger 類擴(kuò)展。
  2. 增加回調(diào)函數(shù)作為參數(shù)。

個(gè)人推薦第二個(gè)方案,但如果每一次調(diào)用,都按照如下方式:

logger.info('message', () => {})

但這種設(shè)計(jì)比較粗糙

2、攔截器

參考 Axios 的攔截器設(shè)計(jì),也就是 AOP(面向切面編程模式)的設(shè)計(jì)思想,來擴(kuò)展 ??_log()?? 方法。

新增類型申明:

/**
* 日志的配置類型
*/
type LoggerConfigType = {
/** 命名空間 */
namespace?: string
}
/**
* 攔截器函數(shù)類型
*/
type InterceptorFuncType = (config: LoggerConfigType) => void

將 Logger 的配置集中的 ??config??? 私有變量中,并新增 ??addBeforeFunc()??? 和 ??addAfterFunc()?? 兩個(gè)方法,用于新增自定義“攔截器”函數(shù)

圖片

其中一個(gè)細(xì)節(jié)是,日志打印之后的攔截器,按照FCLS(First Come Last Serve,先到后服務(wù))的策略,和 Axios 的響應(yīng)攔截器執(zhí)行順序?qū)R,與此同時(shí),攔截器函數(shù)中會(huì)注入當(dāng)前 Logger 的 ??config?? 配置。

通過簡單的“攔截器”,即可實(shí)現(xiàn)功能的擴(kuò)展,這種方式的功能擴(kuò)展不會(huì)影響到主體功能,后期的維護(hù)升級(jí)是無侵入性的,還算比較優(yōu)雅的,是吧!

3、其他方案

這里還可以考慮更多設(shè)計(jì),例如參考發(fā)布訂閱設(shè)計(jì)模式來改造,通過生命周期的關(guān)鍵點(diǎn),被動(dòng)觸發(fā),主動(dòng)通知并執(zhí)行所有訂閱了對(duì)應(yīng)消息的事件,可以參閱《聊一聊發(fā)布訂閱設(shè)計(jì)模式[3]

也可以用插件模式方式來實(shí)現(xiàn)擴(kuò)展,類似發(fā)布訂閱模式,給 ??_log()?? 函數(shù)添加執(zhí)行的鉤子函數(shù)??(回調(diào)函數(shù)),例如這種設(shè)計(jì)下,把“埋點(diǎn)上報(bào)”等功能拆分成插件,再實(shí)現(xiàn)一個(gè)簡單的事件隊(duì)列模型,集成一下子!

六、總結(jié)

至此,一個(gè)基本的日志工具就實(shí)現(xiàn)完成了,但并未完完全全遵守設(shè)計(jì)原則,這里在生產(chǎn)實(shí)踐中還需要封裝、抽離相應(yīng)“職責(zé)”,增加可維護(hù)性。

在團(tuán)隊(duì)中以此作為基礎(chǔ)結(jié)構(gòu),然后針對(duì)團(tuán)隊(duì)、項(xiàng)目、業(yè)務(wù)的特點(diǎn)做適當(dāng)?shù)臄U(kuò)展,構(gòu)建符合當(dāng)前團(tuán)隊(duì)特性的通用日志工具模塊,應(yīng)該也不是什么難事!

責(zé)任編輯:姜華 來源: DYBOY
相關(guān)推薦

2023-12-07 07:02:00

大倉權(quán)限設(shè)計(jì)

2010-02-26 13:14:39

Java日志系統(tǒng)

2013-08-20 15:31:18

前端模塊化

2012-08-20 10:24:15

ASP.NET

2025-04-27 01:05:00

AI智能日志

2022-09-13 15:33:48

KubeEdge邊緣計(jì)算容器

2017-07-26 14:50:37

前端模板

2016-09-29 09:57:08

JavascriptWeb前端模板

2015-08-20 10:23:23

前端代碼日志收集

2014-04-27 10:16:31

QCon北京2014Andrew Bett

2019-05-16 09:00:06

云原生監(jiān)控日志管理

2012-09-06 09:31:14

PHPmysqlpdo

2009-08-13 10:19:43

Linux系統(tǒng)Nagios網(wǎng)絡(luò)管理模塊

2011-03-21 11:14:23

LinuxNagios

2020-10-19 10:01:12

Nodejs線程池設(shè)計(jì)

2022-12-28 08:31:38

平臺(tái)設(shè)計(jì)應(yīng)用

2020-09-17 10:30:21

前端模塊化組件

2020-10-23 08:31:15

Nodejs-Ipc設(shè)計(jì)實(shí)現(xiàn)

2015-06-30 11:05:11

flexibleWebAPP設(shè)計(jì)

2015-11-03 09:28:52

Hybrid技術(shù)設(shè)計(jì)實(shí)現(xiàn)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)