我爱编程

关于App异常监控那点事

2018-05-11  本文已影响1954人  Hozan

本博客讲讲App异常监控,每个app都要保证使用质量,这样才能保住用户量,所以对于应用程序的监控显得尤为重要。想象一下,当用户向你抛出一个bug(或者说异常),而你却找不到异常出现的原因和时机,也很难去重现这种奇葩的事件,此时你有一种众里寻他千百度,那bug却不知在何处的感觉。所以,利用某种工具去实现App的异常监控,方便我们及时查看结果,并且作出合理的处理,这对于那些有产品质量追求的人,是挺重要的。

首先,我们来理解几个概念:

1、异常与捕获

2、DSN(数据源名称)

相关链接:https://docs.sentry.io/quickstart/

一、为什么要集成异常监控?

即使我们对上线的项目做了大量的测试,但有时候还是会有潜在的bug,这种比较顽固的问题只能通过监控机制才能有效的减少其带来的损失,所以异常捕获和上报很重要。
如果你是一个开发者,你的开发流程一般是这样子的:
Design → Code & Review → Testing/QA → CI/CD → Issue discovery → Investigation → Remediation
即:设计 → 编码&复查→ 测试/质量保证 → 持续集成/持续部署 → 问题发现 → 侦查→ 修复
现在的开发是趋向于越来越简单,因为有很多很好的开发工具和SDK,但是在问题发现、侦查和修复这几个过程仍旧是挺痛苦、烦人和漫长的。比如说,在生产环境中发现了一个bug,你如何及时得到通知?你如何估计其影响以及紧迫性?你如何快速找到问题的根源?当你修正这个漏洞时,你怎么知道是否解决了问题?
带着这些疑问,让我们来更进一步了解异常监控,它能给我们带来什么样的收益?而我们又需要付出多大的成本?

二、如何做到异常捕获?

关于异常捕获的原理,笔者第一次接触,不甚了解,这里有一个关于前端异常捕获的链接推荐你们参考,感觉写得还行(前端异常捕获与上报:https://www.cnblogs.com/luozhihao/p/8635507.html)。
因为我这里只注重react native App相关的异常监控,所以就只分析Sentry是如何捕获到js的异常,然后将异常事件上报到服务器的。我是通过react-native-sentry的一些源码慢慢摸索出来的,至于理解的对不对,望大家一起探讨。
作为一个程序猿,大家都一定写过如下面一段代码:

   try {
        throw new Error('自定义异常msg')
    }
    catch (e) {
        console.log('e==='+e)
    }

这是捕获异常的简单程序,它可以避免你的页面因为一个致命的异常而闪崩,而sentry主要是捕获到你未catch的程序异常,所以如果你用程序去catch了异常,sentry就没有将这个异常上报到服务器了。
接下来看react-native-sentry的源码,主要看以下几个:


react-native-sentry.png image.png

RNSentry和RNSentryEventEmitter都是原生封装模块供js端调用,RNSentry有一个捕获异常事件的方法,通过这个方法它将js端的事件的相关信息捕获到原生处理;而RNSentryEventEmitter是一个事件发射模块,用于将event进行发送,通知js端进行相应的操作。

@ReactMethod
public void captureEvent(ReadableMap event) {
    ReadableNativeMap castEvent = (ReadableNativeMap)event;

    EventBuilder eventBuilder = new EventBuilder()
            .withLevel(eventLevel(castEvent));

    if (event.hasKey("message")) {
        eventBuilder.withMessage(event.getString("message"));
    }

    if (event.hasKey("logger")) {
        eventBuilder.withLogger(event.getString("logger"));
    }

    if (event.hasKey("user")) {
        UserBuilder userBuilder = getUserBuilder(event.getMap("user"));
        User builtUser = userBuilder.build();
        if (builtUser.getId() != null) {
            UserInterface userInterface = new UserInterface(
                    builtUser.getId(),
                    builtUser.getUsername(),
                    null,
                    builtUser.getEmail(),
                    builtUser.getData()
            );
            eventBuilder.withSentryInterface(userInterface);
        }
    }

    if (castEvent.hasKey("extra")) {
        for (Map.Entry<String, Object> entry : castEvent.getMap("extra").toHashMap().entrySet()) {
            eventBuilder.withExtra(entry.getKey(), entry.getValue());
        }
    }

    if (castEvent.hasKey("tags")) {
        for (Map.Entry<String, Object> entry : castEvent.getMap("tags").toHashMap().entrySet()) {
            String tagValue = entry.getValue() != null ? entry.getValue().toString() : "INVALID_TAG";
            eventBuilder.withTag(entry.getKey(), tagValue);
        }
    }

    if (event.hasKey("exception")) {
        ReadableNativeArray exceptionValues = (ReadableNativeArray)event.getMap("exception").getArray("values");
        ReadableNativeMap exception = exceptionValues.getMap(0);
        ReadableNativeMap stacktrace = exception.getMap("stacktrace");
        ReadableNativeArray frames = (ReadableNativeArray)stacktrace.getArray("frames");
        if (exception.hasKey("value")) {
            addExceptionInterface(eventBuilder, exception.getString("type"), exception.getString("value"), frames);
        } else {
            // We use type/type here since this indicates an Unhandled Promise Rejection
            // https://github.com/getsentry/react-native-sentry/issues/353
            addExceptionInterface(eventBuilder, exception.getString("type"), exception.getString("type"), frames);
        }
    }

    Sentry.capture(buildEvent(eventBuilder));
}

如果你感兴趣,还可以再看一些更底层的东西


image.png

讲了这么多,简单画个流程图:

流程图.png

三、选择哪个异常监控工具?

选择哪个异常监控工具其实也是蛮纠结的事,一个一个尝试太耗费时间了,个人觉得那些别人用的最多的工具可以尝试使用,只有好的东西才有多人使用。这里只列举一些异常监控的工具:

四、react-native-sentry SDK的集成和使用

集成react-native-sentry也很简单,如下图所示:


image.png

相关链接:https://docs.sentry.io/clients/react-native/

还可以自己搭建sentry,感觉还挺复杂的,这里附录一些教程,有时间可以尝试弄一下:

五、Demo分析

(1)贴上一段简单的代码,自己模拟抛出异常:
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/

import React, {Component} from 'react';
import {
    TouchableOpacity,
    StyleSheet,
    Text,
    View,
    WebView,
} from 'react-native';

import {Sentry} from 'react-native-sentry';

Sentry.config('https://9126f02fbec841f7808636a21b3d89b7:47dccf6f5ca64a09a9bfbab6f5e717ef@sentry.io/1200153').install();


type Props = {};
export default class App extends Component<Props> {

    _refresh = () => {
        try {
            throw new Error('刷新')
        }
        catch (e) {
            console.log('e==='+e)
        }

    }

    _throwException = () => {
        throw new Error('我在客户端用js抛出了一个异常2')
    }

    render() {
        return (
            <View style={styles.container}>

                <View style={{flex: 1, backgroundColor: '#fff'}}>
              
                    <TouchableOpacity onPress={this._throwException} style={{
                        height: 50,
                        backgroundColor: 'green',
                        justifyContent: 'center',
                        alignItems: 'center'
                    }}>
                        <Text style={{color: '#fff'}}>抛出异常</Text>
                    </TouchableOpacity>

                </View>

                <TouchableOpacity onPress={this._refresh} style={{
                    height: 50,
                    backgroundColor: 'green',
                    justifyContent: 'center',
                    alignItems: 'center'
                }}>
                    <Text style={{color: '#fff'}}>刷新</Text>
                </TouchableOpacity>


            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#fff'
    },

});

(2)查看异常详情

device.png

下面解释几个字段所代表的意思:
Architecture :cpu架构
Battery Level:电池电量
Orientation:屏幕方向(portrait为纵向)
Memory:运行内存
Capacity:存储容量
Simulator:是否为模拟器
brand:商标
charging:是否正在充电中
manufacturer:制造商
online:联网
screen_density:屏幕像素比
screen_dpi:屏幕密度
screen_resolution:屏幕分辨率

image.png

Bundle ID:应用的包名
Bundle Name:应用名称

而且Sentry还可以发送崩溃报告邮件到你的邮箱,还可以进行相应的通知设置,如下图所示:


崩溃报告.png

综上所述,Sentry还是比较适合用于做reactnative App的异常监控工具,简单易用,免费使用,而且还可以阅读英文版报告邮件。但是,实践是检验真理的唯一标准,把它应用到实际项目中去,看看是否真的好用。

(备注:写这篇博客的时候还能登陆sentry官网,现在貌似要翻墙才能登陆,也是醉醉哒,请慎用!)

相关网址推荐:

上一篇下一篇

猜你喜欢

热点阅读