关于异常处理:网易云信-Crash-异常治理实践-智企技术委员会技术专题系列

前言Crash(造成用户无奈应用客户端所承载的服务)作为客户端稳固治理的最次要问题之一。云信作为国内业界当先的 RTC/IM PaaS 服务商,对于客户端 SDK(PaaS 服务商对外服务的次要载体)的 Crash 治理再器重也不为过。对于客户端 Crash 稳定性治理,只管业界已有多个成熟的监控平台,实现了从埋点、上报、展现到报警一站式服务,如 Bugly 平台、Fabric 平台等,也有 Crash 捕获的工具 SDK,不便在此基础上按需定制监控平台,如 KSCrash、Breakpad 等。但对于云信这样的 RTC&IM PaaS 服务商来讲,无论是市面上现有 APP Crash 治理平台,还是 Crash 捕获工具 SDK 都是无奈解决针对 PaaS 服务商 Crash 异样治理痛点。 • 平台类型反对无限 业内平台往往只笼罩支流的两个挪动端平台 iOS/Android、对于 Mac OS/PC/Linux/Flutter 不反对。 • APP 与 SDK 监控数据无奈无效隔离 业内对外服务的 Crash 收集平台根本都是设计服务于 APP,后盾剖析与展现无奈同时对 APP 和 SDK 进行辨别。 • 问题治理实现不了闭环解决 以业内驰名 Bugly 为例,只提供了对外闭源 SDK,Bugly 后端目前也没有凋谢第三方接口,解体信息都要人工去被动搜寻解决,无奈与 PaaS 服务商外部 Bug 管理系统进行联动,异样感知也没法主动感知与推送,造成不了 Crash 问题从捕捉到研发人员响应解决高效的闭环。 Crash 异样治理建设为了解决以上 PaaS 服务商稳定性问题治理的痛点,构建一套从异样问题捕捉到研发人员高效问题解决的监控剖析零碎就显得分外火烧眉毛了,云信研发团队从以下三个维度思考: ...

March 8, 2023 · 2 min · jiezi

关于异常处理:处理-Exception-的几种实践被很多团队采纳

起源:http://ww7.rowkey.me/在 Java 中解决异样并不是一个简略的事件。不仅仅初学者很难了解,即便一些有教训的开发者也须要破费很多工夫来思考如何解决异样,包含须要解决哪些异样,怎么解决等等。 这也是绝大多数开发团队都会制订一些规定来标准对异样的解决的起因。而团队之间的这些标准往往是截然不同的。 本文给出几个被很多团队应用的异样解决最佳实际。 1. 在 Finally 块中清理资源或者应用 try-with-resource 语句当应用相似 InputStream 这种须要应用后敞开的资源时,一个常见的谬误就是在 try 块的最初敞开资源。 public void doNotCloseResourceInTry() {    FileInputStream inputStream = null;    try {        File file = new File("./tmp.txt");        inputStream = new FileInputStream(file);        // use the inputStream to read a file        // do NOT do this        inputStream.close();    } catch (FileNotFoundException e) {        log.error(e);    } catch (IOException e) {        log.error(e);    }}上述代码在没有任何 exception 的时候运行是没有问题的。然而当 try 块中的语句抛出异样或者本人实现的代码抛出异样,那么就不会执行最初的敞开语句,从而资源也无奈开释。 正当的做法则是将所有清理的代码都放到 finally 块中或者应用 try-with-resource 语句。 public void closeResourceInFinally() {    FileInputStream inputStream = null;    try {        File file = new File("./tmp.txt");        inputStream = new FileInputStream(file);        // use the inputStream to read a file    } catch (FileNotFoundException e) {        log.error(e);    } finally {        if (inputStream != null) {            try {                inputStream.close();            } catch (IOException e) {                log.error(e);            }        }    }}public void automaticallyCloseResource() {    File file = new File("./tmp.txt");    try (FileInputStream inputStream = new FileInputStream(file);) {        // use the inputStream to read a file    } catch (FileNotFoundException e) {        log.error(e);    } catch (IOException e) {        log.error(e);    }}2. 指定具体的异样尽可能的应用最具体的异样来申明办法,这样能力使得代码更容易了解。 public void doNotDoThis() throws Exception {    ...}public void doThis() throws NumberFormatException {    ...}如上,NumberFormatException 字面上即能够看出是数字格式化谬误。 3. 对异样进行文档阐明当在办法上申明抛出异样时,也须要进行文档阐明。和后面的一点一样,都是为了给调用者提供尽可能多的信息,从而能够更好地防止 / 解决异样。异样解决的 10 个最佳实际,这篇也举荐看下。 在 Javadoc 中退出 throws 申明,并且形容抛出异样的场景。 /** * This method does something extremely useful ... * * @param input * @throws MyBusinessException if ... happens */public void doSomething(String input) throws MyBusinessException {    ...}4. 抛出异样的时候蕴含形容信息在抛出异样时,须要尽可能准确地形容问题和相干信息,这样无论是打印到日志中还是监控工具中,都可能更容易被人浏览,从而能够更好地定位具体错误信息、谬误的重大水平等。 但这里并不是说要对错误信息简明扼要,因为原本 Exception 的类名就可能反映谬误的起因,因而只须要用一到两句话形容即可。 try {    new Long("xyz");} catch (NumberFormatException e) {    log.error(e);}NumberFormatException 即通知了这个异样是格式化谬误,异样的额定信息只须要提供这个谬误字符串即可。当异样的名称不够显著的时候,则须要提供尽可能具体的错误信息。 5. 首先捕捉最具体的异样当初很多 IDE 都能智能提醒这个最佳实际,当你试图首先捕捉最抽象的异样时,会提醒不能达到的代码。当有多个 catch 块中,依照捕捉程序只有第一个匹配到的 catch 块能力执行。因而,如果先捕捉 IllegalArgumentException,那么则无奈运行到对 NumberFormatException 的捕捉。 public void catchMostSpecificExceptionFirst() {    try {        doSomething("A message");    } catch (NumberFormatException e) {        log.error(e);    } catch (IllegalArgumentException e) {        log.error(e)    }}6. 不要捕捉 ThrowableThrowable 是所有异样和谬误的父类。你能够在 catch 语句中捕捉,然而永远不要这么做。如果 catch 了 throwable,那么不仅仅会捕捉所有 exception,还会捕捉 error。而 error 是表明无奈复原的 jvm 谬误。因而除非相对必定可能解决或者被要求解决 error,不要捕捉 throwable。 ...

July 16, 2021 · 1 min · jiezi

关于异常处理:论一个优秀的工程师应该如何做好异常处理和日志记录

异样解决Java类库中定义的能够通过预查看形式躲避的RuntimeException异样不应该通过catch形式来解决: NullPointerExceptionIndexOutofBoundsException无奈通过预查看的异样除外: 在解析字符串模式数字时,不得不通过catch NumberFormatException来实现 if (obj != null) {}异样不要用来做流程管制,条件管制: 异样设计的初衷是解决程序运行中的各种意外状况,且异样的解决效率比条件判断形式要低很多应用catch时要辨别稳固代码和非稳固代码: 稳固代码: 无论如何不会出错的代码非稳固代码: 非稳固代码的catch尽可能辨别异样类型,再做对应解决对于大段代码进行try - catch,会使得程序无奈依据不同的异样做出正确的应激反应,也不利于定位问题 在用户注册场景中,如果用户输出非法字符,或者用户名称已存在,或者用户明码过于简略,在程序上作出分门别类的判断,并提醒给用户捕捉异样是为了解决,不要捕捉了什么都不解决.如果不须要解决,应该将异样抛给调用者 最外层的业务使用者,必须解决异样,将其转化为用户能够了解的内容如果有try块放到了事务代码中 ,catch异样后,如果须要回滚事务,肯定要留神手动回滚事务finally块必须对资源对象,流对象进行敞开,有异样也要做try - catch JDK 7当前,能够应用try - with - resources 形式不要在finally块中应用return: finally块中的return返回后办法完结执行,不会再执行try块中的return语句捕捉异样与抛出异样必须齐全匹配,或者是抛异样的父类办法的返回值能够为null,不强制返回空集合或者空对象等,必须增加正文充分说明什么状况下会返回null值 即便调用办法返回空集合或者空对象,对于调用者来说,必须思考到近程调用失败,序列化失败,运行时异样等返回null的场景肯定要防止出现NPE异样,留神NPE产生的场景: 返回类型为根本数据类型,return包装数据类型的对象时, 主动拆箱有可能产生NPE数据库的查问后果可能为null汇合里的元素即便isNotEmpty, 取出的数据元素也可能为null近程调用返回对象时,一律要进行空指针判断,避免NPE对于Session中获取的数据,倡议进行NPE查看,防止空指针级联调用obj.getA().getB.getC(), 一连串的调用,容易产生NPEJDK 8应用Optional类来避免NPE问题定义时辨别unchecked和checked异样,防止间接抛出new RuntimeException(), 不容许抛出Exception或者Throwable, 应该应用有业务含意的自定义异样 举荐应用业务界已定义过的异样: DAOExceptionServiceException对于公司外的http或者api凋谢接口必须应用 "错误码"; 利用外部举荐异样抛出; 跨利用间的RPC调用优先思考应用Result形式,封装isSuccess()办法,错误码,谬误简短信息 RPC办法应用Result形式的起因: 应用抛异样返回形式,调用方如果没有捕捉到就会产生运行时谬误如果不加栈信息,只是new自定义异样,退出本人了解的error message, 对于调用端解决问题的帮忙不会太多.如果加了栈信息,在频繁调用出错的状况下,数据序列化和传输的性能损耗也是问题避免出现反复的代码,即DRY(Don't Repeat Yourself)准则: 反复的代码在当前的批改时,须要批改所有的正本,容易脱漏抽取共性办法,或者形象公共类,或者组件化 一个类中有多个public办法,都须要进行数行雷同的参数校验工作,这个时候就要进行抽取: private boolean checkParam(DTO dto) {...}日志规约利用中不可间接应用日志零碎(log4j,logback)中的API,应该应用日志框架slf4j中的API, 应用门面模式的日志框架,有利于保护和各个类的日志解决形式对立日志文件至多保留15天,因为有些异样具备以 "周" 为频次产生的特点利用中的扩大日志(打点,长期监控,拜访日志等)命名形式: appName_logType_logName.log logType: 日志类型,如 stats,monitor,accesslogName: 日志形容这样通过文件名就能够晓得日志文件属于什么利用,什么类型,什么目标,也不便归类查找 mppserver利用中独自监控时区转换异样: mppserver_monitor_timeZoneConvert.log对日志进行分类,比方将谬误日志和业务日志离开寄存,便于开发人员查看,也便于对日志零碎进行及时监控对 trace,debug,info级别的日志输入,必须应用条件输入模式或者应用占位符形式 logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);如果日志级别是warn: 上述日志不会打印,然而或执行字符串拼接操作如果symbol是对象,会执行toString() 办法,节约了系统资源,执行上述操作,最终日志却没有打印应用条件输入模式: ...

June 30, 2021 · 1 min · jiezi

关于异常处理:SpringBoot-工程中项目异常的处理

背景剖析 在我的项目的开发中,不论是对底层的数据逻辑操作过程,还是业务逻辑的处理过程,还是管制逻辑的解决过程,都不可避免会遇到各种可预知的、不可预知的异样。解决好异样对系统有很好的爱护作用,同时会大大提高用户的体验。 异样解决剖析Java我的项目中解决异样形式无非两种,要么执行trycatch操作,要么执行throw操作(抛给其它对象解决),无论采纳哪种形式,其目标是让咱们的系统对异样要有反馈。但当初的问题是咱们如何让这种反馈代码的编写即简略又直观、敌对。 Java我的项目异样解决标准咱们在解决异样的过程中通常要遵循肯定的设计规范,例如: 第一:捕捉异样时与抛出的异样必须齐全匹配,或者捕捉异样是抛出异样的父类类型。第二:防止间接抛出RuntimeException,更不容许抛出Exception或者Throwable,应应用有业务含意的自定义异样(例如ServiceException)。第三:捕捉异样后必须进行解决(例如记录日志)。如果不想解决它,须要将异样抛给它的调用者。第四:最外层的逻辑必须解决异样,将其转化成用户能够了解的内容。第五:避免出现反复的代码(Don’t Repeat Yourself),即DAY准则。 SpringBoot 工程下的异样解决实际第一种形式:间接在Controller办法中进行try操作 try{.....}catch(Exception e){.....}如果controller中每个办法都要这样做,代码量大并且可重用性太差,难以保护. 第二种形式:在Controller中定义一个或多个异样解决办法,要害代码如下: @ExceptionHandler(RuntimeException.class)@ResponseBodypublic String doHandleException(RuntimeException e){ log.error("exception {}",e.getMessage()); return e.getMessage();}所有的异样解决办法须要应用@ExceptionHandler进行形容进行形容,并申明它形容的办法能够解决的异样类型,然而对于此种形式而言,它只能解决以后Controller中各个办法呈现的RuntimeException或者是其子类类型的异样,如果多个Controller类中须要同样形式的异样解决办法,那间接在Controller类中定义异样解决办法并不是一种很好的抉择. 第三种形式:在管制逻辑层定义全局异样解决类以及异样解决办法,要害代码如下: @Slf4j//@ResponseBody//@ControllerAdvice@RestControllerAdvicepublic class GlobalExceptionHandler { 异样解决办法 @ExceptionHandler(IllegalArgumentException.class) public String doHandleException(IllegalArgumentException e){ log.error("IllegalArgumentException.exception {}",e.getMessage()); return e.getMessage();} 异样解决办法 @ExceptionHandler(RuntimeException.class) public String doHandleException(RuntimeException e){ log.error("RuntimeException.exception {}",e.getMessage()); return e.getMessage();}其中,@RestControllerAdvice 注解形容的类为全局异样解决类,当管制层办法中的异样没有本人捕捉,也没有定义其外部的异样解决办法,底层默认会查找全局异样解决类,调用对应的异样解决办法进行异常解决。

March 13, 2021 · 1 min · jiezi

关于异常处理:异常处理

什么时候应该抛出异样当一个类型的口头成员不能正在残缺口头工作时,就应该抛出异样告诉调用者。 *口头成员指类型自身或者类型实例能够执行的操作,如C#中StringBuilder中定义的Append,Insert等 捕获异样代码构造private void DoSomething(){ try { //将可能产生异样的代码放在这里 } catch (InvalidOperationException) { //捕捉到InvalidOperationException异样,对应的解决代码放在这里 } catch (IOException) { //捕捉到IOException异样,对应的解决代码放在这里 } catch (Exception) { //捕捉到除了上述之外的其它异样,对应的解决代码放在这里 //这里是将异样抛出 throw; } finally { //这里的代码总是被执行 } //如果try块没有抛出异样或者某个catch捕捉到异样却没有抛出就执行以下的代码,否则以下代码不执行}try块try块中蕴含的是可能会产生异样的代码,异样复原代码应放在一个或多个catch块中。针对应用程序平安的复原某一种异样都须要有一个对应的catch块。一个try块至多要关联一个catch块或finally块,独自一个try块C#是不容许的,而且这样也没有意义。 *如果一个try块中蕴含执行多个可能抛出同一个异样类型的操作,但不同的操作对应的复原措施不同,咱们就应该将这些操作拆分到它本人的try块中,以保障正确的复原状态 catch块catch块蕴含的是响应一个异样须要执行的代码。一个try块能够关联0个或多个catch块。如果try块中的代码没有异样产生,CLR永远不会执行catch块中的代码。线程将跳过所有catch块,直至finally块(外面有代码的话)中的代码。catch关键字前面圆括号中的表达式称为捕获类型,即要捕获的异样类型。 *应用Visual Studio调试catch块时,能够在监督窗口中增加非凡的变量名称$exception来查看以后抛出异样的对象 catch块检索程序与注意事项CLR是自上而下检索一个匹配的catch块,所以编程的时候应留神将派生水平最大的异样类型放在顶部,接着是它们的基类,最初才是System.Exception,如果程序没有放对,例如将最具体的异样类型放在了最底部的catch块中,C#编译器将会产生谬误,因为这个catch块无奈被执行到。 一旦try块中的代码产生异样,而没有与之匹配的catch块的话,CLR会去调用栈的更高一层搜寻与之匹配的异样类型,如果到了栈的顶部还是没有找到,就会产生一个未解决的异样。 在catch块的开端能够做的事件从新抛出雷同的异样,向调用栈高一层的代码告诉该异样的产生抛出一个不同的异样,向调用栈高一层的代码提供更加丰盛的异样信息让线程从catch块的底部退出前两种技术CLR将回溯调用栈,查找捕获类型与抛出异样的类型匹配的catch块并抛出一个异样。如果抉择让线程从catch块的底部退出,将立刻执行蕴含在finally块中的代码,结束后执行紧跟在finally块之后的语句。如果不存在finally块,线程将从最初一个catch块之后的语句开始执行。 finally块finally块中的代码是保障肯定会执行的代码且肯定要在所有catch块的前面,通常蕴含的是对try块中的口头所要求的资源清理操作。一个try块最多只能关联一个finally块。 private void ReadFile(string path){ FileStream fs = null; try { fs = new FileStream(path, FileMode.Open); //解决文件数据... } catch (IOException) { //IOException异样复原代码 } finally { //确保文件敞开 if (fs != null) fs.Close(); }}上述代码,无论try块代码有没有产生异样,文件都肯定会被敞开。如果将敞开文件的代码放在finally块语句之后是不正确的,因为如果抛出异样但没有捕捉到,finally块之后的语句将永远不会被执行,直到下一次垃圾回收才会敞开文件。 ...

December 22, 2020 · 1 min · jiezi

关于异常处理:第三阶段-Day06-图片回显-异常处理机制

1 商品后盾治理1.1 动静实现商品分类展示1.1.1 异步树加载控件阐明1).树形控件树形 `每个节点都具备以下属性:id:节点ID,对加载近程数据很重要。text:显示节点文本。state:节点状态,'open' 或 'closed',默认:'open'。如果为'closed'的时候,将不主动开展该节点。checked:示意该节点是否被选中。attributes: 被增加到节点的自定义属性。children: 一个节点数组申明了若干节点。` 2).异步树阐明树控件读取URL。子节点的加载依赖于父节点的状态。当开展一个关闭的节点,如果节点没有加载子节点,它将会把节点id的值作为http申请参数并命名为’id’,通过URL发送到服务器下面检索子节点。如果用户点击父节点须要开展子节点时,会将父节点的id的值当做参数传递. 1.1.2 编辑ItemCatController(形式一) `/** * 业务需要: 用户通过ajax申请,动静获取树形构造的数据. * url: http://localhost:8091/item/cat/list * 参数: 只查问一级商品分类信息 parentId = 0 * 返回值后果: List<EasyUITree> * * 注意事项: * 1.树形构造初始化时不会传递任何信息.只有开展子节点时传递Id * 2.页面传递什么样的数据,后端必须接管什么样的数据 * * id: 296 */ @RequestMapping("/list") public List<EasyUITree> findItemCatList(Long id){ Long parentId = (id==null?0L:id); return itemCatService.findItemCatList(parentId); }` 1.1.3 编辑ItemCatController(形式二)能够利用注解动静获取参数,并且实现转化. `//2.个别写业务须要见名知意 @RequestMapping("/list") public List<EasyUITree> findItemCatList( @RequestParam(value = "id",defaultValue = "0") Long parentId){ //形式1 //Long parentId = (id==null?0L:id); return itemCatService.findItemCatList(parentId); }` ...

October 22, 2020 · 2 min · jiezi

springmvcspringboot-全局异常处理和自定义异常

前言异常处理其实一直都是项目开发中的大头,但关注异常处理的人一直都特别少。经常是简单的 try/catch 所有异常,然后简单的 printStackTrace ,最多使用 logger 来打印下日志或者重新抛出异常,还有的已经有自定义异常了,但是还是在 controller 捕获异常,需要 catch(异常1 )catch(异常2) 特别繁琐,而且容易漏。 其实 springmvc 在 ControllerAdvice 已经提供了一种全局处理异常的方式,并且我们还可以使用 aop 来统一处理异常,这样在任何地方我们都只需要关注自己的业务,而不用关注异常处理,而且抛出异常还可以利用 spring 的事务,它只有在检测到异常才会事务回滚。 重要说明 下面的相关代码用到了 lombok ,不知道的可以百度下 lombok 的用途使用建造者模式统一异常处理这里使用 springmvc 的 ControllerAdvice 来做统一异常处理 import com.sanri.test.testmvc.dto.ResultEntity;import com.sanri.test.testmvc.exception.BusinessException;import com.sanri.test.testmvc.exception.RemoteException;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.ArrayUtils;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;import java.util.ArrayList;import java.util.List;@RestControllerAdvice@Slf4jpublic class GlobalExceptionHandler { @Value("${project.package.prefix:com.sanri.test}") protected String packagePrefix; /** * 处理业务异常 * @param e * @return */ @ExceptionHandler(BusinessException.class) public ResultEntity businessException(BusinessException e){ printLocalStackTrack(e); return e.getResultEntity(); } @ExceptionHandler(RemoteException.class) public ResultEntity remoteException(RemoteException e){ ResultEntity parentResult = e.getParent().getResultEntity(); ResultEntity resultEntity = e.getResultEntity(); //返回给前端的是业务错误,但是需要在控制台把远程调用异常给打印出来 log.error(parentResult.getReturnCode()+":"+parentResult.getMessage() +" \n -| "+resultEntity.getReturnCode()+":"+resultEntity.getMessage()); printLocalStackTrack(e); //合并两个结果集返回 ResultEntity merge = ResultEntity.err(parentResult.getReturnCode()) .message(parentResult.getMessage()+" \n |- "+resultEntity.getReturnCode()+":"+resultEntity.getMessage()); return merge; } /** * 打印只涉及到项目类调用的异常堆栈 * @param e */ private void printLocalStackTrack(BusinessException e) { StackTraceElement[] stackTrace = e.getStackTrace(); List<StackTraceElement> localStackTrack = new ArrayList<>(); StringBuffer showMessage = new StringBuffer(); if (ArrayUtils.isNotEmpty(stackTrace)) { for (StackTraceElement stackTraceElement : stackTrace) { String className = stackTraceElement.getClassName(); int lineNumber = stackTraceElement.getLineNumber(); if (className.startsWith(packagePrefix)) { localStackTrack.add(stackTraceElement); showMessage.append(className + "(" + lineNumber + ")\n"); } } log.error("业务异常:" + e.getMessage() + "\n" + showMessage); } else { log.error("业务异常,没有调用栈 " + e.getMessage()); } } /** * 异常处理,可以绑定多个 * @return */ @ExceptionHandler(Exception.class) public ResultEntity result(Exception e){ e.printStackTrace(); return ResultEntity.err(e.getMessage()); }}统一返回值一般我们都会定义统一的返回,这样前端好做返回值的解析,像这样package com.sanri.test.testmvc.dto;import lombok.Data;import lombok.ToString;import java.io.Serializable;/** * 普通消息返回 * @param <T> */@Data@ToStringpublic class ResultEntity<T> implements Serializable { private String returnCode = "0"; private String message; private T data; public ResultEntity() { this.message = "ok"; } public ResultEntity(T data) { this(); this.data = data; } public static ResultEntity ok() { return new ResultEntity(); } public static ResultEntity err(String returnCode) { ResultEntity resultEntity = new ResultEntity(); resultEntity.returnCode = returnCode; resultEntity.message = "fail"; return resultEntity; } public static ResultEntity err() { return err("-1"); } public ResultEntity message(String msg) { this.message = msg; return this; } public ResultEntity data(T data) { this.data = data; return this; }}自定义异常自定义异常,就我目前的工作经历来看的话,异常一般就三种 。 ...

September 8, 2019 · 4 min · jiezi

实现简单的监控脚本Bash的执行和异常捕获

当我们需要监控服务运行状态时,一般的策略是写定时脚本,定时执行探测服务状态,如果出现预期外情况,就报警。那么第一步我们就需要学会写一个监控脚本,这里我们会讲到bash的执行环境和异常捕获,以及一些简单的全局参数。 示例先看一段shell代码,这个监控脚本会时刻监控我们的mysql进程是否正常服务,每2分钟执行一次: #!/bin/bash#设置异常的捕获和退出set -eset -o pipefailset -u#获取当前脚本执行的命令和路径#self_name=`readlink -f $0`#self_path=`dirname $self_name`set +e# 脚本主体mysql_process_num=`ps aux | grep mysql | grep -v grep | grep -v bash | wc -l`set -e# 判断脚本输出,此处0为异常if [ "$mysql_process_num" -ge 1 ];then echo "$mysql_process_num|proc_name=mysql"else echo "0|proc_name=mysql"fi脚本命令解析执行器#!/bin/bash首行表示此脚本使用/bin/sh来解释执行,#!是特殊的标识符,后跟此脚本解释器的路径。类似的还有/bin/sh, /bin/perl, /bin/awk等。 我们在使用bash执行脚本的时候,会创建一个新的Shell,这个Shell就是脚本的执行环境,并默认提供这个环境的各个参数。 异常捕获set -eset -o pipefailset -uset +e我们的Shell会给脚本提供默认的环境参数,但是我们也可以用set命令来修改运行参数。在官方手册里一共有十几个参数,我们介绍常用的四个参数。 如果我们直接在终端运行set,不带任何参数,会显示所有的环境变量和Shell函数。 开启和关闭参数我们常见的类似传参形式的set -e代表打开e代表的环境参数,相反的set +e代表关闭e代表的环境参数。 捕获单行异常当我们遇到一个异常,如操作不存在的变量或者一行指令执行出错(行指令返回值不为0),Bash会默认输出错误信息,然后忽略这行错误,继续执行。这在大部分场景下并不是开发者想要的行为,也不利于脚本的安全和Debug。我们应该在错误出现的时候输出错误信息并中断执行。这样能够防止错误被累计和放大。 # 可执行文件run#!/bin/bash# 调用未定义的命令fooecho bar# 执行该文件$ ./run./run: line 3: foo: command not foundbar可以看到输出了错误信息,并继续执行。 如果我们想保证单行如果出现错误,就中断执行脚本,可以有三种写法: # 方法一command || exit 1# 方法二if ! command; then exit 1; fi# 方法三commandif [ "$?" -ne 0 ]; then exit 1; fi上面的方法统一为判断一行指令返回值是否为0来判断异常。类似的,如果我们的多个命令有依赖关系,即后者的执行需要前者成功,则需要写: ...

June 11, 2019 · 2 min · jiezi

异常检测的N种方法阿里工程师都盘出来了

阿里妹导读:互联网黑产盛行,其作弊手段层出不穷,导致广告效果降低,APP推广成本暴增。精准识别作弊是互联网公司和广告主的殷切期望。今天我们将从时间序列、统计、距离、线性方法、分布、树、图、行为序列、有监督机器学习和深度学习模型等多个角度探讨异常检测。作者 | 黎伟斌、胡熠、王皓 背景异常点检测(Outlier detection),又称为离群点检测,是找出与预期对象的行为差异较大的对象的一个检测过程。这些被检测出的对象被称为异常点或者离群点。异常点检测在生产生活中有着广泛应用,比如信用卡反欺诈、工业损毁检测、广告点击反作弊等。 异常点(outlier)是一个数据对象,它明显不同于其他的数据对象。如下图1所示,N1、N2区域内的点是正常数据。而离N1、N2较远的O1、O2、O3区域内的点是异常点。 异常检测的一大难点是缺少ground truth。常见的方法是先用无监督方法挖掘异常样本,再用有监督模型融合多个特征挖掘更多作弊。 近期使用多种算法挖掘异常点,下面从不同视角介绍异常检测算法的原理及其适用场景,考虑到业务特殊性,本文不涉及特征细节。 1.时间序列1.1 移动平均(Moving Average,MA) 移动平均是一种分析时间序列的常用工具,它可过滤高频噪声和检测异常点。根据计算方法的不同,常用的移动平均算法包括简单移动平均、加权移动平均、指数移动平均。假设移动平均的时间窗口为T,有一个时间序列: 1.1.1 简单移动平均(Simple Moving Average,SMA) 从上面的公式容易看出可以用历史的值的均值作为当前值的预测值,在序列取值随时间波动较小的场景中,上述移动均值与该时刻的真实值的差值超过一定阈值则判定该时间的值异常。 适用于: a.对噪声数据进行平滑处理,即用移动均值替代当前时刻取值以过滤噪声; b.预测未来的取值。 1.1.2 加权移动平均(Weighted Moving Average, WMA) 由于简单移动平均对窗口内所有的数据点都给予相同的权重,对近期的最新数据不够敏感,预测值存在滞后性。按着这个思路延伸,自然的想法就是在计算移动平均时,给近期的数据更高的权重,而给窗口内较远的数据更低的权重,以更快的捕捉近期的变化。由此便得到了加权移动平均和指数移动平均。 加权移动平均比简单移动平均对近期的变化更加敏感,加权移动平均的滞后性小于简单移动平均。但由于仅采用线性权重衰减,加权移动平均仍然存在一定的滞后性。 1.1.3 指数移动平均(Exponential Moving Average, EMA) 指数移动平均(Exponential Moving Average, EMA)和加权移动平均类似,但不同之处是各数值的加权按指数递减,而非线性递减。此外,在指数衰减中,无论往前看多远的数据,该期数据的系数都不会衰减到 0,而仅仅是向 0 逼近。因此,指数移动平均实际上是一个无穷级数,即无论多久远的数据都会在计算当期的指数移动平均数值时,起到一定的作用,只不过离当前太远的数据的权重非常低。在实际应用中,可以按如下方法得到t时刻的指数移动平均: 1.2 同比和环比 同比和环比计算公式如图2所示。适合数据呈周期性规律的场景中。如:1.监控APP的DAU的环比和同比,以及时发现DAU上涨或者下跌;2.监控实时广告点击、消耗的环比和同比,以及时发现变化。当上述比值超过一定阈值(阈值参考第10部分)则判定出现异常。 1.3 时序指标异常检测(STL+GESD) STL是一种单维度时间指标异常检测算法。大致思路是: (1)先将指标做STL时序分解,得到seasonal,trend,residual成分,如图3所示;(2)用GESD (generalized extreme studentized deviate)算法对trend+residual成分进行异常检测;(3)为增强对异常点的鲁棒性,将GESD算法中的mean,std等统计量用median, MAD(median absolute deviation)替换;(4)异常分输出:abnorm_score = (value - median)/MAD, value为当前值,median为序列的中位数。负分表示异常下跌,正分表示异常上升。 ...

May 23, 2019 · 1 min · jiezi

yii2-开发-api-接口时优雅的处理全局异常

前言:个人觉得,学习或温习一套Web框架,在快速阅读一遍文档后,应从路由,控制器,请求/响应对象,数据模型(Logic,Dao,Entity),全局异常处理几个方面下手,这几项了解后,框架上手就游刃有余了。然后我比较喜欢在开工前整理好框架的全局异常处理,方便写 api时错误的统一响应。 在api接口的开发过程中,我们需要对用户数据进行严格的校验,防止非法输入对服务产生安全问题,在开发过程中,我比较喜欢即时的以抛出异常的方式中断请求的处理,并以全局异常处理器格式化处理后统一返回给客户端。 今天就把 yii2 自带的全局异常处理器改写至对 api 友好(yii2的 yii\web\HttpException默认对 web 请求友好,都是以text/html的方式返回错误描述,对api不友好,api当然是json)。 注册异常处理器yii2也是以 controller/action 的方式定义一个异常处理器的,我们可以在 components=>errorHandler中自定义。 # config/web.php'components' => [ 'errorHandler' => [ 'errorAction' => 'exception/handler' ]]异常处理器定义相应的异常处理器,app\actions\ErrorApiAction 继承 yii\web\ErrorAction,可以拿到yii2为我们整理好的全局异常。 # controllers/ExceptionController.php<?phpnamespace app\controllers;use yii\web\Controller;class ExceptionController extends Controller{ /** * 为 actionHandler 挂载独立的 action * @return array */ public function actions() { return [ 'handler' => [ 'class' => 'app\actions\ErrorApiAction', ] ]; }}对api友好的错误异常处理器,这里我也只是简单的把响应格式改了一下,异常的上下文还是用yii2自带的处理的。 #actions/ErrorApiAction.php<?php/** * @author wangzhijian@styd.com * @date 2019-5-13 17:20:10 * Api 全局错误异常处理器 */namespace app\actions;use Yii;use yii\web\ErrorAction;use yii\web\Response;class ErrorApiAction extends ErrorAction{ public function run() { // 根据异常类型设定相应的响应码 Yii::$app->getResponse()->setStatusCodeByException($this->exception); // json 格式返回 Yii::$app->getResponse()->format = Response::FORMAT_JSON; // 返回的内容数据 return [ 'msg' => $this->exception->getMessage(), 'err' => $this->exception->getCode() ]; }}异常实体主要是简单的把状态码的传递封装一下,用更容易理解的类名来代理传递。exceptions/HttpException.php ...

May 13, 2019 · 1 min · jiezi

使用Python进行异常处理

来源 | 愿码(ChainDesk.CN)内容编辑愿码Slogan | 连接每个程序员的故事网站 | http://chaindesk.cn愿码愿景 | 打造全学科IT系统免费课程,助力小白用户、初级工程师0成本免费系统学习、低成本进阶,帮助BAT一线资深工程师成长并利用自身优势创造睡后收入。官方公众号 | 愿码 | 愿码服务号 | 区块链部落免费加入愿码全思维工程师社群 | 任一公众号回复“愿码”两个字获取入群二维码本文阅读时长:10min 本文所涉及知识点 Python中有哪些异常 ?使用try ... except子句控制程序流通过处理异常来处理常见问题创建和使用自定义异常类在直接进入代码并解决这些问题之前,让我们首先了解异常是什么以及处理异常是什么意思。 什么是异常?异常是Python中的对象。它为我们提供了有关在程序执行期间检测到的错误的信息。在调试应用程序时注意到的错误是未处理的异常,因为我们没有这些异常。在本文后面,您将学习处理这些异常的技巧。 在早期回溯中看到的ValueError和IndexError异常是Python中内置异常类型的示例 。在下一节中,您将了解Python支持的其他一些内置异常 。 最常见的异常让我们快速回顾一些最常遇到的异常。最简单的方法是尝试运行一些错误的代码,让它报告错误回溯的问题!启动Python解释器并编写以下代码: 以下是一些异常情况: 正如您所看到的,代码的每一行都会抛出一个带有异常类型的错误回溯(突出显示)。这些是Python中的一些内置异常。Python提供BaseException作为所有内置异常的基类。但是,大多数内置异常不直接继承BaseException。相反,它们是从一个名为Exception的类派生而来的,而这个类又继承自BaseException。处理程序退出的内置异常(例如,SystemExit)直接从BaseException派生。您还可以创建自己的异常类作为Exception的子类。您将在本文后面了解到这一点。 异常处理到目前为止,我们已经看到了异常的发生方式 现在,是时候学习如何使用try ... except子句来处理这些异常。以下伪代码显示了try ... except子句的一个非常简单的示例: 我们来看看前面的代码片段: · 首先,程序尝试执行try子句中的代码。 · 在执行期间,如果出现错误(如果发生异常),它将跳出此try子句。try块中的其余代码不会被执行。 · 然后,它在except子句中查找适当的异常处理程序并执行它。 这里使用的 except子句是通用的。它将捕获try子句中发生的所有类型的异常。而不是拥有这个“全能”处理程序,更好的做法是捕获您预期的错误并编写特定于这些错误的异常处理代码。例如,try子句中的代码可能会抛出AssertionError。您可以编写特定的异常处理程序,而不是使用universal except子句,如下所示: 在这里,我们有一个except子句专门处理AssertionError。它还意味着除了AssertionError之外的任何错误都将作为未处理的异常漏掉。为此,我们需要使用不同的异常处理程序定义多个except子句。但是,在任何时候,只会调用一个异常处理程序。用一个例子可以更好地解释这一点。我们来看看下面的代码片段: 该试块调用solve_something() 。此函数接受一个数字作为用户输入,并断言该数字大于零。如果断言失败,它会直接跳转到处理程序,但AssertionError除外。 在另一个场景中,如果> 0,则执行solve_something()中的其余代码。您会注意到未定义变量x,这会导致NameError。此异常由另一个异常子句处理,但NameError除外。同样,您可以为预期的错误定义特定的异常处理程序。 提高并重新提出异常Python中的raise关键字用于强制发生异常。换句话说,它引发了一个异常。语法很简单; 只需打开Python解释器并输入: >>> raise AssertionError("some error message")这会产生以下错误回溯: Traceback (most recent call last): File "", line 1, in AssertionError : some error message在某些情况下,我们需要重新引发异常。假设,在try子句中,您有一个将数字除以零的表达式。在普通算术中,这个表达没有意义。这是一个错误!这会导致程序引发一个名为ZeroDivisionError的异常。如果没有异常处理代码,程序将只打印错误消息并终止。 ...

May 13, 2019 · 1 min · jiezi

带你抛出优雅的处理系统异常

关于抛出异常如在我的上一篇文中所说的一样, 在接口的设计中, 接口的返回的数据是非常重要的, 例如无法避免的500等等, 这些都是要命的错误 同时还有一个极大的问题, 就是在新增模块中, 例如我最近需要新增一个 elasticsearh 的分词查询模块, 这个在添加索引删除索引等等操作的时候, 是非常容易导致抛出错误异常的. 按照平常的解决思路, 我们可能首先就是针对异常处理直接进行使用Exception进行捕获,/偷笑, 我在以前的时候也是非常喜欢这么做的 1) try catch (1) 代码案例: public function addIndex($data){ $params = [ 'index' => 'show', 'type' => 'store', 'body' => $data ]; try{ $this->client->index($params); }catch (\Exception $exception) { halt('参数错误的异常'); # 处理错误1 }}在我们初期的学习和开发当前,以上的方法的确是可行的,类似于我们常常说的 JQ一把梭 , 上面的便是 错误一刀切,这种方式的确是能够进行解决问题, 但是非常不利于针对某个错误的处理 例如 BadRequest400Exception 方法抛出的 getMessage() 的结果是json数据, 而 BadMethodCallException 抛出的是字符串数据 你告诉我怎么处理呢? 所以, 衍生了如下的第二种处理错误方式 2) try catch (2) ...

May 13, 2019 · 4 min · jiezi

springboot结合全局异常处理之登录注册验证

在学校做一个校企合作项目,注册登录这一块需要对注册登录进行输入合法的服务器端验证,因为是前后端分离开发,所以要求返回JSON数据。方法有很多,这觉得用全局异常处理比较容易上手 全局异常处理首先来创建一个sprIngboot的web项目或模块,目录结构如下 实体类User.java @Datapublic class User { private String userName; private String passwold;}实体类UserResult.java 把数据封装到这里返回到客户端 @Data@NoArgsConstructor@AllArgsConstructorpublic class UserResult { private int code; private String msg;}接下来自定义异常,都继承自ExceptionUserNullException.java 当用户名为空抛出这个异常 public class UserNullException extends Exception{ public UserNullException() { super("用户名不能为空"); }}PasswoldNullException.java 当密码为空抛出这个异常 public class PasswoldNullException extends Exception { public PasswoldNullException() { super("密码不能为空"); }}UserNamePasswordNullException.java 当用户名和密码都为空抛出这个异常 public class UserNamePasswordNullException extends Exception { public UserNamePasswordNullException() { super("请输入用户名和密码"); }}UserNameValidationException.jva 当输入不符合要求的用户名时抛出此异常 public class UserNameValidationException extends Exception{ public UserNameValidationException() { super("请输入6到16位的数字或字母组合"); }}UserNamePasswordNullException.java 当输入的密码不符合要求时抛出这个异常 ...

May 11, 2019 · 2 min · jiezi

SpringBoot-2X-Kotlin系列之数据校验和异常处理

在开发项目时,我们经常需要在前后端都校验用户提交的数据,判断提交的数据是否符合我们的标准,包括字符串长度,是否为数字,或者是否为手机号码等;这样做的目的主要是为了减少SQL注入攻击的风险以及脏数据的插入。提到数据校验我们通常还会提到异常处理,因为为了安全起见,后端出现的异常我们通常不希望直接抛到客户端,而是经过我们的处理之后再返回给客户端,这样做主要是提升系统安全性,另外就是给予用户友好的提示。定义实体并加上校验注解class StudentForm() { @NotBank(message = '生日不能为空') var birthday: String = "" @NotBlank(message = "Id不能为空") var id:String = "" @NotBlank(message = "年龄不能为空") var age:String = "" @NotEmpty(message = "兴趣爱好不能为空") var Interests:List<String> = Collections.emptyList() @NotBlank(message = "学校不能为空") var school: String = "" override fun toString(): String { return ObjectMapper().writeValueAsString(this) }}这里首先使用的是基础校验注解,位于javax.validation.constraints下,常见注解有@NotNull、@NotEmpty、@Max、@Email、@NotBank、@Size、@Pattern,当然出了这些还有很多注解,这里就不在一一讲解,想了解更多的可以咨询查看jar包。 这里简单讲解一下注解的常见用法: @NotNull: 校验一个对象是否为Null@NotBank: 校验字符串是否为空串@NotEmpty: 校验List、Map、Set是否为空@Email: 校验是否为邮箱格式@Max @Min: 校验Number或String是否在指定范围内@Size: 通常需要配合@Max @Min一期使用@Pattern: 配合自定义正则表达式校验定义返回状态枚举enum class ResultEnums(var code:Int, var msg:String) { SUCCESS(200, "成功"), SYSTEM_ERROR(500, "系统繁忙,请稍后再试"),}自定义异常这里主要是参数校验,所以定义一个运行时异常,代码如下: ...

April 30, 2019 · 2 min · jiezi

JavaScript之错误异常探讨

同步发布于 https://github.com/xianshanna…我的建议是不要隐藏错误,勇敢地抛出来。没有人会因为代码出现 bug 导致程序崩溃而羞耻,我们可以让程序中断,让用户重来。错误是无法避免的,如何去处理它才是最重要的。JavaScript 提供一套错误处理机制,错误是干扰程序正常流程的非正常的事故。而没人可以保持程序没有 bug,那么上线后遇到特殊的 bug,如何更快的定位问题所在呢?这就是我们这个专题需要讨论的问题。下面会从 JavaScript Error 基础知识、如何拦截和捕获异常、如何方便的在线上报错误等方面来叙述,本人也是根据网上的知识点进行了一些总结和分析(我只是互联网的搬运工,不是创造者),如果有什么错漏的情况,请在 issue 上狠狠的批评我。这个专题目前是针对浏览器的,还没考虑到 node.js,不过都是 JavaScript Es6 语法,大同小异。什么时候 JavaScript 会抛出错误呢?一般分为两种情况:JavaScript 自带错误开发者主动抛出的错误JavaScript 引擎自动抛出的错误大多数场景下我们遇到的错误都是这类错误。如果发生Javscript 语法错误、代码引用错误、类型错误等,JavaScript 引擎就会自动触发此类错误。如下一些场景:场景一console.log(a.notExited)// 浏览器会抛出 Uncaught ReferenceError: a is not defined场景二const a;// 浏览器抛出 Uncaught SyntaxError: Missing initializer in const declaration语法错误,浏览器一般第一时间就抛出错误,不会等到执行的时候才会报错。场景三let data;data.forEach(v=>{})// Uncaught TypeError: Cannot read property ‘forEach’ of undefined手动抛出的错误一般都是类库开发的自定义错误异常(如参数等不合法的错误异常抛出)。或者重新修改错误 message 进行上报,以方便理解。场景一function sum(a,b){ if(typeof a !== ’number’) { throw TypeError(‘Expected a to be a number.’); } if(typeof b !== ’number’) { throw TypeError(‘Expected b to be a number.’); } return a + b;}sum(3,’d’);// 浏览器抛出 uncaught TypeError: Expected b to be a number.场景二当然我们不一定需要这样做。let data;try { data.forEach(v => {});} catch (error) { error.message = ‘data 没有定义,data 必须是数组。’; error.name = ‘DataTypeError’; throw error;}如何创建 Error 对象?创建语法如下:new Error([message[,fileName,lineNumber]])省略 new 语法也一样。其中fileName 和 lineNumber 不是所有浏览器都兼容的,谷歌也不支持,所以可以忽略。Error 构造函数是通用错误类型,除了 Error 类型,还有 TypeError、RangeError 等类型。Error 实例这里列举的都是 Error 层的原型链属性和方法,更深层的原型链的继承属性和方便不做说明。一些有兼容性的而且不常用的属性和方法不做说明。console.log(Error.prototype)// 浏览器输出 {constructor: ƒ, name: “Error”, message: “”, toString: ƒ}其他错误类型构造函数是继承 Error,实例是一致的。属性Error.prototype.message错误信息, Error(“msg”).message === “msg”。Error.prototype.name错误类型(名字), Error(“msg”).name === “Error”。如果是 TypeError,那么 name 为 TypeError。Error.prototype.stackError 对象作为一个非标准的栈属性提供了一种函数追踪方式。无论这个函数被被调用,处于什么模式,来自于哪一行或者哪个文件,有着什么样的参数。这个栈产生于最近一次调用最早的那次调用,返回原始的全局作用域调用。这个不是规范,存在兼容性。经测试,谷歌、火狐、Edge、safar 都支持此特性(都是在最新的版本下测试 2019-04-02),IE 不支持。方法Error.prototype.constructorError.prototype.toString返回值格式为 ${name}: ${message}。常用 Error 类型除了通用的 Error 构造函数外,JavaScript还有常见的 5 个其他类型的错误构造函数。TypeError创建一个 Error 实例,表示错误的原因:变量或参数不属于有效类型。throw TypeError(“类型错误”);// Uncaught TypeError: 类型错误RangeError创建一个 Error 实例,表示错误的原因:数值变量或参数超出其有效范围。throw RangeError(“数值超出有效范围”);// Uncaught RangeError: 数值超出有效范围ReferenceError创建一个 Error 实例,表示错误的原因:无效引用。throw ReferenceError(“无效引用”);// Uncaught ReferenceError: 无效引用SyntaxError创建一个 Error 实例,表示错误的原因:语法错误。这种场景很少用,除非类库定义了新语法(如模板语法)。throw SyntaxError(“语法错误”);// Uncaught SyntaxError: 语法错误URIError创建一个 Error 实例,表示错误的原因:涉及到 uri 相关的错误。throw URIError(“url 不合法”);// Uncaught RangeError: url 不合法自定义 Error 类型自定义新的 Error 类型需要继承 Error ,如下自定义 CustomError:function CustomError(…args){ class InnerCustomError extends Error { name = “CustomError”; } return new InnerCustomError(…args);}继承 Error 后,我们只需要对 name 做重写,然后封装成可直接调用的函数即可。如何拦截 JavaScript 错误?既然没人能保证 web 应用不会出现 bug,那么出现异常报错时,如何拦截并进行一些操作呢?try…catch… 拦截这是拦截 JavaScript 错误,拦截后,如果不手动抛出错误,这个错误将静默处理。平常写代码如果我们知道某段代码可能会出现报错问题,就可以使用这种方式。如下:const { data } = this.props;try { data.forEach(d=>{}); // 如果 data 不是数组就会报错} catch(err){ console.error(err); // 这里可以做上报处理等操作}一些使用方式十分不友好的处理方式try…catch… 使用需要注意,try…catch… 后,错误会被拦截,如果不主动抛出错误,那么无法知道报错位置。如下面的处理方式就是不好的。function badHandler(fn) { try { return fn(); } catch (err) { /noop,不做任何处理/ } return null;}badHandler();这样 fn 回调发送错误后,我们无法知道错误是在哪里发生的,因为已经被 try…catch 了,那么如何解决这个问题呢?相对友好但糟糕的处理方式function CustomError(…args){ class InnerCustomError extends Error { name = “CustomError”; } return new InnerCustomError(…args);}function uglyHandlerImproved(fn) { try { return fn(); } catch (err) { throw new CustomError(err.message); } return null;}badHandler();现在,这个自定义的错误对象包含了原本错误的信息,因此变得更加有用。但是因为再度抛出来,依然是未处理的错误。try…catch… 可以拦截异步错误吗?这个也要分场景,也看个人的理解方向,首先理解下面这句话:try…catch 只会拦截当前执行环境的错误,try 块中的异步已经脱离了当前的执行环境,所以 try…catch… 无效。setTimeout 和 Promise 都无法通过 try…catch 捕获到错误,指的是 try 包含异步(非当前执行环境),不是异步包含 try(当前执行环境)。异步无效和有效 try…catch 如下:setTimeout这个无效:try { setTimeout(() => { data.forEach(d => {}); });} catch (err) { console.log(‘这里不会运行’);}下面的 try…catch 才会有效:setTimeout(() => { try { data.forEach(d => {}); } catch (err) { console.log(‘这里会运行’); }});Promise这个无效:try { new Promise(resolve => { data.forEach(d => {}); resolve(); });} catch (err) { console.log(‘这里不会运行’);}下面的 try…catch 才会有效:new Promise(resolve => { try { data.forEach(d => {}); } catch (err) { console.log(‘这里会运行’); }});小结不是所有场景都需要 try…catch… 的,如果所有需要的地方都 try…catch,那么代码将变得臃肿,可读性变差,开发效率变低。那么我需要统一获取错误信息呢?有没有更好的处理方式?当然有,后续会提到。Promise 错误拦截Promise.prototype.catch 可以达到 try…catch 一样的效果,只要是在 Promise 相关的处理中报错,都会被 catch 到。当然如果你在相关回调函数中 try…catch,然后做了静默提示,那么也是 catch 不到的。如下会被 catch 到:new Promise(resolve => { data.forEach(v => {});}).catch(err=>{/这里会运行/})下面的不会被 catch 到:new Promise(resolve => { try { data.forEach(v => {}); }catch(err){}}).catch(err=>{/这里不会运行/})Promise 错误拦截,这里就不详细说了,如果你看懂了 try…catch,这个也很好理解。setTimeout 等其他异步错误拦截呢?目前没有相关的方式直接拦截 setTimeout 等其他异步操作。如果要拦截 setTimeout 等异步错误,我们需要在异步回调代码中处理,如:setTimeout(() => { try { data.forEach(d => {}); } catch (err) { console.log(‘这里会运行’); }});这样可以拦截到 setTimeout 回调发生的错误,但是如果是下面这样 try…catch 是无效的:try { setTimeout(() => { data.forEach(d => {}); });} catch (err) { console.log(‘这里不会运行’);}如何获取 JavaScript 错误信息?你可以使用上面拦截错误信息的方式获取到错误信息。但是呢,你要每个场景都要去拦截一遍吗?首先我们不确定什么地方会发生错误,然后我们也不可能每个地方都去拦截错误。不用担心,JavaScript 也考虑到了这一点,提供了一些便捷的获取方式(不是拦截,错误还是会终止程序的运行,除非主动拦截了)。window.onerror 事件获取错误信息onerror 事件无论是异步还是非异步错误(除了 Promise 错误),onerror 都能捕获到运行时错误。需要注意一下几点:window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxxx。如果使用 addEventListener,event.preventDefault() 可以达到同样的效果。window.onerror 是无法捕获到网络异常的错误、<img/>或<script>资源加载失败等错误。不过如果你使用了 fetch 等支持 promise 的方式,错误可以通过 unhandledrejection 方式拿到错误信息。语法:window.onerrorwindow.onerror = function(message, source, lineno, colno, error) { … }window.addEventListener(’error’)window.addEventListener(’error’, function(event) { … })详细看这里。unhandledrejection 事件获取 Promise 错误unhandledrejection 存在兼容性问题,IE、Edge、火狐等目前都不支持。用法如下:window.addEventListener(“unhandledrejection”, event => { console.warn(UNHANDLED PROMISE REJECTION: ${event.reason});});或者window.onunhandledrejection = event => { console.warn(UNHANDLED PROMISE REJECTION: ${event.reason});};详细看这里。如何处理错误信息?错误对象 Error 包含如下的字段返回:name错误的类型,一般有 Error、TypeError、ReferenceError、RangeError、URIError 等,当然也可以是自定义的错误类型。message错误的详细信息,可以是 JavaScript 自动抛出的错误信息,也可以是手动抛出的自定义信息。stack这个不是规范,存在兼容性。经测试,谷歌、火狐、Edge、safar 都支持此特性(都是在最新的版本下测试 2019-04-02),IE 不支持。name 和 message 我们都不用做什么处理,主要是要针对 stack 做处理,一般我们需要把,这三个字段的信息提交到错误处理系统,针对性处理。同时我们生成环境的代码是被压缩后的代码,需要使用 sourceMap 进行映射还原代码。后续会补充这个讨论。参考文章Error(MDN)A Guide to Proper Error Handling in JavaScriptAnatomy of a JavaScript Error ...

April 3, 2019 · 3 min · jiezi

编程语言中的错误处理

在日常的编程过程中,不可避免地需要处理错误的情况,而每一种编程语言都自有其错误处理逻辑,其背后的考量是什么?下面来探讨一下各编程语言中的错误处理,尝试总结出一些通用的方法与原则。一、什么是异常讨论一个问题之前,第一步就是要明晰下它所涉及的概念。首先,标题所说的错误是广义的错误,它包括异常(Exception)与错误(Error)。下文中提到的『错误』均为狭义的区别与异常的错误。程序中的异常(Exception)是指发生在程序执行过程中非频繁非正常的事件,它位于程序正常流程之外。异常大致可分为两类:硬件异常:由CPU发起,它们可能是某些指令序列的执行导致的。比如除零或访问非法内存地址等。软件异常:由应用程序或操作系统显式发起。例如系统可以检测到指定的参数非法值。编程语言中的异常则属于软件异常。而程序中的错误(Error),通常是指发生在程序执行过程中正常的事件,它就在程序正常流程范围之内。二、正确区分异常与错误与异常概念最容易混淆的就是错误。二者通常可以通过下面三个维度来区分:是否正常/可预期/终止程序概念是否正常是否可预期是否终止程序异常否否是错误是是否前两个维度主要是对概念的描述,最后一个维度(是否终止程序,即是否可恢复)建议作为定义错误与异常的标准。如果一个事件它不可恢复应该定义为异常,及时终止程序退出,避免程序进入不可预知的状态(如造成数据不一致);如果一个事件可以预测出错误,那么就应该check,并做一些相应的恢复处理。如Golang中的Error与Panic就是遵循该原则而设计。三、错误处理区分了异常与错误,下一步则是考虑针对错误的处理机制。正确地区分了异常与错误的概念,我们就可以根据具体场景,正确地定义出异常与错误,以及安排相应的错误处理。通常,编程语言中的错误处理可以分为两类:check式,检查返回值,以C语言为代表,Go亦如此;try/catch式,目前大部分主流编程语言中的异常处理均采用类似方式,如C++/Java/PHP/JavaScript等。虽然目前主流编程语言中的异常处理均采用『try/catch』式的原则,但是大都数在写代码过程中都是双管齐下的,依据具体的场景,选择合适的处理方式(抛异常 or 检查返回值)。3.1 check式最早的C语言是通过检查函数返回值(通常零值/非空成功;非零值/空失败)来进行错误处理的。如定义一个函数:int foo() { // <try something here> if (failed) { return 1; } return 0;}调用者则在进行下一步操作之前,需要判断foo函数返回值:int err = foo();if (err) { // Error! Deal with it.} 基于C或者底层级别的系统均是通过这种检查返回值的方式来处理错误的。如Window和Linux操作系统级别的调用(API)。这种方式很简单,代码可读性也较好,但是写起来非常繁琐,这意味着你需要对每一个函数在调用之前的都需要手动check一下。而且,一旦忘记检查,很容易出现bug。Golang则在C语言的基础上增加了更符合现代编程语言的语法和库。它允许函数有两个返回值,通常最后一个返回值为Error类型,调用者可以通过检查该类型返回值来检查函数返回情况,没有错误则使用第二个返回值,继续接下来的业务逻辑操作。如:func foo() (int, error){ // <try something here> if (failed) { return -1, errors.New(“something error”) } return 0, nil;}调用:if sum, err := foo(); err != nil { // Deal with the error.}// do something with sum…Golang的实现方式看起来比C语言更加优雅一些,但是频繁地检查返回值仍然不可避免。C语言在不使用goto语句的情况下,异常代码复用几乎不可能,Golang也难以解决这个问题。于是在后来发展起来的面向对象编程语言中,大部分都引入了类似try/catch式的异常处理机制。3.2 try/catch式下面主要以Java语言举例说明Java中所有的错误处理均基于Throwable顶层父类,其下有两个子类:Error,它表示不希望被程序捕获或者是程序无法处理的错误。另一个是Exception,它表示用户程序可能捕捉的异常情况或者说是程序可以处理的异常。这是Java对异常与错误的划分,并且在此基础上为进一步提高程序健壮性,引入了checked异常与unchecked异常概念:针对那些除了RuntimeException与其子类,以及错误(Error),其他的都是需要编译时强制检查的异常,否则编译器会报错。try{ method();}catch (IOException ioe){ System.out.println(“I/O failure”);}// …void method() throws IOException{ throw new IOException(“some text”);}也正是Java的这种处理方式让人诟病:checked异常容易让相关代码里充斥着大量的try/catch,使代码同样变得晦涩难懂。同时,checked异常所起到的作用也只是将捕获的异常,包装成运行时异常,然后再重新抛出。正如,前文所言,每一门编程语言在设计之初都有自身的考量,且在进行实际的错误处理时均会同时考虑『check』与『try/catch』两种方式。在C#中,并没有引入checked异常概念,而是把检查的义务又『还给』了开发者。除此之外,try/catch式异常处理通常会有很大的性能开销,故应当慎用。四、『check』 OR『try/catch』即使发展至今,关于异常处理(『try/catch』)与检查返回值(『check』)这两种错误处理方式仍然争议不断。check式与try/catch式两种错误处理的方式,没有哪一种是绝对优势的,都有各自的优缺点,这有赖于语言设计者当时的权衡与抉择。但是不管哪种编程语言,基本衍生于这两类处理方式。REFERENCEShttp://www.cs.tut.fi/~popl/ny…http://joeduffyblog.com/2016/...https://www.zhihu.com/questio...https://nedbatchelder.com/tex...https://www.javaworld.com/art… ...

March 18, 2019 · 1 min · jiezi

基于实时计算(Flink)与高斯模型构建实时异常检测系统

案例与解决方案汇总页:阿里云实时计算产品案例&解决方案汇总1. 概述异常检测(anomaly detection)指的是对不符合预期模式或数据集(英语:dataset)中其他项目的项目、事件或观测值的识别。实际应用包括入侵检测、欺诈检测、故障检测、系统健康监测、传感器网络事件检测和生态系统干扰检测等。之前我曾经介绍过一种异常检测的解决方案《准实时异常检测系统》,但那个架构中Flink主要承担的还是检测后的分析,真正的异常检测被前置到了业务系统中。在本文中,我将介绍一种直接使用Flink做实时异常检测的方案。2. 异常检测算法2.1 异常的种类异常(离群点)分为三种类型:全局离群点,最基本的异常,即一个单独的远离群体的点;情境(或条件)离群点,该点在全局不算异常,但在某个上下文中却是异常的,比如人的性别为男性不是异常,但如果选定范围为女厕所,那么这个就是异常的;集体离群点,单个点不算异常,但一系列组合起来却是异常。比如偶尔的服务延迟不是异常,但如果整个系统大部分服务都延迟那就是异常。本文以说明基本原理为主,所以使用最简单的全局离群点做例子,即只关注检测某个单独的事件是否偏离正常。完成的异常分类可参考:这里2.2 异常监测算法关于异常检测有大量的算法,详细理论可参考scikit-learn的《 Novelty and Outlier Detection》一章。本文选取最简单的一种算法,基于高斯分布分布的异常检测算法。假设我们已经有了一组正常数据,x(1),x(2),..,x(m),那么针对新的数据x,我们判断这个x是否正常,可以计算x在正常数据中出现的概率如何,如果x出现的概率大于某个阈值,则为正常,否则即为异常,这种方法叫做密度估计。那么我们可以假设,这些数据遵循高斯分布(正态分布),那么对某个特定的值来说,其在高斯分布的中间部分是比较正常的,在两端可能是异常的。通常如果我们认为变量 x 符合高斯分布 x~N(,2),则其概率密度函数为:异常检测算法的步骤为:对于给定的数据集 x(1),x(2),…,x(m),针对每一个特征计算 和 2 的估计值,计算方法如下。一旦我们获得了每个特征的平均值和方差的估计值,给定新的一个训练实例,根据模型计算每一特征的概率再相乘得到整体的概率:注:可能你要检测的事件只有一个特征,那么很显然就不用再乘了。选择一个阈值 ,将 p(x)= 作为判定边界,当 p(x)> 时预测数据为正常数据,否则为异常,这样就完成了整个异常检测过程。注:阈值的选择可以直接估算一个,也可以简单训练得出,具体训练方式这里不再赘述,可参考这里。总结一下,其实整个模型我们只需要计算正常数据中每个特征的平均值和方差,再加上最终整体阈值,所以模型是非常小的,完全可以把这些数据计算出来后随代码一起发布。(当然从解耦性来说,最好能够独立存储,通过注册或配置的方式来发布)3. 基于Flink和高斯分布的实时异常检测系统前面介绍了异常检测的基本算法,那么本小节我们就基于Flink和高斯分布设计一个实时异常检测系统。假设你是一个公司的运维人员,负责管理全公司的IT资源,为了保证公司IT稳定性,提前发现主机或者系统的问题,你设计了这样一个实时异常检测系统。系统采用Kapp架构,关于Kappa架构的说明可参考:数据仓库介绍与实时数仓案例。系统架构与所选软件如下图所示:数据源包括两个部分,主机运行信息与系统的运行日志,主机运行信息通过collectd 收集,系统运行日志通过Filebeat收集,二者均将数据推送到Kafka。数据通过Kafka流转,支持Flink计算过程中的实时分层。最终数据存储到Elastic Search中,并通过KIBANA可视化。异常检测由实时计算Flink完成,计算过程很简单:数据清洗,把原始数据格式化;计算特征值,计算所选事件的特征,比如某个服务打印日志的频率就是一个特征,假如系统调用失败,则会打印一条失败记录,那么当系统打印失败记录的频率变高时,系统可能出现了问题;计算特征统计值,即找到该特征的高斯分布(确定平均值和方差即可确定高斯分布);这里高斯分布直接在线计算,好处是随时可更新,没有显式的训练过程,缺点是可能受异常数据影响。另外一种方式是离线选取一些正常数据然后计算高斯分布。检测异常值,利用2.2节中的算法原理检测异常事件;输出,最后把检测出的异常数据写到下游;好了,一个简单的实时异常检测系统就完成了。4. 总结在本文中,在Kappa架构上添加简单的异常检测算法即可完成一个简单有效的实时异常检测系统。该架构具备良好的可扩展性,基于Flink的Kappa架构让系统能够应对超大规模数据流,并且能够在数据流转的过程中完成处理。此外,虽然本文中直接把异常检测算法内置到了Flink的逻辑中,但实际的应用中很容易把通过算法API的方式让系统解耦:算法团队训练并提供一个用以判别某个事件或特征是否异常的算法服务,在Flink的处理过程中计算好特征值之后调用这个算法服务进行检测。该方案来自真实的案例,如果有兴趣进一步了解,可参考下边资料:准实时异常检测系统数据仓库介绍与实时数仓案例实时欺诈检测(风控)Novelty and Outlier DetectionApplying the Kappa architecture in the telco industry,Kappa architecture and Bayesian models yield quick, accurate analytics in cloud monitoring systems.Anomaly detection高斯分布、异常检测Outlier Analysis: A Quick Guide to the Different Types of Outliers本文作者:付空阅读原文本文为云栖社区原创内容,未经允许不得转载。

March 6, 2019 · 1 min · jiezi

Result对象 + 统一异常处理

错误异常码设计1.1 统一异常码接口定义/** * 统一异常码接口定义 * * @author 王洪玉 * @date 2018/11/11 /public interface ExceptionEnum { /* * 获取异常编码 * * @return 异常码 / Integer getCode(); /* * 获取异常信息 * * @return 异常信息 / String getMessage();}1.2. 通用异常错误码Enum/* * 全局异常错误码 /public enum ResultMsgEnum implements ExceptionEnum { // 请求成功 SUCCESS(200,“成功”), // 服务器内部错误 ERROR(500,“失败”); private int code; private String message; ResultMsgEnum(int value, String text) { this.code = value; this.message = text; } @Override public Integer getCode() { return code; } @Override public String getMessage() { return message; }}1.3. 业务异常错误码Enum/* * CMS系统错误异常码 5001** /public enum CmsErrorCodeEnum implements ExceptionEnum { // 文章错误 Article_NOT_EXIST(500100,“该文章不存在”); CmsErrorCodeEnum(Integer code, String message) { this.code = code; this.message = message; } private Integer code; private String message; @Override public Integer getCode() { return code; } @Override public String getMessage() { return message; }}2. Result对象设计/* * 通用返回result /@Datapublic class ZingResult<T> implements Serializable { private int code; private String msg; private T data; private ZingResult() { this.code = ResultMsgEnum.SUCCESS.getCode(); this.msg = ResultMsgEnum.SUCCESS.getMessage(); } private ZingResult(T data) { this.code = ResultMsgEnum.SUCCESS.getCode(); this.msg = ResultMsgEnum.SUCCESS.getMessage(); this.data = data; } private ZingResult(ExceptionEnum exceptionEnum) { this.code = exceptionEnum.getCode(); this.msg = exceptionEnum.getMessage(); } public static ZingResult success() { return new ZingResult(); } public static <T> ZingResult<T> success(T data) { return new ZingResult<>(data); } public static <T> ZingResult<T> error(ExceptionEnum exceptionEnum) { return new ZingResult<>(exceptionEnum); }}3. 统一异常处理3.1 异常父类/* * 异常父类 * * @author 王洪玉 * @date 2018/11/11 /@Getterpublic class ZingException extends RuntimeException { private Integer code; private String message; public ZingException(ExceptionEnum exceptionEnum){ super(exceptionEnum.getMessage()); this.code = exceptionEnum.getCode(); this.message = exceptionEnum.getMessage(); }}3.2 业务异常类/* * 业务自定义异常类 /@Getterpublic class BusinessException extends ZingException { private ExceptionEnum exceptionEnum; public BusinessException(ExceptionEnum exceptionEnum) { super(exceptionEnum); this.exceptionEnum = exceptionEnum; }}3.3 异常拦截处理@Slf4j@ResponseBody@ControllerAdvicepublic class GlobalExceptionHandler { /* * 拦截未知错误异常 * * @param request 请求 * @param e 未知异常 * @return 通用返回格式 / @ExceptionHandler(Exception.class) public ZingResult cmsException(HttpServletRequest request, Exception e) { log.error(“请求的url为{}出现系统异常,异常信息为:”, request.getRequestURI(), e); return ZingResult.error(ResultMsgEnum.ERROR); } /* * 拦截CMS业务异常 * * @param request 请求 * @param e 业务异常 * @return 通用返回格式 */ @ExceptionHandler(CmsBusinessException.class) public ZingResult cmsBusinessException(HttpServletRequest request, CmsBusinessException e) { log.error(“请求的url为{}出现业务异常,异常信息为:”, request.getRequestURI(), e); return ZingResult.error(e.getExceptionEnum()); }}4. 使用示例if(CollectionUtils.isEmpty(articleList)){ throw new CmsBusinessException(CmsErrorCodeEnum.ARTICLE_NOT_EXIST);}

February 25, 2019 · 2 min · jiezi

怎样针对JavaScript中的异步函数进行异常处理及测试

翻译:疯狂的技术宅原文:https://www.valentinog.com/bl…本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章可以在 Javascript 的异步函数中抛出错误吗?这个话题已被反复提起过几百次,不过这次让我们从TDD(Test-Driven Development)的角度来回答它。如果你能不在Stackoverflow上搜索就能回答这个问题,会给我留下深刻的印象。如果不能的话也可以很酷。 继续往下读,你就能学到!你将学到什么通过后面的内容你将学到:如何从 Javascript 的异步函数中抛出错误如何使用 Jest 测试来自异步函数的异常要求要继续往下读你应该:对 Javascript 和 ES6 有基本的了解安装 Node.Js 和 Jest如何从 Javascript 的常规函数中抛出错误使用异常而不是返回码(清洁代码)。抛出错误是处理未知的最佳方法。同样的规则适用于各种现代语言:Java、Javascript、Python、Ruby。你可以从函数中抛出错误,可以参照以下示例:function upperCase(name) { if (typeof name !== “string”) { throw TypeError(“name must be a string”); } return name.toUpperCase();}module.exports = upperCase;这是对它的测试(使用Jest):“use strict”;const assert = require(“assert”);const upperCase = require("../function");describe(“upperCase function”, () => { test(“it throws when name is not provided”, () => { assert.throws(() => upperCase()); }); test(“it throws when name is not a string”, () => { assert.throws(() => upperCase(9)); });});也可以从 ES6 的类中抛出错误。在 Javascript 中编写类时,我总是在构造函数中输入意外值。下面是一个例子:class Person { constructor(name) { if (typeof name !== “string”) { throw TypeError(“name must be a string”); } this.name = name; } // some method here}module.exports = Person;以下是该类的测试:“use strict”;const assert = require(“assert”);const Person = require("../index");describe(“Person class”, () => { test(“it throws when name is not provided”, () => { assert.throws(() => new Person()); }); test(“it throws when name is not a string”, () => { assert.throws(() => new Person(9)); });});测试确实通过了:PASS test/index.test.js Person class ✓ it throws when name is not provided (1ms) ✓ it throws when name is not a string安排的明明白白!所以无论异常是从常规函数还是从类构造函数(或从方法)抛出的,一切都会按照预期工作。但是如果我想从异步函数中抛出错误怎么办?我可以在测试中使用assert.throws吗?各位看官请上眼!测试异常既然都看到这里了,所以你应该知道什么是 Javascript 的异步函数,对吗?先看一段代码:class Person { constructor(name) { if (typeof name !== “string”) { throw TypeError(“name must be a string”); } this.name = name; } // some method here}module.exports = Person;假设你要添加异步方法来获取有关该人的数据。这种方法需要一个网址。如果url不是字符串,就要像上一个例子中那样抛出错误。先来修改一下这个类:class Person { constructor(name) { if (typeof name !== “string”) { throw TypeError(“name must be a string”); } this.name = name; } async getData(url) { if (typeof url !== “string”) { throw TypeError(“url must be a string”); } // const response = await fetch(url) // do stuff }}module.exports = Person;如果我运行代码会怎么样?试试吧:const Person = require("../index");const valentinogagliardi = new Person(“valentinogagliardi”);valentinogagliardi.getData();结果是这样UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: name must be a stringDeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.果然不出所料,异步方法返回了一个Promise rejection,从严格意义上来讲,并没有抛出什么东西。错误被包含在了Promise rejection中。换句话说,我不能使用 assert.throws 来测试它。让我们通过测试来验证一下:“use strict”;const assert = require(“assert”);const Person = require("../index");describe(“Person methods”, () => { test(“it throws when url is not a string”, () => { const valentinogagliardi = new Person(“valentinogagliardi”); assert.throws(() => valentinogagliardi.getData()); });});测试失败了!FAIL test/index.test.js Person methods › it throws when url is not a string assert.throws(function) Expected the function to throw an error. But it didn’t throw anything. Message: Missing expected exception.有没有悟出点什么?看把你能的,来抓我啊从严格意义上讲异步函数和异步方法不会抛出错误。异步函数和异步方法总是返回一个Promise,无论它已完成还是被拒绝,你必须附上 then() 和 catch(),无论如何。(或者将方法包装在try/catch中)。被拒绝的Promise将会在堆栈中传播,除非你抓住(catch)它。至于测试代码,应该这样写:“use strict”;const assert = require(“assert”);const Person = require("../index");describe(“Person methods”, () => { test(“it rejects when url is not a string”, async () => { expect.assertions(1); const valentinogagliardi = new Person(“valentinogagliardi”); await expect(valentinogagliardi.getData()).rejects.toEqual( TypeError(“url must be a string”) ); });});我们测试的不能是普通的异常,而是带有TypeError的rejects。现在测试通过了:PASS test/index.test.js Person methods ✓ it rejects when url is not a string那代码该怎么写呢?为了能够捕获错误,你应该这样重构:const Person = require("../index");const valentinogagliardi = new Person(“valentinogagliardi”);valentinogagliardi .getData() .then(res => res) .catch(err => console.error(err));现在异常将会出现在控制台中:TypeError: url must be a string at Person.getData (/home/valentino/Documenti/articles-and-broadcasts/throw-from-async-functions-2018-04-02/index.js:12:13) at Object.<anonymous> (/home/valentino/Documenti/articles-and-broadcasts/throw-from-async-functions-2018-04-02/index.js:22:4) // …如果你想要更多的try/catch.,有一件重要的事需要注意。下面的代码不会捕获错误:const Person = require("../index");async function whatever() { try { const valentinogagliardi = new Person(“valentinogagliardi”); await valentinogagliardi.getData(); // do stuff with the eventual result and return something } catch (error) { throw Error(error); }}whatever();记住:被拒绝的Promise会在堆栈中传播,除非你抓住(catch)它。要在 try/catch 中正确捕获错误,可以像这样重构:async function whatever() { try { const valentinogagliardi = new Person(“valentinogagliardi”); await valentinogagliardi.getData(); // do stuff with the eventual result and return something } catch (error) { throw Error(error); }}whatever().catch(err => console.error(err));这就是它的工作原理。总结最后总结一下:从异步函数抛出的错误不会是“普通的异常”。异步函数和异步方法总是返回一个Promise,无论是已解决还是被拒绝。要拦截异步函数中的异常,必须使用catch()。以下是在Jest中测试异常的规则:使用 assert.throws 来测试普通函数和方法中的异常使用 expect + rejects 来测试异步函数和异步方法中的异常如果你对如何使用 Jest 测试 Koa 2 感兴趣,请查看使用Jest和Supertest进行测试的简绍这篇文章。感谢阅读!本文首发微信公众号:jingchengyideng欢迎关注,每天都给你推送新鲜的前端技术文章 ...

January 25, 2019 · 3 min · jiezi

四种检测异常值的常用技术简述

摘要: 本文介绍了异常值检测的常见四种方法,分别为Numeric Outlier、Z-Score、DBSCAN以及Isolation Forest在训练机器学习算法或应用统计技术时,错误值或异常值可能是一个严重的问题,它们通常会造成测量误差或异常系统条件的结果,因此不具有描述底层系统的特征。实际上,最佳做法是在进行下一步分析之前,就应该进行异常值去除处理。在某些情况下,异常值可以提供有关整个系统中局部异常的信息;因此,检测异常值是一个有价值的过程,因为在这个工程中,可以提供有关数据集的附加信息。目前有许多技术可以检测异常值,并且可以自主选择是否从数据集中删除。在这篇博文中,将展示KNIME分析平台中四种最常用的异常值检测的技术。数据集和异常值检测问题本文用于测试和比较建议的离群值检测技术的数据集来源于航空公司数据集,该数据集包括2007年至2012年间美国国内航班的信息,例如出发时间、到达时间、起飞机场、目的地机场、播出时间、出发延误、航班延误、航班号等。其中一些列可能包含异常值。从原始数据集中,随机提取了2007年和2008年从芝加哥奥黑尔机场(ORD)出发的1500次航班样本。为了展示所选择的离群值检测技术是如何工作的,将专注于找出机场平均到达延误的异常值,这些异常值是在给定机场降落的所有航班上计算的。我们正在寻找那些显示不寻常的平均到达延迟时间的机场。四种异常值检测技术数字异常值|Numeric Outlier数字异常值方法是一维特征空间中最简单的非参数异常值检测方法,异常值是通过IQR(InterQuartile Range)计算得的。计算第一和第三四分位数(Q1、Q3),异常值是位于四分位数范围之外的数据点x i:使用四分位数乘数值k=1.5,范围限制是典型的上下晶须的盒子图。这种技术是使用KNIME Analytics Platform内置的工作流程中的Numeric Outliers节点实现的(见图1)。Z-scoreZ-score是一维或低维特征空间中的参数异常检测方法。该技术假定数据是高斯分布,异常值是分布尾部的数据点,因此远离数据的平均值。距离的远近取决于使用公式计算的归一化数据点z i的设定阈值Zthr:其中xi是一个数据点,是所有点xi的平均值,是所有点xi的标准偏差。然后经过标准化处理后,异常值也进行标准化处理,其绝对值大于Zthr:Zthr值一般设置为2.5、3.0和3.5。该技术是使用KNIME工作流中的行过滤器节点实现的(见图1)。DBSCAN该技术基于DBSCAN聚类方法,DBSCAN是一维或多维特征空间中的非参数,基于密度的离群值检测方法。在DBSCAN聚类技术中,所有数据点都被定义为核心点(Core Points)、边界点(Border Points)或噪声点(Noise Points)。核心点是在距离ℇ内至少具有最小包含点数(minPTs)的数据点;边界点是核心点的距离ℇ内邻近点,但包含的点数小于最小包含点数(minPTs);所有的其他数据点都是噪声点,也被标识为异常值;从而,异常检测取决于所要求的最小包含点数、距离ℇ和所选择的距离度量,比如欧几里得或曼哈顿距离。该技术是使用图1中KNIME工作流中的DBSCAN节点实现的。孤立森林|Isolation Forest该方法是一维或多维特征空间中大数据集的非参数方法,其中的一个重要概念是孤立数。孤立数是孤立数据点所需的拆分数。通过以下步骤确定此分割数:随机选择要分离的点“a”;选择在最小值和最大值之间的随机数据点“b”,并且与“a”不同;如果“b”的值低于“a”的值,则“b”的值变为新的下限;如果“b”的值大于“a”的值,则“b”的值变为新的上限;只要在上限和下限之间存在除“a”之外的数据点,就重复该过程;与孤立非异常值相比,它需要更少的分裂来孤立异常值,即异常值与非异常点相比具有更低的孤立数。因此,如果数据点的孤立数低于阈值,则将数据点定义为异常值。阈值是基于数据中异常值的估计百分比来定义的,这是异常值检测算法的起点。有关孤立森林技术图像的解释,可以在此找到详细资料。通过在Python Script中使用几行Python代码就可以实现该技术。from sklearn.ensemble import IsolationForestimport pandas as pdclf = IsolationForest(max_samples=100, random_state=42)table = pd.concat([input_table[‘Mean(ArrDelay)’]], axis=1)clf.fit(table)output_table = pd.DataFrame(clf.predict(table))```pythonPython Script节点是KNIME Python Integration的一部分,它允许我们将Python代码编写/导入到KNIME工作流程。在KNIME工作流程中实施KNIME Analytics Platform是一个用于数据科学的开源软件,涵盖从数据摄取和数据混合、数据可视化的所有数据需求,从机器学习算法到数据应用,从报告到部署等等。它基于用于可视化编程的图形用户界面,使其非常直观且易于使用,大大减少了学习时间。此外,它被设计为对不同的数据格式、数据类型、数据源、数据平台以及外部工具(例如R和Python)开放,还包括许多用于分析非结构化数据的扩展,如文本、图像或图形。KNIME Analytics Platform中的计算单元是小彩色块,名为“节点”。一个接一个地组装管道中的节点,实现数据处理应用程序。管道也被称为“工作流程”。鉴于所有这些特性,本文选择它来实现上述的四种异常值检测技术。图1中展示了异常值检测技术的工作流程。工作流程:1.读取Read data metanode中的数据样本;2.进行数据预处理并计算Preproc元节点内每个机场的平均到达延迟;3.在下一个名为密度延迟的元节点中,对数据进行标准化,并将标准化平均到达延迟的密度与标准正态分布的密度进行对比;4.使用四种选定的技术检测异常值;5.使用KNIME与Open Street Maps的集成,在MapViz元节点中显示美国地图中的异常值机场。检测到的异常值在图2-5中,可以看到通过不同技术检测到的异常值机场。其中。蓝色圆圈表示没有异常行为的机场,而红色方块表示具有异常行为的机场。平均到达延迟时间定义的大小了记。一些机场一直被四种技术确定为异常值:斯波坎国际机场(GEG)、伊利诺伊大学威拉德机场(CMI)和哥伦比亚大都会机场(CAE)。斯波坎国际机场(GEG)具有最大的异常值,平均到达时间非常长(180分钟)。然而,其他一些机场仅能通过一些技术来识别、例如路易斯阿姆斯特朗新奥尔良国际机场(MSY)仅被孤立森林和DBSCAN技术所发现。对于此特定问题,Z-Score技术仅能识别最少数量的异常值,而DBSCAN技术能够识别最大数量的异常值机场。且只有DBSCAN方法(MinPts = 3/ℇ= 1.5,欧几里德距离测量)和孤立森林技术(异常值的估计百分比为10%)在早期到达方向发现异常值。总结本文在一维空间中描述并实施了四种不同的离群值检测技术:2007年至2008年间所有美国机场的平均到达延迟。研究的四种技术分别是Numeric Outlier、Z-Score、DBSCAN和Isolation Forest方法。其中一些用于一维特征空间、一些用于低维空间、一些用于高维空间、一些技术需要标准化和检查维度的高斯分布。而有些需要距离测量,有些需要计算平均值和标准偏差。有三个机场,所有异常值检测技术都能将其识别为异常值。但是,只有部分技术(比如,DBSCAN和孤立森林)可以识别分布左尾的异常值,即平均航班早于预定到达时间到达的那些机场。因此,应该根据具体问题选择合适的检测技术。参考Santoyo, Sergio. (2017, September 12). A Brief Overview of Outlier Detection Techniques;本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。

December 10, 2018 · 1 min · jiezi

前端异常监控-看这篇就够了

前端异常监控如果debug是移除bug的流程,那么编程就一定是将bug放进去的流程。如果没有用户反馈问题,那就代表我们的产品棒棒哒,对不对?主要内容Web规范中相关前端异常异常按照捕获方式分类异常的捕获方式日志上报的方式前端异常类型(Execption)WebIDL和ecma-262中的错误类型ECMAScript exceptions <==> IDL 的简单异常当脚本代码运行时发生的错误,会创建Error对象,并将其抛出,除了通用的Error构造函数外,以下是另外几个ECMAScript 2015中定义的错误构造函数。EvalError eval错误RangeError 范围错误ReferenceError 引用错误TypeError 类型错误URIError URI错误SyntaxError 语法错误 (这个错误WebIDL中故意省略,保留给ES解析器使用)Error 通用错误 (这个错误WebIDL中故意省略,保留给开发者使用使用)DOMException 最新的DOM规范定义的错误类型集,兼容旧浏览的DOMError接口, 完善和规范化DOM错误类型。IndexSizeError 索引不在允许的范围内HierarchyRequestError 节点树层次结构是不正确的。WrongDocumentError 对象是错误的InvalidCharacterError 字符串包含无效字符。NoModificationAllowedError 对象不能被修改。NotFoundError 对象不能在这里被找到。NotSupportedError 不支持的操作InvalidStateError 对象是一个无效的状态。SyntaxError 字符串不匹配预期的模式InvalidModificationError 对象不能以这种方式被修改NamespaceError 操作在XML命名空间内是不被允许的InvalidAccessError 对象不支持这种操作或参数。TypeMismatchError 对象的类型不匹配预期的类型。SecurityError 此操作是不安全的。NetworkError 发生网络错误AbortError 操作被中止URLMismatchError 给定的URL不匹配另一个URL。QuotaExceededError 已经超过给定配额。TimeoutError 操作超时。InvalidNodeTypeError 这个操作的 节点或节点祖先 是不正确的DataCloneError 对象不能克隆。前端错误异常按照捕获方式分类[x] 运行时异常语法错误[x] 资源加载异常imgscriptlinkaudiovideoiframe…外链资源的DOM元素[x] 异步请求异常XMLHttpRequestfetch[x] Promise异常[ ] CSS中资源异常@font-facebackground-image…暂时无法捕获前端错误异常的捕获方式try-catch (ES提供基本的错误捕获语法)只能捕获同步代码的异常回调setTimeoutpromisewindow.onerror = cb (DOM0)imgscriptlinkwindow.addEventListener(’error’, cb, true) (DOM2)window.addEventListener(“unhandledrejection”, cb) (DOM4)Promise.then().catch(cb)封装XMLHttpRequest&fetch | 覆写请求接口对象try-catch-finally将能引发异常的代码块放到try中,并对应一个响应,然后有异常会被捕获 try { // 模拟一段可能有错误的代码 throw new Error(“会有错误的代码块”) } catch(e){ // 捕获到try中代码块的错误得到一个错误对象e,进行处理分析 report(e) } finally { console.log(“finally”) }onerror事件window.onerror当JavaScript运行时错误(包括语法错误)发生时,window会触发一个ErrorEvent接口的事件,并执行window.onerror()/** * @description 运行时错误处理器 * @param {string} message 错误信息 * @param {string} source 发生错误的脚本URL * @param {number} lineno 发生错误的行号 * @param {number} colno 发生错误的列号 * @param {object} error Error对象 /function err(message,source,lineno,colno,error) {…}window.onerror = errelement.onerror当一项资源(如<img>或<script>)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。element.onerror = function(event) { … } //注意和window.onerror的参数不同注意:这些error事件不会向上冒泡到window,不过能被单一的window.addEventListener捕获。window.addEventListeneraddEventListener相关的一些内容W3C DOM2 Events规范中提供的注册事件监听器的方法, 在这之前均使用el.onclick的形式(DOM0 规范的基本内容,几乎所有浏览器都支持)。注意: 接口的几种语法error事件捕获资源加载错误资源加载失败,不会冒泡,但是会被addEventListener捕获,所以我们可以指定在加载失败事件的捕获阶段捕获该错误。注意: 接口同时也能捕获运行时错误。window.addEventListener(“error”, function(e) { var eventType = [].toString.call(e, e); if (eventType === “[object Event]”) { // 过滤掉运行时错误 // 上报加载错误 report(e) } }, true);unhandledrejection事件捕获Promise异常最新的规范中定义了 unhandledrejection事件用于全局捕获promise对象没有rejection处理器时异常情况。window.addEventListener(“unhandledrejection”, function (event) { // …your code here to handle the unhandled rejection… // Prevent the default handling (error in console) event.preventDefault();});Promise.then().catch(cb).finally()Promise中的错误会被Promise.prototype.catch捕获,所以我们通过这种方式捕获错误,这包括一些不支持unhandledrejection事件的环境中promisede polyfill实现。new Promise(function(resolve, reject) { throw ‘Uncaught Exception!’;}).catch(function(e) { console.log(e); // Uncaught Exception!});封装XMLHttpRequest&fetch | 覆写请求接口对象// 覆写XMLHttpRequest APIif(!window.XMLHttpRequest) return; var xmlhttp = window.XMLHttpRequest; var _oldSend = xmlhttp.prototype.send; var _handleEvent = function (event) { if (event && event.currentTarget && event.currentTarget.status !== 200) { report(event) } } xmlhttp.prototype.send = function () { if (this[‘addEventListener’]) { this[‘addEventListener’](’error’, _handleEvent); this[‘addEventListener’](’load’, _handleEvent); this[‘addEventListener’](‘abort’, _handleEvent); this[‘addEventListener’](‘close’, _handleEvent); } else { var _oldStateChange = this[‘onreadystatechange’]; this[‘onreadystatechange’] = function (event) { if (this.readyState === 4) { _handleEvent(event); } _oldStateChange && _oldStateChange.apply(this, arguments); }; } return _oldSend.apply(this, arguments); }// 覆写fetch APIif (!window.fetch) return;var _oldFetch = window.fetch;window.fetch = function() { return _oldFetch .apply(this, arguments) .then(function(res){ if (!res.ok) { // True if status is HTTP 2xx report(res) } return res; }) .catch(function(error){ report(res) });}日志上报的方式异步请求上报, 后端提供接口,或者直接发到日志服务器img请求上报, url参数带上错误信息eg:(new Image()).src = ‘http://baidu.com/tesjk?r=tksjk'注意跨源脚本异常当加载自不同域的脚本中发生语法错误时,为避免信息泄露,语法错误的细节将不会报告,而代之简单的 “Script error.“由于同源策略影响,浏览器限制跨源脚本的错误访问,这样跨源脚本错误报错信息如下图:在H5的规定中,只要满足下面俩个条件,是允许获取跨源脚本的错误信息的。客户端在script标签上增加crossorigin属性;服务端设置js资源响应头Access-Control-Origin:(或者是域名)。扩展阅读业界已经有的监控平台Sentry开源阿里的ARMSfundebugFrontJS几个异常监控的问题如何保证大家提交的代码是符合预期的? 如何了解前端项目的运行是否正常,是否存在错误?代码质量体系控制和错误监控以及性能分析如果用户使用网页,发现白屏,现在联系上了你们,你们会向他询问什么信息呢?先想一下为什么会白屏?我们以用户访问页面的过程为顺序,大致排查一下用户没打开网络DNS域名劫持http劫持cdn或是其他资源文件访问出错服务器错误前端代码错误前端兼容性问题用户操作出错通过以上可能发生错误的环节,我们需要向用户手机一下以下的用户信息当前的网络状态运营商地理位置访问时间客户端的版本(如果是通过客户端访问)系统版本浏览器信息设备分辨率页面的来源用户的账号信息通过performance API收集用户各个页面访问流程所消耗的时间收集用户js代码报错的信息如果我们使用了脚本代码压缩,然而我们又不想将sourcemap文件发布到线上,我们怎么捕获到错误的具体信息?CSS文件中也存在引用资源,@font-face, background-image …等这些请求错误该如何进行错误捕获?总结Web规范中相关前端异常DOM处理异常ECMAScript处理异常异常按照捕获方式分类运行时异常资源加载异常异步请求异常Promise异常异常的捕获方式try-catch (ES提供基本的错误捕获语法)只能捕获同步代码的异常回调setTimeoutpromisewindow.onerror = cb (DOM0)imgscriptlinkwindow.addEventListener(’error’, cb, true) (DOM2)window.addEventListener(“unhandledrejection”, cb) (DOM4)Promise.then().catch(cb)封装XMLHttpRequest&fetch | 覆写请求接口对象注意点:跨源脚本异常的捕获日志上报的方式异步请求上报new img上报扩展阅读业界已有的异常监控平台几个跟异常监控有关的问题 ...

November 9, 2018 · 2 min · jiezi