# CLA服务部署与使用指南

[TOC]

# 介绍

CLA(Cloud Log Analysis)是旨在让浏览器端/小程序端/客户端的日志更容易被获取,前端问题更容易被定位的系统,支持两种架构的日志上报。

1、基于sdk + proxy + kafka + ELK架构,在kibana上查询日志

2、基于sdk + proxy + 腾讯云cls架构,在cls控制台上查询日志

# SDK引入(cla接入者关注)

# js sdk

# 下载 cla-web-sdk

  npm install cla-web -s
  # 或者
  yarn add cla-web
1
2
3

# 引入SDK

  1. 内联引入 (注意:务必在项目头部<head></head>引入

    • 可在 cla-web 包目录下找到 /lab/cla.min.js 然后自行复制即可
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>CLA-WEB-SDK</title>
      <script src="/libs/cla.min.js"></script>
    </head>
    <body>
    
    </body>
    </html>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  2. VUE项目中引入

    import Cla from 'cla-web'
    
    1

# 实例化SDK

引入SDK后,需实例化SDK

注意:appname baseUrl 为必传参数,为空会导致初始化SDK失败,无法正常上报

const claIns = new Cla({
  appname: 'fca-wechat-help-test', // 项目名称
  baseUrl: '' // 上报地址
})
1
2
3
4
  • CLS架构(推荐):appname自行填写,能表示自己的项目及所在的环境即可;baseUrl找@朱琦或@王梦杰获取
  • ELK架构:appnamebaseUrl找@王梦杰申请

# 查询数据

  • CLS架构(推荐):登录cls控制台查询,地址找@朱琦或@王梦杰获取
  • ELK架构:登陆kibana,查询对应的 appname 即可。

# 参数说明

名称 类型 必填 默认值 说明
appname String 项目名称
baseUrl String 上报地址
repeat Number 5 相同错误最大重复次数
off Boolean true 是否开启 cgi 异常返回码上报
delay Number 2000 上报延迟时间(ms)
sampling Number 1 错误抽样率
timeout Number 20000 ajax 请求最大延迟时间(ms)
performance Boolean false 是否自动上报页面性能加载数据
blacklist Array [] cgi类上报地址黑名单列表

# API

claIns 的实例方法 实例化SDK后调用

  1. 自定义上报
  • claIns.report(customLog)
  • 自定义上报 customLog 格式可以为 Error 对象或者自定义对象。
  • 自定义对象建议格式如下:
    const customLog = {
        msg: 'custom message',
        stack: 'custom report error',
        level: 'INFO'
    }
    // level: INFO/DEBUG/ERROR
    
    1
    2
    3
    4
    5
    6
  1. 手动上报性能数据
  • claIns.reportPerformance()
  • 可配置开启自动上报性能数据,也可以在合适的时候手动上报。

# 其他

  • 在VUE项目中接入SDK后 若定义 Vue.config.errorHandler 方法,建议实例化SDK后调用。claIns.report()方法手动上报,SDK会自动解析报错信息并上报,若未定义无需额外配置。

      Vue.config.errorHandler = function (err, vm, info) {
        claIns.report(err)
      }
    
    1
    2
    3
  • SDK支持通过url参数,获取用户uuid,便于查询特定用户的日志。

    • 用户输入 http://a.com?cla=1 时,页面弹出提示框,显示用户uuid。

# 小程序sdk

# 下载

npm i cla-mina -s
# or
yarn add cla-mina
1
2
3

# 使用sdk

  • 在小程序入口app.js引入并初始化
// 引入sdk
const CLA = require('./libs/cla')
// 初始化并传入配置参数,参数参考‘参数说明’
const cla = CLA({
    // 参数配置
    appname: "fca-wechat-help-test",
    repeat: 0,
    delay: 0,
    sampling: 1,
    baseUrl: "",
})
//app.js
App({
  onLaunch: function () {
    
  },
  //...
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • CLS架构(推荐):appname自行填写,能表示自己的项目及所在环境即可;baseUrl找@朱琦或@王梦杰获取
  • ELK架构:appnamebaseUrl找@王梦杰申请

# 查询数据

  • CLS架构(推荐):登录cls控制台查询,地址找@朱琦或@王梦杰获取
  • ELK架构:登陆kibana,查询对应的 appname 即可。

# 参数说明

名称 sdk传 说明
timestamp 上报时间点
ip 用户IP
appname 应用名,es以此字段建索引
level 日志级别。ERROR/DEBUG/INFO
uuid 用户ID
userAgent UA
page web sdk为location.href; mina sdk为小程序页面路径
ref 页面refer
msg 日志内容
stack 错误堆栈
reqBody cgi请求参数
latency cgi时延
status cgi状态码

# 日志格式

  • 基础数据
字段 说明
appname 应用名
timestamp 上报时间点
level 日志级别,ERROR/DEBUG/INFO
uuid 用户ID
userAgent UA
page 小程序页面路径
ref 页面refer
  • 日志内容
字段 说明
msg 日志内容体(js异常错误内容、debug日志内容、异常请求返回内容)
msg.code 请求返回码
msg.result 请求返回结果数据
msg.isOk 返回错误与否
msg.errMsg 返回错误信息
  • 请求内容
字段 说明
reqBody 请求体
reqBody.method 请求方法
reqBody.url 请求url
reqBody.data 请求参数
  • 响应内容
字段 说明
status 状态码
latency 时延

其他返回参考msg

  • 异常内容
字段 说明
stack 错误堆栈

其他内容参考msg

# API

cla为初始化后实例

  • cla.report([option])
    • 为手动上报接口
    • option参考‘参数配置’

# 部署指南(cla维护者关注)

# 腾讯云cls架构(推荐)

这种架构可以直接使用腾讯云提供的cls,无需开发者搭建部署kafka + ELK,依赖更少,搭建更快。

# cls日志集创建

在腾讯云控制台开启日志服务CLS,并创建一个日志主题,完成后会生产一个日志主题ID

# proxy服务部署

jenkins构建过程说明:

该服务编译打包过程已全部封装到Dockerfile中,只需要 docker build docker push 即可完成代码构建。

一个proxy服务配置一个日志主题ID,将收到得日志请求向指定得日志主题上传;

服务启动参数

# 配置文件中包含腾讯云api密钥,部署后替换成对应的即可
/data/app/app-go --config=/data/app/prod.yaml [--logId=主题ID]

1
2
3

特别注意

CLS 服务使用的主题ID需要是对应云api密钥对应账号创建的,否则会出现权限不足的情况。

# ELK架构

# 资源准备

  • kafka
    • 分区: 24
    • topic: topic-fe-log
  • elasticsearch

# 服务部署

# logstash
# configmap
apiVersion: v1
data:
  jvm.options: |
    ## JVM configuration

    # Xms represents the initial size of total heap space
    # Xmx represents the maximum size of total heap space

    -Xms4g
    -Xmx4g

    ################################################################
    ## Expert settings
    ################################################################
    ##
    ## All settings below this section are considered
    ## expert settings. Don't tamper with them unless
    ## you understand what you are doing
    ##
    ################################################################

    ## GC configuration
    -XX:+UseParNewGC
    -XX:+UseConcMarkSweepGC
    -XX:CMSInitiatingOccupancyFraction=75
    -XX:+UseCMSInitiatingOccupancyOnly

    ## Locale
    # Set the locale language
    #-Duser.language=en

    # Set the locale country
    #-Duser.country=US

    # Set the locale variant, if any
    #-Duser.variant=

    ## basic

    # set the I/O temp directory
    #-Djava.io.tmpdir=$HOME

    # set to headless, just in case
    -Djava.awt.headless=true

    # ensure UTF-8 encoding by default (e.g. filenames)
    -Dfile.encoding=UTF-8

    # use our provided JNA always versus the system one
    #-Djna.nosys=true

    # Turn on JRuby invokedynamic
    -Djruby.compile.invokedynamic=true
    # Force Compilation
    -Djruby.jit.threshold=0

    ## heap dumps

    # generate a heap dump when an allocation from the Java heap fails
    # heap dumps are created in the working directory of the JVM
    -XX:+HeapDumpOnOutOfMemoryError

    # specify an alternative path for heap dumps
    # ensure the directory exists and has sufficient space
    #-XX:HeapDumpPath=${LOGSTASH_HOME}/heapdump.hprof

    ## GC logging
    #-XX:+PrintGCDetails
    #-XX:+PrintGCTimeStamps
    #-XX:+PrintGCDateStamps
    #-XX:+PrintClassHistogram
    #-XX:+PrintTenuringDistribution
    #-XX:+PrintGCApplicationStoppedTime

    # log GC status to a file with time stamps
    # ensure the directory exists
    #-Xloggc:${LS_GC_LOG_FILE}

    # Entropy source for randomness
    -Djava.security.egd=file:/dev/urandom
  log4j2.properties: |
    status = error
    name = LogstashPropertiesConfig

    appender.console.type = Console
    appender.console.name = plain_console
    appender.console.layout.type = PatternLayout
    appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c] %m%n

    appender.json_console.type = Console
    appender.json_console.name = json_console
    appender.json_console.layout.type = JSONLayout
    appender.json_console.layout.compact = true
    appender.json_console.layout.eventEol = true

    appender.rolling.type = RollingFile
    appender.rolling.name = plain_rolling
    appender.rolling.fileName = ${sys:ls.logs}/logstash-${sys:ls.log.format}.log
    appender.rolling.filePattern = ${sys:ls.logs}/logstash-${sys:ls.log.format}-%d{yyyy-MM-dd}-%i.log.gz
    appender.rolling.policies.type = Policies
    appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
    appender.rolling.policies.time.interval = 1
    appender.rolling.policies.time.modulate = true
    appender.rolling.layout.type = PatternLayout
    appender.rolling.layout.pattern = [%d{ISO8601}][%-5p][%-25c] %-.10000m%n
    appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
    appender.rolling.policies.size.size = 100MB

    appender.json_rolling.type = RollingFile
    appender.json_rolling.name = json_rolling
    appender.json_rolling.fileName = ${sys:ls.logs}/logstash-${sys:ls.log.format}.log
    appender.json_rolling.filePattern = ${sys:ls.logs}/logstash-${sys:ls.log.format}-%d{yyyy-MM-dd}-%i.log.gz
    appender.json_rolling.policies.type = Policies
    appender.json_rolling.policies.time.type = TimeBasedTriggeringPolicy
    appender.json_rolling.policies.time.interval = 1
    appender.json_rolling.policies.time.modulate = true
    appender.json_rolling.layout.type = JSONLayout
    appender.json_rolling.layout.compact = true
    appender.json_rolling.layout.eventEol = true
    appender.json_rolling.policies.size.type = SizeBasedTriggeringPolicy
    appender.json_rolling.policies.size.size = 100MB


    rootLogger.level = ${sys:ls.log.level}
    rootLogger.appenderRef.console.ref = ${sys:ls.log.format}_console
    rootLogger.appenderRef.rolling.ref = ${sys:ls.log.format}_rolling

    # Slowlog

    appender.console_slowlog.type = Console
    appender.console_slowlog.name = plain_console_slowlog
    appender.console_slowlog.layout.type = PatternLayout
    appender.console_slowlog.layout.pattern = [%d{ISO8601}][%-5p][%-25c] %m%n

    appender.json_console_slowlog.type = Console
    appender.json_console_slowlog.name = json_console_slowlog
    appender.json_console_slowlog.layout.type = JSONLayout
    appender.json_console_slowlog.layout.compact = true
    appender.json_console_slowlog.layout.eventEol = true

    appender.rolling_slowlog.type = RollingFile
    appender.rolling_slowlog.name = plain_rolling_slowlog
    appender.rolling_slowlog.fileName = ${sys:ls.logs}/logstash-slowlog-${sys:ls.log.format}.log
    appender.rolling_slowlog.filePattern = ${sys:ls.logs}/logstash-slowlog-${sys:ls.log.format}-%d{yyyy-MM-dd}-%i.log.gz
    appender.rolling_slowlog.policies.type = Policies
    appender.rolling_slowlog.policies.time.type = TimeBasedTriggeringPolicy
    appender.rolling_slowlog.policies.time.interval = 1
    appender.rolling_slowlog.policies.time.modulate = true
    appender.rolling_slowlog.layout.type = PatternLayout
    appender.rolling_slowlog.layout.pattern = [%d{ISO8601}][%-5p][%-25c] %.10000m%n
    appender.rolling_slowlog.policies.size.type = SizeBasedTriggeringPolicy
    appender.rolling_slowlog.policies.size.size = 100MB

    appender.json_rolling_slowlog.type = RollingFile
    appender.json_rolling_slowlog.name = json_rolling_slowlog
    appender.json_rolling_slowlog.fileName = ${sys:ls.logs}/logstash-slowlog-${sys:ls.log.format}.log
    appender.json_rolling_slowlog.filePattern = ${sys:ls.logs}/logstash-slowlog-${sys:ls.log.format}-%d{yyyy-MM-dd}-%i.log.gz
    appender.json_rolling_slowlog.policies.type = Policies
    appender.json_rolling_slowlog.policies.time.type = TimeBasedTriggeringPolicy
    appender.json_rolling_slowlog.policies.time.interval = 1
    appender.json_rolling_slowlog.policies.time.modulate = true
    appender.json_rolling_slowlog.layout.type = JSONLayout
    appender.json_rolling_slowlog.layout.compact = true
    appender.json_rolling_slowlog.layout.eventEol = true
    appender.json_rolling_slowlog.policies.size.type = SizeBasedTriggeringPolicy
    appender.json_rolling_slowlog.policies.size.size = 100MB

    logger.slowlog.name = slowlog
    logger.slowlog.level = trace
    logger.slowlog.appenderRef.console_slowlog.ref = ${sys:ls.log.format}_console_slowlog
    logger.slowlog.appenderRef.rolling_slowlog.ref = ${sys:ls.log.format}_rolling_slowlog
    logger.slowlog.additivity = false
  logstash.yml: ""
  pipelines.yml: |-
    - pipeline.id: kafka_input
      pipeline.workers: 8
      pipeline.output.workers: 8
      pipeline.batch.size: 10000
      pipeline.batch.delay: 5
      path.config: "/usr/share/logstash/config/pipline_kafka_input.conf"
  pipline_kafka_input.conf: "input {\n    kafka {\n        bootstrap_servers => [\"127.0.0.1:9092\"] \n        group_id => \"group_logstash_fe_log\"\n        topics => [\"topic-fe-log\"] \n        consumer_threads => 24\n        decorate_events => true\n        codec => \"json\"\n        }\n}\n\nfilter {\n    ruby {\n        code => \"event.set('timestamp', event.get('@timestamp').time.utc+8*60*60)\"\n    }\n}\n\noutput {\n  elasticsearch {\n        hosts => [\"127.0.0.1:9200\"]\n        user => \"elastic\"\n        password => \"xxxxxx\"\n        index => \"%{appname}-%{+YYYY.MM.dd}\"\n        manage_template => true\n        template_overwrite => true\n        template => \"/usr/share/logstash/config/syslog_template.json\"\n  }\n}"
  syslog_template.json: |
    {
      "template": "syslog-*",
      "settings": {
        "index.number_of_shards": 24,
        "number_of_replicas": 1
      },
      "mappings": {
        "tradelog": {
          "_all": {
            "enabled": false
          },
          "properties": {
            "@timestamp": {
              "type": "date",
              "format": "yyyy-MM-dd'T'HH:mm:ssZZ.SSS",
              "doc_values": true
            },
            "@version": {
              "type": "string",
              "index": "not_analyzed"
            }
          }
        }
      }
    }
kind: ConfigMap
metadata:
  name: logstash
  namespace: log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211

请按实际情况更改配置:

# workload
  • CPU内存限制范围在:2C4G - 4C8G
  • 无需创建服务发现
  • 镜像地址:ccr.ccs.tencentyun.com/iov_inc/local-logstash:6.4.3
# cla-proxy
# configmap
apiVersion: v1
data:
  CLA_APPS: |-
    [{
        "APPNAME": "fca-wechat-main-prod",
        "SCOPE": [".tsdp.fcachinagsdp.com"]
    }]
  CLA_GRPC_PORTS: '[8085,8086]'
  CLA_KAFKA_TOPIC: topic-fe-log
  CLA_KAFKA_URL: 127.0.0.1:9092
  CLA_LEVEL: DEBUG
  CLA_PORT: "8080"
  GRPC_SVR: 127.0.0.1:8085,127.0.0.1:8086
kind: ConfigMap
metadata:
  name: cla-proxy
  namespace: log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

请按实际情况更改配置:

# workload
  • 服务端口:8080
  • Ingress:需要创建,作为sdk上报的接口
  • 镜像地址:ccr.ccs.tencentyun.com/iov_inc/cla-proxy:v0.0.5

# ES创建索引



lastUpdate: 1/17/2022, 11:45:26 PM