成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

輕松打造高效日志系統

系統 開發
本文介紹了如何設計并實現高效日志系統,介紹了一個有效的日志系統需要考慮的關鍵問題,強調了日志在系統調試和監控中的重要性。

作為開發者,經常需要在調試時查看檢查日志,缺乏日志或者不清楚如何通過日志分析問題,就無法定位出錯的代碼。

對于每天為成千上萬甚至上百萬用戶提供服務的系統來說,日志必不可少,因為:

  • 日志可以幫助我們找到影響最終用戶的錯誤。
  • 日志可以跟蹤系統的 "健康狀況",在系統出問題之前察覺到某些 "異常跡象"。
  • ……等等

由此可見,在開發或運行系統時,日志至關重要,因此,設計和實施完善的日志系統有助于簡化監控工作。

本文將分享我在設計和構建日志系統方面的經驗和理解。希望通過這篇文章,你能:

  • 了解在操作系統中記錄日志的重要性。
  • 可以作為實施日志系統時的參考。

一、日志策略

下面列出了我們在實施日志系統之前應該問自己的問題。

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

二、日志級別

了解日志的目的后,應該對日志進行分級

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


三、案例

定義日志級別后,必須明確要輸出的日志類型。

本節將針對每種日志類型回答以下六個問題。

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

1. 系統日志(System Log)

(1) Why: 當系統出現錯誤時,系統日志將用于調試。

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

(3) When:出錯時輸出日志。

(4) Where:

  • FATAL / ERROR:通知開發人員立即處理。
  • WARN / INFO:在系統或日志管理工具中輸出。
  • DEBUG / TRACE:輸出到預發環境中的 console.log。

(5) What:

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

(6) How:

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

2. 訪問日志(Access Log)

  • Why: 輸出日志以跟蹤發送和接收請求的過程。
  • Who: 系統本身或基礎設施。
  • When: 在發送或接收請求時輸出。
  • Where: 在 INFO 級別和拉模式中。由于日志量可能很大,必須注意查找日志的速度。
  • What: 輸出誰、如何、何時進入系統。
  • How: 根據目的不同,可能會有一些差異。

3. 操作日志(Action Log)

  • Why: 分析用戶操作,從而在此基礎上改進服務。
  • Who: 系統本身或外部工具。
  • When: 某些操作發生時。
  • Where: 日志分析工具(BigQuery 等)。
  • What: 取決于目的。
  • How: 根據目的不同,可能會有一些差異。

4. 認證日志(Auth Log)

  • Why: 跟蹤用戶驗證的輸出。
  • Who: 系統本身。
  • When: 驗證用戶。
  • Where: 在 INFO 級別和拉模式中。
  • What: 輸出認證的時間、用戶、方式。
  • How: 根據認證方法不同,可能會有一些差異。

四、示例

概念就介紹到這里,下面來看一個示例項目。

有關代碼的更多詳情,請參閱Github[2]。

1. 選擇日志庫

我選擇 log4js[3] 庫,原因很簡單,因為 log4js 構建日志級別的方式與我的想法一致。

2. 實施

步驟 1 - 定義日志類

首先定義日志類:

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 類中定義了日志級別:

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

基于此,我又定義了日志類型:

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

第 2 步 - 將 Logger 應用到項目中

將 Logger 類應用到由 NestJS[4] 框架實現的項目中。

通過 NestJS 的 Interceptor(攔截器[5])功能,將日志類注入到項目中。

選擇 Interceptor 的原因是 NestJS 攔截器不僅能封裝請求流,還能封裝從 API 輸入和輸出的響應流,因此使用攔截器是捕獲請求日志和響應日志的最簡單方法。我是這樣定義 LoggerInterceptor 類的:

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: 用于記錄請求信息。
  • responseLogger: 用于記錄響應信息。
  • responseErrorLogger: 用于記錄錯誤信息。

像這樣:

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 后,將此攔截器應用到應用程序中:

const app = await NestFactory.create(AppModule);

app.useGlobalInterceptors(new LoggerInterceptor());

在 NestJS 應用程序中應用自定義攔截器并不難,因為這是 NestJS 的內置功能。

對于 fatal 和 debug 日志,我將在用例層或基礎架構層中使用,以達到以下目的:

  • 通知無法連接數據庫等致命錯誤。
  • 當用戶遇到問題時進行調試。

只要這樣做:

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

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

結果如下:

首先是訪問請求日志和響應日志(當沒有發生錯誤時)。

可以看到,與請求相關的信息,如 method、body 等都已清晰顯示。

如果出錯:

同時顯示錯誤類型和錯誤信息。

fatal 日志會是這樣的:

同樣會輸出錯誤信息和錯誤類型。

五、結論

本文分享了如何設計和實施一個基本的日志系統。

通過簡單的示例,希望你能理解建立日志系統的重要性和必要性,這將有助于系統的運行和調試。

參考資料:

  • [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
責任編輯:趙寧寧 來源: DeepNoMind
相關推薦

2023-11-16 08:53:05

NumPy庫Python

2011-01-19 10:42:15

2024-01-03 10:03:26

PythonTCP服務器

2010-06-23 11:41:00

高校企業高效數據中心

2019-12-12 09:30:31

工具代碼開發

2012-08-06 11:55:10

災備系統深信服易寶支付

2025-01-03 09:34:54

2009-05-05 13:19:53

戴爾高效企業

2010-02-22 15:00:47

2010-01-11 15:12:30

VB.NET特殊窗體

2014-10-27 14:09:01

華為

2010-05-12 15:39:49

IT運維信息化建設北塔

2013-08-13 14:11:39

2009-12-11 15:37:58

Linux日志處理

2025-05-12 10:02:05

2010-01-14 11:00:48

VB.NET文件合并

2011-11-02 10:13:46

激光打印機評測

2014-10-27 18:13:02

ITSM

2024-06-13 10:52:43

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产福利视频 | av网站在线播放 | 亚洲高清免费观看 | 天天搞天天操 | 成人av在线播放 | 国产三级电影网站 | 成人免费在线网 | 久久久精品网 | 高清一区二区三区 | 中文在线一区二区 | 久久精品视频免费看 | 一区二区在线不卡 | 欧美高清免费 | 青青久久久 | 久久国内精品 | 欧美精品一区二区免费视频 | 亚洲三级免费看 | 成人亚洲一区 | 欧美日高清视频 | 欧美一区二区三区在线看 | 日本精品一区二区三区视频 | 国产日韩欧美激情 | 欧美日韩黄色一级片 | 国产精品18久久久久久久 | 成人精品福利 | 日韩精品一区二区三区在线播放 | 综合激情久久 | 国产二区三区 | 伊人狼人影院 | 91社影院在线观看 | 嫩草懂你的影院入口 | 日韩精品一区二区三区免费观看 | 免费观看一级视频 | 亚洲精品成人网 | 岛国视频 | 国产欧美精品一区二区 | 欧美一级电影免费 | 麻豆精品一区二区三区在线观看 | 日韩一区在线播放 | 羞羞网站在线观看 | 国产成人福利在线观看 |