Node Framework를 ELK에 연결하는 과정 요약

不言
풀어 주다: 2018-11-21 11:23:21
앞으로
2471명이 탐색했습니다.

이 글의 내용은 Node 프레임워크를 ELK에 연결하는 과정을 요약한 것입니다. 참고할 만한 가치가 있으니 도움이 필요한 분들에게 도움이 되길 바랍니다.

우리 모두는 클러스터 수가 증가하면 이러한 원시적인 작업으로 인한 비효율성으로 인해 기존 네트워크 문제를 찾는 데 큰 어려움을 겪을 뿐만 아니라 이를 수행하는 것도 불가능하게 됩니다. 목표한 최적화 및 개선은 물론, 서비스 프레임워크의 다양한 지표에 대한 효과적인 정량적 진단을 수행하는 것도 불가능합니다. 이때 정보검색, 서비스 진단, 데이터 분석 등의 기능을 갖춘 실시간 로그 모니터링 시스템을 구축하는 것이 특히 중요하다.

ELK(ELK 스택: ElasticSearch, LogStash, Kibana, Beats)는 오픈 소스와 높은 성능을 갖춘 성숙한 로깅 솔루션입니다. 우리 사업에서 사용하는 서비스 프레임워크는 ELK 시스템과 어떻게 연결되나요?

비즈니스 배경

비즈니스 프레임워크 배경:

  • 비즈니스 프레임워크는 NodeJs WebServer를 기반으로 합니다.

  • 서비스는 Winston 로그 모듈을 사용하여 로그를 현지화합니다.

  • 서비스에서 생성된 로그가 저장됩니다.

  • 서비스는 서로 다른 지역의 여러 머신에 배포됩니다

액세스 단계

전체 프레임워크를 ELK에 다음 단계로 간단히 요약하겠습니다.

  • 로그 구조 설계 : 전통적인 일반 텍스트로 구성 로그는 구조화된 객체로 변경되어 JSON으로 출력됩니다.

  • 로그 수집: 프레임워크 요청 수명 주기의 일부 주요 노드에서 로그를 출력합니다.

  • ES 인덱스 템플릿 정의: JSON에서 매핑 설정 to ES 실제 저장소

1. 로그 구조 설계

전통적으로 우리는 로그 출력을 할 때 로그 레벨(level)과 로그 내용 문자열(message)을 직접 출력했습니다. 그러나 우리는 언제, 무슨 일이 일어났는지에만 주의를 기울이는 것이 아니라, 유사한 로그가 몇 번이나 발생했는지, 로그의 내용과 맥락, 관련 로그에도 주의를 기울여야 할 수도 있습니다. 따라서 우리는 단순히 로그를 객체로 구성할 뿐만 아니라 로그의 주요 필드도 추출합니다.

1. 로그를 이벤트로 추상화

각 로그의 발생을 이벤트로 추상화합니다. 이벤트에는 다음이 포함됩니다.

이벤트 메타필드

이벤트 발생 시간: 날짜시간, 타임스탬프

이벤트 수준: 레벨, 예: ERROR, INFO, WARNING, DEBUG

이벤트 이름: 이벤트, 예: client-request

이벤트 발생 상대 시간(단위: 나노초): reqLife, 이 필드는 요청과 관련된 이벤트가 발생하기 시작하는 시간(간격)입니다.

이벤트 위치: 라인, 서버 코드 위치, 서버 위치

요청 메타 필드

요청 고유 ID: reqId, 이 필드는 전체 요청 링크에서 발생하는 모든 이벤트를 통해 실행됩니다.

요청 사용자 ID: reqUid, 이 필드는 사용자의 액세스 또는 링크 요청

데이터 필드

다양한 유형의 이벤트를 출력하려면 다양한 세부정보가 필요합니다. 이러한 세부정보(비메타 필드)를 d -- 데이터에 넣습니다. 이를 통해 이벤트 구조가 더 명확해지고 동시에 데이터 필드가 메타 필드를 오염시키는 것을 방지할 수 있습니다.

예: 서버가 사용자 요청을 받을 때마다 인쇄되는 client-init 이벤트와 같이 사용자의 IP, URL 및 기타 이벤트를 데이터 필드에 고유하게 분류합니다. object中

완전한 예 제공

{
    "datetime":"2018-11-07 21:38:09.271",
    "timestamp":1541597889271,
    "level":"INFO",
    "event":"client-init",
    "reqId":"rJtT5we6Q",
    "reqLife":5874,
    "reqUid": "999793fc03eda86",
    "d":{
        "url":"/",
        "ip":"9.9.9.9",
        "httpVersion":"1.1",
        "method":"GET",
        "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
        "headers":"*"
    },
    "browser":"{"name":"Chrome","version":"70.0.3538.77","major":"70"}",
    "engine":"{"version":"537.36","name":"WebKit"}",
    "os":"{"name":"Mac OS","version":"10.14.0"}",
    "content":"(Empty)",
    "line":"middlewares/foo.js:14",
    "server":"127.0.0.1"
}
로그인 후 복사

일부 필드(예: browser, os,engine)때때로 로그가 외부 레이어에서 가능한 한 평평해지기를 원하는 이유는 무엇입니까? (최대 깊이는 2), ES에서 불필요한 인덱스로 인한 성능 손실을 방지합니다. 실제 출력에서는 깊이가 1보다 큰 값을 문자열로 출력합니다. 때때로 일부 객체 필드가 ​​우리에게 중요하므로 이러한 특수 필드를 외부 레이어에 배치하여 출력 깊이가 2보다 크지 않도록 합니다.

일반적으로 로그를 출력할 때 이벤트 이름데이터 필드에만 주의하면 됩니다. 또한, 로그를 출력하는 방식으로 context에 접근하여 일률적으로 획득, 계산, 출력할 수 있다. 事件名称数据字段即可。其他,我们可以在打印日志的方法中,通过访问上下文统一获取,计算,输出。

2. 日志改造输出

前面我们提到了如何定义一个日志事件, 那么,我们如何基于已有日志方案做升级,同时,兼容旧代码的日志调用方式。

升级关键节点的日志

// 改造前
logger.info('client-init => ' + JSON.stringfiy({
    url,
    ip,
    browser,
    //...
}));

// 改造后
logger.info({
    event: 'client-init',
    url,
    ip,
    browser,
    //...
});
로그인 후 복사

兼容旧的日志调用方式

logger.debug('checkLogin');
로그인 후 복사

因为 winston 的 日志方法本身就支持 string 或者 object 的传入方式, 所以对于旧的字符串传入写法,formatter 接收到的实际上是{ level: 'debug', message: 'checkLogin' }。formatter 是 winston 的日志输出前调整日志格式的一道工序, 这一点使我们在日志输出前有机会将这类调用方式输出的日志,转为一个纯输出事件 -- 我们称它们为raw-log事件,而不需要修改调用方式。

改造日志输出格式

前面提到 winston 输出日志前,会经过我们预定义的formatter,因此除了兼容逻辑的处理外,我们可以将一些公共逻辑统一放在这里处理。而调用上,我们只关注字段本身即可。

  • 元字段提取及处理

  • 字段长度控制

  • 兼容逻辑处理

如何提取元字段,这里涉及上下文的创建与使用,这里简单介绍一下 domain 的创建与使用。

//--- middlewares/http-context.js
const domain = require('domain');
const shortid = require('shortid');

module.exports = (req, res, next) => {
    const d = domain.create();
    d.id =  shortid.generate(); // reqId;
    d.req = req;
    
    //...

    res.on('finish', () => process.nextTick(() => {
        d.id = null;
        d.req = null;
        d.exit();
    });

    d.run(() => next());
}

//--- app.js
app.use(require('./middlewares/http-context.js'));

//--- formatter.js
if (process.domain) {
    reqId = process.domain.id;
}
로그인 후 복사

这样,我们就可以将 reqId

2. 로그 변환 출력

앞서 로그 이벤트 정의 방법을 언급했는데, 기존 로그 솔루션을 기반으로 업그레이드하는 동시에 이전 코드 호출 방법의 로그입니다.

키 노드의 로그 업그레이드
    PUT my_logs
    {
      "mappings": {
        "_doc": { 
          "properties": { 
            "title":    {
                "type": "date",
                "format": "epoch_millis"
             }, 
          }
        }
      }
    }
    로그인 후 복사
    로그인 후 복사
  1. 기존 로그 호출 방식과 호환

    PUT _template/my_logs_template
    {
      "index_patterns": "my_logs*",
      "mappings": {
        "_doc": { 
          "properties": { 
            "title":    {
                "type": "date",
                "format": "epoch_millis"
             }, 
          }
        }
      }
    }
    로그인 후 복사
    로그인 후 복사
    윈스턴의 로그 메소드 자체는 문자열 또는 객체 입력 방법이므로 이전 문자열 입력 쓰기 방법의 경우 포맷터가 실제로 받는 것은 { level: 'debug', message: 'checkLogin' }입니다. 포맷터는 로그 출력 전에 Winston의 로그 형식을 조정하는 프로세스입니다. 이는 로그 출력 전에 이러한 종류의 호출 방법을 통해 로그 출력을 순수 출력 이벤트로 변환할 수 있는 기회를 제공합니다. 이를 원시 로그 이벤트라고 부릅니다. 호출 방법.
  2. 로그 출력 형식 변환

    앞서 언급했듯이 Winston은 로그를 출력하기 전에 미리 정의된 포맷터를 거치게 되므로 호환 가능한 논리 처리 외에도 일부 공통 논리를 결합할 수도 있습니다. 여기에 모두 모아두세요. 통화에 관해서는 현장 자체에만 집중합니다.

    메타필드 추출 및 처리

    필드 길이 제어#🎜 🎜 #Node Framework를 ELK에 연결하는 과정 요약

    호환 논리 처리

메타 필드를 추출하는 방법, 여기에는 컨텍스트 생성 및 사용이 포함됩니다. 여기에는 간략한 소개 도메인 생성 및 사용.
rrreee

이런 방식으로 요청의 모든 이벤트에 reqId를 출력하여 이벤트 상관 목적을 달성할 수 있습니다.

2. 로그 수집

이제 이벤트 출력 방법을 알았으니 다음 단계에서는 두 가지 문제를 고려해야 합니다.

# 🎜🎜##🎜🎜#이벤트를 어디에 출력하고 싶은가요? #🎜🎜##🎜🎜##🎜🎜##🎜🎜#이벤트에 어떤 세부정보가 출력되어야 하나요? #🎜🎜##🎜🎜##🎜🎜##🎜🎜#즉, 전체 요청 링크에서 어떤 노드에 문제가 발생하면 어떤 노드의 정보를 사용하여 문제를 빠르게 찾을 수 있을까요? 또한 통계 분석에는 어떤 노드 데이터를 사용할 수 있나요? #🎜🎜##🎜🎜# 아래 흐름도와 같이 공통 요청 링크(사용자 요청, 서비스 측 수신 요청, 다운스트림 서버/데이터베이스에 대한 서비스 요청(*여러 번), 데이터 집계 렌더링, 서비스 응답)와 결합됩니다. # 🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜# 그런 다음 이벤트를 다음과 같이 정의할 수 있습니다. #🎜🎜##🎜🎜#User request#🎜🎜##🎜🎜##🎜 🎜 #client-init: 요청 주소, 요청 헤더, HTTP 버전 및 방법, 사용자 IP 및 브라우저를 포함하여 프레임이 요청(구문 분석되지 않음)을 수신할 때 인쇄됩니다. #🎜🎜##🎜🎜#client-request: 프레임이 수신될 때 인쇄됩니다. 요청 주소, 요청 헤더, 쿠키, 요청 패키지 본문 #🎜🎜##🎜🎜#client-response: 요청 주소, 응답 코드 등을 포함하여 요청을 반환하기 위해 프레임에 인쇄합니다. 응답 헤더, 응답 패키지 본문#🎜🎜##🎜🎜#

下游依赖

http-start: 打印于请求下游起始:请求地址,请求包体,模块别名(方便基于名字聚合而且域名)

http-success: 打印于请求返回 200:请求地址,请求包体,响应包体(code & msg & data),耗时

http-error:  打印于请求返回非 200,亦即连接服务器失败:请求地址,请求包体,响应包体(code & message & stack),耗时。

http-timeout:  打印于请求连接超时:请求地址,请求包体,响应包体(code & msg & stack),耗时。

字段这么多,该怎么选择? 一言以蔽之,事件输出的字段原则就是:输出你关注的,方便检索的,方便后期聚合的字段。

一些建议

  1. 请求下游的请求体和返回体有固定格式, e.g. 输入:{ action: 'getUserInfo', payload: {} } 输出: { code: 0, msg: '', data: {}} 我们可以在事件输出 action,code 等,以便后期通过 action 检索某模块具体某个接口的各项指标和聚合。

一些原则

  1. 保证输出字段类型一致 由于所有事件都存储在同一个 ES 索引, 因此,相同字段不管是相同事件还是不同事件,都应该保持一致,例如:code不应该既是数字,又是字符串,这样可能会产生字段冲突,导致某些记录(document)无法被冲突字段检索到。

  2. ES 存储类型为 keyword, 不应该超过 ES mapping 设定的 ignore_above 中指定的字节数(默认4096个字节)。否则同样可能会产生无法被检索的情况

三、ES 索引模版定义

这里引入 ES 的两个概念,映射(Mapping)与模版(Template)。

首先,ES 基本的存储类型大概枚举下,有以下几种

  • String: keyword & text

  • Numeric: long, integer, double

  • Date: date

  • Boolean: boolean

一般的,我们不需要显示指定每个事件字段的在ES对应的存储类型,ES 会自动根据字段第一次出现的document中的值来决定这个字段在这个索引中的存储类型。但有时候,我们需要显示指定某些字段的存储类型,这个时候我们需要定义这个索引的 Mapping, 来告诉 ES 这此字段如何存储以及如何索引。

e.g.

还记得事件元字段中有一个字段为 timestamp ?实际上,我们输出的时候,timestamp 的值是一个数字,它表示跟距离 1970/01/01 00:00:00 的毫秒数,而我们期望它在ES的存储类型为 date 类型方便后期的检索和可视化, 那么我们创建索引的时候,指定我们的Mapping。

PUT my_logs
{
  "mappings": {
    "_doc": { 
      "properties": { 
        "title":    {
            "type": "date",
            "format": "epoch_millis"
         }, 
      }
    }
  }
}
로그인 후 복사
로그인 후 복사

但一般的,我们可能会按日期自动生成我们的日志索引,假定我们的索引名称格式为 my_logs_yyyyMMdd (e.g. my_logs_20181030)。那么我们需要定义一个模板(Template),这个模板会在(匹配的)索引创建时自动应用预设好的 Mapping。

PUT _template/my_logs_template
{
  "index_patterns": "my_logs*",
  "mappings": {
    "_doc": { 
      "properties": { 
        "title":    {
            "type": "date",
            "format": "epoch_millis"
         }, 
      }
    }
  }
}
로그인 후 복사
로그인 후 복사
提示:将所有日期产生的日志都存在一张索引中,不仅带来不必要的性能开销,也不利于定期删除比较久远的日志。

小结

至此,日志改造及接入的准备工作都已经完成了,我们只须在机器上安装 FileBeat -- 一个轻量级的文件日志Agent, 它负责将日志文件中的日志传输到 ELK。接下来,我们便可使用 Kibana 快速的检索我们的日志。

위 내용은 Node Framework를 ELK에 연결하는 과정 요약의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:segmentfault.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿