关于App异常监控那点事

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

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

1、异常与捕获

  • 异常指的是在程序运行过程中发生的异常事件,通常是由外部问题(如硬件错误、输入错误)所导致的。在Java等面向对象的编程语言中异常属于对象。

  • 异常(Exception)都是运行时的。编译时产生的不是异常,而是错误(Error)。需要注意的是,程序设计导致的错误不属于异常(Exception)。

  • 总结:
    Error:系统内部错误,这类错误由系统进行处理,程序本身无需捕获处理;
    Exception:可以处理的异常

    Read More:https://www.zhihu.com/question/36278363

  • 捕获:获取异常
    通过try…catch语句进行捕获异常。
    通过throw抛出异常,throws向上一级调用方法抛出异常。

2、DSN(数据源名称)

  • DSN:即Data Source Name
  • Sentry官网是如此描述DSN的:
    完成Sentry项目的设置后,将给您指定一个值,我们称之为DSN或数据源名称。它看起来像标准的URL,但实际上只是Sentry SDKS所需要的配置的表示。它由几个部分组成,包括协议、公共密钥和秘密密钥、服务器地址和项目标识符。
    如下图所示:


    DSN.png

    可以在你所在的项目中查看你的Client Keys,它将集成到你的客户端代码中:


    Client Keys.png

相关链接: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
  • Sentry.d.ts:用typescript写的一个sentry相关的配置、注册方法等


    Sentry.d.ts.png
  • Sentry.js:这一块是Sentry的主要代码,在这里它引入了两个原生模块RNSentry和RNSentryEventEmitter,可以查看原生的代码(注意这里是安卓端的,ios的请自行查看),如下图所示:
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));
}
  • RavenClient.js和raven-plugin.js:这两个主要是写有关捕获js端的异常
  • raven-plugin.js这个插件有什么用处呢?
    它会自动检查并捕获js端所触发的未catch的异常。

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


image.png

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

流程图.png

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

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

  • 国内的
  • (1)笔者之前集成过腾讯的bugly,每天都会收到一份崩溃报告邮件


    bugly.png

    但是感觉比较适合纯原生开发的应用,不太适合reactnative的应用,因为它报告的异常基本都是java端的代码,你很难定位到JavaScript端的代码异常。

  • (2)之前也集成过百度移动统计SDK,这种也是适合原生开发的应用,reactnative应用也是不推荐使用,而且百度产品的有关服务相当不给力,还不如去使用极光的相关产品。


    baidu.png

    最主要的一点是,从报告的异常来看,你也很难快速定位到错误的原因和位置。
    这里我只举例之前用过的,其他的国内异常监控工具一定还有更好的。

  • 国外的
    国外的有bugsnag、sentry等,这两个貌似还比较流行。
  • (1)Bugsnag
    bugsnag:聚焦于web端、移动端、服务端程序的错误监控,能自动报告未处理的异常和崩溃,通过崩溃报告日志更好地了解到有关用户的操作情况,也附上了相应的用户信息来确定受崩溃的程度。
    官网上目前可以集成的平台如下图所示:


    bugsnag.png

    我访问bugsnag超级慢且卡,用我的qq邮箱无法登陆,总感觉要翻墙,直接导致我不继续研究bugsnag的原因是看到了这个:


    image.png

    也就是说它能免费试用14天,oh my god,要收费的请离我远一点。
  • (2)Sentry
    Sentry是一个实时事件日志和聚合平台。它的核心是监视错误和提取所有必要的信息,以便进行正确的事后分析。它支持以下语言或平台:
    sentry.png

    如果您最喜欢的语言没有在以上列出,可以在社区论坛上开始讨论如何支持它(社区链接:https://forum.sentry.io/
    后面主要讲sentry方面的集成和使用,因为笔者认为对于reactnative的应用,集成sentry异常监控相当合适,而且没有出现要收费的提示。
    相关链接:https://blog.sentry.io/2018/03/06/the-sentry-workflow

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

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


image.png

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

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

五、Demo分析

  • sentry分析

(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)查看异常详情

  • 自己模拟抛出异常(这里是在开发模式下)
  • throw ‘刷新’


    TAGS.png

    点 JSON可以查看所有的字段:

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:应用名称

  • throw new Error('点刷新抛出一个Error')


    image.png
  • 如果你打包出正式的apk,那么其报告日志就有所不同了

  • 如下图所示:它会定位到代码文件和代码行数,让你很快知道异常抛出的位置。


    image.png

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


崩溃报告.png

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

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

相关网址推荐:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 北京时间8月19日,我相信所有人都记住了这一天,在奥运女子十米台跳水比赛中,中国年仅15岁的小将任茜夺得了中国里约...
    瑜八阅读 470评论 0 0
  • 车看向我 你在左边,疑惑 我不知道 我只知道, 这个傍晚,我一无所知
    长别却短歌阅读 163评论 0 3
  • 1953年,19岁的她是村里有名的才女,绘画厉害,刺绣无人能及,家里大大小小的衣服都是她亲手缝制。而他是从河南那场...
    停不住的梦想阅读 201评论 0 0