记录--百分百空手接大锅

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

背景

愉快的双休周末刚过完,早上来忽然被运营通知线上业务挂了,用户无法下单。卧槽,赶紧进入debug模式,一查原来是服务端返回的数据有问题,赶紧问了服务端,大佬回复说是业务部门配置套餐错误。好在主责不在我们,不过赶紧写了复盘文档,主动找自己的责任,扛起这口大锅,都怪我们前端,没有做好前端监控,导致线上问题持续两天才发现。原本以为运营会把推辞一下说不,锅是她们的,可惜人家不太懂人情世故,这锅就扣在了技术部头上。虽然但是,我还是静下心来把前端异常监控搞了出来,下次一定不要主动接锅,希望看到本文的朋友们也不要随便心软接锅^_^

监控

因为之前基于sentry做了埋点处理,基础已经打好,支持全自动埋点、手动埋点和数据上报。相关的原理可以参考之前的一篇文章如何从0-1构建数据平台(2)- 前端埋点。本次监控的数据上报也基于sentry.js。那么如何设计整个流程呢。具体步骤如下:

  1. 监控数据分类

  2. 监控数据定义

  3. 监控数据收集

  4. 监控数据上报

  5. 监控数据输出

  6. 监控数据预警

数据分类

我们主要是前端的数据错误,一般的异常大类分为逻辑异常和代码异常。基于我们的项目,由于涉及营收,我们就将逻辑错误专注于支付异常,其他的代码导致的错误分为一大类。然后再将两大异常进行细分,如下:

  1. 支付异常

    1.1 支付成功

    1.2 支付失败

  2. 代码异常

    2.1 bindexception

1
2
3
4
5
6
7
8
9
2.1.1  js_error
 
2.1.2  img_error
 
2.1.3  audio_error
 
2.1.4  script_error
 
2.1.5 video_error
  1. unhandleRejection

    3.1 promise_unhandledrejection_error

    3.2 ajax_error

  2. vueException

  3. peformanceInfo

数据定义

基于sentry的上报数据,一般都包括事件与属性。在此我们定义支付异常事件为“page_h5_pay_monitor”,定义代码异常事件为“page_monitor”。然后支付异常的属性大概为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pay_time,
 
pay_orderid,
 
pay_result,
 
pay_amount,
 
pay_type,
 
pay_use_coupon,
 
pay_use_coupon_id,
 
pay_use_coupon_name,
 
pay_use_discount_amount,
 
pay_fail_reason,
 
pay_platment
代码异常不同的错误类型可能属性会有所区别:
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
// js_error
 
monitor_type,
 
monitor_message,
 
monitor_lineno,
 
monitor_colno,
 
monitor_error,
 
monitor_stack,
 
monitor_url
 
// src_error
 
monitor_type,
 
monitor_target_src,
 
monitor_url
 
// promise_error
 
monitor_type,
 
monitor_message,
 
monitor_stack,
 
monitor_url
 
// ajax_error
 
monitor_type,
 
monitor_ajax_method,
 
monitor_ajax_data,
 
monitor_ajax_params,
 
monitor_ajax_url,
 
monitor_ajax_headers,
 
monitor_url,
 
monitor_message,
 
monitor_ajax_code
 
// vue_error
 
monitor_type,
 
monitor_message,
 
monitor_stack,
 
monitor_hook,
 
monitor_url
 
// peformanceInfo 为数据添加 loading_time 属性,该属性通过entryTypes获取
 
try {
 
    const observer = new PerformanceObserver((list) => {
 
        for (const entry of list.getEntries()) {
 
            if (entry.entryType === 'paint') {
 
                sa.store.set('loading_time', entry.startTime)
 
            }
        }
 
    })
 
    observer.observe({ entryTypes: ['paint'] })
 
} catch (err) {
 
    console.log(err)
 
}

数据收集

数据收集通过事件绑定进行收集,具体绑定如下:

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
import {
 
    BindErrorReporter,
 
    VueErrorReporter,
 
    UnhandledRejectionReporter
 
} from './report'
 
const Vue = require('vue')
 
 
// binderror绑定
 
const MonitorBinderror = () => {
 
    window.addEventListener(
 
    'error',
 
    function(error) {
 
        BindErrorReporter(error)
 
    },true )
 
}
 
// unhandleRejection绑定 这里由于使用了axios,因此ajax_error也属于promise_error
 
const MonitorUnhandledRejection = () => {
 
    window.addEventListener('unhandledrejection', function(error) {
 
        if (error && error.reason) {
 
            const { message, code, stack, isAxios, config } = error.reason
 
            if (isAxios && config) {
 
                // console.log(config)
 
                const { data, params, headers, url, method } = config
 
                UnhandledRejectionReporter({
 
                isAjax: true,
 
                data: JSON.stringify(data),
 
                params: JSON.stringify(params),
 
                headers: JSON.stringify(headers),
 
                url,
 
                method,
 
                message: message || error.message,
 
                code
 
                })
 
            } else {
 
                UnhandledRejectionReporter({
 
                isAjax: false,
 
                message,
 
                stack
 
                })
 
            }
 
        }
 
    })
 
}
 
// vueException绑定
 
const MonitorVueError = () => {
 
    Vue.config.errorHandler = function(error, vm, info) {
 
        const { message, stack } = error
 
        VueErrorReporter({
 
            message,
 
            stack,
 
            vuehook: info
 
        })
 
    }
 
}
 
// 输出绑定方法
 
export const MonitorException = () => {
 
    try {
 
        MonitorBinderror()
 
        MonitorUnhandledRejection()
 
        MonitorVueError()
 
    } catch (error) {
 
        console.log('monitor exception init error', error)
 
    }
 
}

数据上报

数据上报都是基于sentry进行上报,具体如下:

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
/*
 
* 异常监控库 基于sentry jssdk
 
* 监控类别:
 
* 1、window onerror 监控未定义属性使用 js资源加载失败问题
 
* 2、window addListener error 监控未定义属性使用 图片资源加载失败问题
 
* 3、unhandledrejection 监听promise对象未catch的错误
 
* 4、vue.errorHandler 监听vue脚本错误
 
* 5、自定义错误 包括接口错误 或其他diy错误
 
* 上报事件: page_monitor
 
*/
 
// 错误类别常量
 
const ERROR_TYPE = {
 
    JS_ERROR: 'js_error',
 
    IMG_ERROR: 'img_error',
 
    AUDIO_ERROR: 'audio_error',
 
    SCRIPT_ERROR: 'script_error',
 
    VIDEO_ERROR: 'video_error',
 
    VUE_ERROR: 'vue_error',
 
    PROMISE_ERROR: 'promise_unhandledrejection_error',
 
    AJAX_ERROR: 'ajax_error'
 
}
 
const MONITOR_NAME = 'page_monitor'
 
const PAY_MONITOR_NAME = 'page_h5_pay_monitor'
 
const MEMBER_PAY_MONITOR_NAME = 'page_member_pay_monitor'
 
export const BindErrorReporter = function(error) {
 
    if (error) {
 
        if (error.error) {
 
            const { colno, lineno } = error
 
            const { message, stack } = error.error
 
            // 过滤
 
            // 客户端会有调用calljs的场景 可能有一些未知的calljs
 
            if (message && message.toLowerCase().indexOf('calljs') !== -1) {
 
                return
 
            }
 
            sa.track(MONITOR_NAME, {
 
            //属性
 
            })
 
        } else if (error.target) {
 
            const type = error.target.nodeName.toLowerCase()
 
            const monitorType = type + '_error'
 
            const src = error.target.src
 
            sa.track(MONITOR_NAME, {
 
            //属性
 
            })
 
        }
 
    }
 
}
 
export const UnhandledRejectionReporter = function({
 
    isAjax = false,
 
    method,
 
    data,
 
    params,
 
    url,
 
    headers,
 
    message,
 
    stack,
 
    code
 
}) {
 
        if (!isAjax) {
 
            // 过滤一些特殊的场景
 
            // 1、自动播放触发问题
 
            if (message && message.toLowerCase().indexOf('user gesture') !== -1) {
 
                return
 
            }
 
            sa.track(MONITOR_NAME, {
 
                //属性
 
            })
 
        } else {
 
            sa.track(MONITOR_NAME, {
 
                //属性
 
            })
 
        }
 
    }
 
    export const VueErrorReporter = function({ message, stack, vuehook }) {
 
    sa.track(MONITOR_NAME, {
 
        //属性
 
    })
 
}
 
export const H5PayErrorReport = ({
 
    isSuccess = true,
 
    amount = 0,
 
    type = -1,
 
    couponId = -1,
 
    couponName = '',
 
    discountAmount = 0,
 
    reason = '',
 
    orderid = 0,
 
}) => {
 
    // 事件名:page_member_pay_monitor
 
    sa.track(PAY_MONITOR_NAME, {
 
        //属性
 
    })
 
}

以上,通过sentry的sa.track进行上报,具体不作展开

输出与预警

数据被上报到大数据平台,被存储到hdfs中,然后我们直接做定时任务读取hdfs进行一定的过滤通过钉钉webhook输出到钉钉群,另外如果有需要做数据备份可以通过hdfs到数据仓库再到kylin进行存储。

总结

数据监控对于大的,特别是涉及营收的平台是必要的,我们在设计项目的时候一定要考虑到,最好能说服服务端,让他们服务端也提供相应的代码监控。ngnix层或者云端最好也来一层。严重的异常可以直接给你打电话,目前云平台都有相应支持。这样有异常及时发现,锅嘛,接到手里就可以精准扔出去了。

本文转载于:

https://juejin.cn/post/7244363578429030459

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

posted @   林恒  阅读(114)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
历史上的今天:
2022-07-21 web前端工程化合集
欢迎阅读『记录--百分百空手接大锅』
点击右上角即可分享
微信分享提示