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

輕松打造高效日志系統(tǒng)

系統(tǒng) 開(kāi)發(fā)
本文介紹了如何設(shè)計(jì)并實(shí)現(xiàn)高效日志系統(tǒng),介紹了一個(gè)有效的日志系統(tǒng)需要考慮的關(guān)鍵問(wèn)題,強(qiáng)調(diào)了日志在系統(tǒng)調(diào)試和監(jiān)控中的重要性。

作為開(kāi)發(fā)者,經(jīng)常需要在調(diào)試時(shí)查看檢查日志,缺乏日志或者不清楚如何通過(guò)日志分析問(wèn)題,就無(wú)法定位出錯(cuò)的代碼。

對(duì)于每天為成千上萬(wàn)甚至上百萬(wàn)用戶(hù)提供服務(wù)的系統(tǒng)來(lái)說(shuō),日志必不可少,因?yàn)椋?/p>

  • 日志可以幫助我們找到影響最終用戶(hù)的錯(cuò)誤。
  • 日志可以跟蹤系統(tǒng)的 "健康狀況",在系統(tǒng)出問(wèn)題之前察覺(jué)到某些 "異常跡象"。
  • ……等等

由此可見(jiàn),在開(kāi)發(fā)或運(yùn)行系統(tǒng)時(shí),日志至關(guān)重要,因此,設(shè)計(jì)和實(shí)施完善的日志系統(tǒng)有助于簡(jiǎn)化監(jiān)控工作。

本文將分享我在設(shè)計(jì)和構(gòu)建日志系統(tǒng)方面的經(jīng)驗(yàn)和理解。希望通過(guò)這篇文章,你能:

  • 了解在操作系統(tǒng)中記錄日志的重要性。
  • 可以作為實(shí)施日志系統(tǒng)時(shí)的參考。

一、日志策略

下面列出了我們?cè)趯?shí)施日志系統(tǒng)之前應(yīng)該問(wèn)自己的問(wèn)題。

  • Why(為什么):日志記錄的目的是什么?
  • Who(誰(shuí)): 哪個(gè)模塊將生成日志?
  • When(何時(shí)):何時(shí)輸出日志?
  • Where(哪里):在哪里輸出日志(發(fā)送到 Slack 或 BigQuery 等)
  • What(什么):日志能提供什么信息?
  • How(如何): 如何輸出日志?

二、日志級(jí)別

了解日志的目的后,應(yīng)該對(duì)日志進(jìn)行分級(jí)

Log level

Concept

How to handle

Example

FATAL

This Level hinder the operating of the system

Have to fix immediately

Can not connect to the DB

ERROR

Unexpected errors occur

Should be fixed as soon as you can

Can not send the email

WARN

Not an error, but are some problems like unexpected input or unexpected executing unexpected input or unexpected executing

Should be refactored regularly

Regularly delete data API

INFO

Notification when starting or ending an executing or a transaction.

Maybe outputting another needed information

Do not need to fix Output the body of the request or response

DEBUG

The information that relating to system status

Do not output in the production environment

Can be put inside a function

TRACE

Information that is more detailed than DEBUG

Do not output in the production environment


三、案例

定義日志級(jí)別后,必須明確要輸出的日志類(lèi)型。

本節(jié)將針對(duì)每種日志類(lèi)型回答以下六個(gè)問(wèn)題。

  • Why(為什么)
  • Who(誰(shuí))
  • When(何時(shí))
  • Where(哪里)
  • What(什么)
  • How(如何)

1. 系統(tǒng)日志(System Log)

(1) Why: 當(dāng)系統(tǒng)出現(xiàn)錯(cuò)誤時(shí),系統(tǒng)日志將用于調(diào)試。

(2) Who: 系統(tǒng)本身將輸出日志。

(3) When:出錯(cuò)時(shí)輸出日志。

(4) Where:

  • FATAL / ERROR:通知開(kāi)發(fā)人員立即處理。
  • WARN / INFO:在系統(tǒng)或日志管理工具中輸出。
  • DEBUG / TRACE:輸出到預(yù)發(fā)環(huán)境中的 console.log。

(5) What:

  • FATAL / ERROR:堆棧跟蹤。
  • WARN / INFO / DEBUG/ TRACE:要通知的內(nèi)容。

(6) How:

  • FATAL / ERROR:通過(guò)日志管理工具或 Slack、SMS......(推模式)輸出。
  • WARN / INFO / DEBUG / TRACE:通過(guò)日志管理工具或系統(tǒng)內(nèi)部輸出(拉模式)。

2. 訪問(wèn)日志(Access Log)

  • Why: 輸出日志以跟蹤發(fā)送和接收請(qǐng)求的過(guò)程。
  • Who: 系統(tǒng)本身或基礎(chǔ)設(shè)施。
  • When: 在發(fā)送或接收請(qǐng)求時(shí)輸出。
  • Where: 在 INFO 級(jí)別和拉模式中。由于日志量可能很大,必須注意查找日志的速度。
  • What: 輸出誰(shuí)、如何、何時(shí)進(jìn)入系統(tǒng)。
  • How: 根據(jù)目的不同,可能會(huì)有一些差異。

3. 操作日志(Action Log)

  • Why: 分析用戶(hù)操作,從而在此基礎(chǔ)上改進(jìn)服務(wù)。
  • Who: 系統(tǒng)本身或外部工具。
  • When: 某些操作發(fā)生時(shí)。
  • Where: 日志分析工具(BigQuery 等)。
  • What: 取決于目的。
  • How: 根據(jù)目的不同,可能會(huì)有一些差異。

4. 認(rèn)證日志(Auth Log)

  • Why: 跟蹤用戶(hù)驗(yàn)證的輸出。
  • Who: 系統(tǒng)本身。
  • When: 驗(yàn)證用戶(hù)。
  • Where: 在 INFO 級(jí)別和拉模式中。
  • What: 輸出認(rèn)證的時(shí)間、用戶(hù)、方式。
  • How: 根據(jù)認(rèn)證方法不同,可能會(huì)有一些差異。

四、示例

概念就介紹到這里,下面來(lái)看一個(gè)示例項(xiàng)目。

有關(guān)代碼的更多詳情,請(qǐng)參閱Github[2]。

1. 選擇日志庫(kù)

我選擇 log4js[3] 庫(kù),原因很簡(jiǎn)單,因?yàn)?log4js 構(gòu)建日志級(jí)別的方式與我的想法一致。

2. 實(shí)施

步驟 1 - 定義日志類(lèi)

首先定義日志類(lèi):

class Logger {
  public default: log4js.Logger;
  public system: log4js.Logger;
  public api: log4js.Logger;
  public access_req: log4js.Logger;
  public access_res: log4js.Logger;
  public sql: log4js.Logger;
  public auth: log4js.Logger;

  public fatal: log4js.Logger;
  public error: log4js.Logger;
  public warn: log4js.Logger;
  public info: log4js.Logger;
  public debug: log4js.Logger;
  public trace: log4js.Logger;

constructor() {
    log4js.configure(loggerConfig);

    this.system = log4js.getLogger('system');
    this.api = log4js.getLogger('api');
    this.access_req = log4js.getLogger('access_req');
    this.access_res = log4js.getLogger('access_res');
    this.sql = log4js.getLogger('sql');
    this.auth = log4js.getLogger('auth');

    this.fatal = log4js.getLogger('fatal');
    this.fatal.level = log4js.levels.FATAL;

    this.error = log4js.getLogger('error');
    this.error.level = log4js.levels.ERROR;

    this.warn = log4js.getLogger('warn');
    this.warn.level = log4js.levels.WARN;

    this.info = log4js.getLogger('info');
    this.info.level = log4js.levels.INFO;

    this.debug = log4js.getLogger('debug');
    this.debug.level = log4js.levels.DEBUG;

    this.trace = log4js.getLogger('trace');
    this.trace.level = log4js.levels.TRACE;
  }
}

在 Logger 類(lèi)中定義了日志級(jí)別:

  • fatal
  • error
  • warn
  • info
  • debug
  • trace

基于此,我又定義了日志類(lèi)型:

  • system
  • api
  • access_req
  • access_res
  • sql
  • auth

第 2 步 - 將 Logger 應(yīng)用到項(xiàng)目中

將 Logger 類(lèi)應(yīng)用到由 NestJS[4] 框架實(shí)現(xiàn)的項(xiàng)目中。

通過(guò) NestJS 的 Interceptor(攔截器[5])功能,將日志類(lèi)注入到項(xiàng)目中。

選擇 Interceptor 的原因是 NestJS 攔截器不僅能封裝請(qǐng)求流,還能封裝從 API 輸入和輸出的響應(yīng)流,因此使用攔截器是捕獲請(qǐng)求日志和響應(yīng)日志的最簡(jiǎn)單方法。我是這樣定義 LoggerInterceptor 類(lèi)的:

export class LoggerInterceptor implements NestInterceptor {
  intercept(
    context: ExecutionContext,
    next: CallHandler<any>
  ): Observable<any> | Promise<Observable<any>> {
    // intercept() method will "wrap" request/ response stream

    /*
     * Get request object from context
     * After that, pass request object to "requestLogger" function
     * to output the log
     */
    const request = context.switchToHttp().getRequest();
    requestLogger(request);

    /*
     * Get response object from context
     * After that pass response object to "responseLogger" & "responseErrorLogger" functions for ouputting the log or
     * error log
     */
    const response = context.switchToHttp().getResponse();

    return next.handle().pipe(
      // 200 - Success Response
      map((data) => {
        responseLogger({ requestId: request._id, response, data });
      }),
      // 4xx, 5xx - Error Response
      tap(null, (exception: HttpException | Error) => {
        try {
          responseErrorLogger({ requestId: request._id, exception });
        } catch (e) {
          logger.access_res.error(e);
        }
      })
    );
  }
}

定義了三種方法:

  • requestLogger: 用于記錄請(qǐng)求信息。
  • responseLogger: 用于記錄響應(yīng)信息。
  • responseErrorLogger: 用于記錄錯(cuò)誤信息。

像這樣:

const MaskField = {
Email: 'email',
Password: 'password',
} asconst;

type MaskField = (typeof MaskField)[keyof typeof MaskField];

const _maskFields = (object: FixType, fields: MaskField[]): FixType => {
const maskOptions = {
    maskWith: '*',
    unmaskedStartCharacters: 0,
    unmaskedEndCharacters: 0,
  };

for (let i = 0; i < fields.length; i++) {
    switch (fields[i]) {
      case MaskField.Email: {
        object[MaskField.Email] = maskData.maskEmail2(
          object[MaskField.Email],
          maskOptions
        );
      }
      case MaskField.Password: {
        object[MaskField.Password] = maskData.maskPassword(
          object[MaskField.Password],
          maskOptions
        );
      }
    }
  }

return object;
};

exportconst requestLogger = (request: Request) => {
const { ip, originalUrl, method, params, query, body, headers } = request;

// logTemplate includes: now(time), ip, http_method, url, request_object
const logTemplate = '%s %s %s %s %s';
const now = dayjs().format('YYYY-MM-DD HH:mm:ss.SSS');

const logContent = util.formatWithOptions(
    { colors: true },
    logTemplate,
    now,
    ip,
    method,
    originalUrl,
    JSON.stringify({
      method,
      url: originalUrl,
      userAgent: headers['user-agent'],
      body: _maskFields(body, [MaskField.Email, MaskField.Password]),
      params,
      query,
    })
  );

// Using access_req logger object have been defined before.
  logger.access_req.info(logContent);
};

// Ouptput success response log
exportconst responseLogger = (input: {
requestId: number;
  response: Response;
  data: any;
}) => {
const { requestId, response, data } = input;

const log: ResponseLog = {
    requestId,
    statusCode: response.statusCode,
    data,
  };

// Using access_res logger object have been defined before.
  logger.access_res.info(JSON.stringify(log));
};

// Ouptput error response log
exportconst responseErrorLogger = (input: {
requestId: number;
  exception: HttpException | Error;
}) => {
const { requestId, exception } = input;

const log: ResponseLog = {
    requestId,
    statusCode:
      exception instanceof HttpException ? exception.getStatus() : null,
    message: exception?.stack || exception?.message,
  };

// Using access_res logger object have been defined before.
  logger.access_res.info(JSON.stringify(log));
  logger.access_res.error(exception);
};

定義完 LoggerInterceptor 后,將此攔截器應(yīng)用到應(yīng)用程序中:

const app = await NestFactory.create(AppModule);

app.useGlobalInterceptors(new LoggerInterceptor());

在 NestJS 應(yīng)用程序中應(yīng)用自定義攔截器并不難,因?yàn)檫@是 NestJS 的內(nèi)置功能。

對(duì)于 fatal 和 debug 日志,我將在用例層或基礎(chǔ)架構(gòu)層中使用,以達(dá)到以下目的:

  • 通知無(wú)法連接數(shù)據(jù)庫(kù)等致命錯(cuò)誤。
  • 當(dāng)用戶(hù)遇到問(wèn)題時(shí)進(jìn)行調(diào)試。

只要這樣做:

logger.fatal.error('Error message');

可以將 fatal 日志輸出到控制臺(tái)或 Slack 等通知管道......

結(jié)果如下:

首先是訪問(wèn)請(qǐng)求日志和響應(yīng)日志(當(dāng)沒(méi)有發(fā)生錯(cuò)誤時(shí))。

可以看到,與請(qǐng)求相關(guān)的信息,如 method、body 等都已清晰顯示。

如果出錯(cuò):

同時(shí)顯示錯(cuò)誤類(lèi)型和錯(cuò)誤信息。

fatal 日志會(huì)是這樣的:

同樣會(huì)輸出錯(cuò)誤信息和錯(cuò)誤類(lèi)型。

五、結(jié)論

本文分享了如何設(shè)計(jì)和實(shí)施一個(gè)基本的日志系統(tǒng)。

通過(guò)簡(jiǎn)單的示例,希望你能理解建立日志系統(tǒng)的重要性和必要性,這將有助于系統(tǒng)的運(yùn)行和調(diào)試。

參考資料:

  • [1] Design And Building A Logging System: https://levelup.gitconnected.com/design-and-building-a-logging-system-fd5dcad110ed
  • [2] NewAnigram-BE-DDD: https://github.com/tuananhhedspibk/NewAnigram-BE-DDD
  • [3] log4js: https://github.com/log4js-node/log4js-node
  • [4] NestJS: https://docs.nestjs.com
  • [5] NestJS Interceptor: https://docs.nestjs.com/interceptors
責(zé)任編輯:趙寧寧 來(lái)源: DeepNoMind
相關(guān)推薦

2023-11-16 08:53:05

NumPy庫(kù)Python

2011-01-19 10:42:15

2024-01-03 10:03:26

PythonTCP服務(wù)器

2010-06-23 11:41:00

高校企業(yè)高效數(shù)據(jù)中心

2009-12-11 15:37:58

Linux日志處理

2012-08-06 11:55:10

災(zāi)備系統(tǒng)深信服易寶支付

2025-01-03 09:34:54

2009-05-05 13:19:53

戴爾高效企業(yè)

2010-02-22 15:00:47

2019-12-12 09:30:31

工具代碼開(kāi)發(fā)

2010-01-11 15:12:30

VB.NET特殊窗體

2013-08-13 14:11:39

2014-10-27 14:09:01

華為

2010-05-12 15:39:49

IT運(yùn)維信息化建設(shè)北塔

2011-11-02 10:13:46

激光打印機(jī)評(píng)測(cè)

2010-01-14 11:00:48

VB.NET文件合并

2024-06-13 10:52:43

2024-04-12 08:17:26

考勤系統(tǒng)員工人臉?shù)浫?/a>OpenCV

2018-09-27 08:59:29

點(diǎn)贊
收藏

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