You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2020/01/08 05:43:29 UTC
[skywalking-client-js] 11/29: feat: add monitor
This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-client-js.git
commit b0560f85521523134e38e6efe36d5f5effed9a94
Author: fine <fi...@outlook.com>
AuthorDate: Sat Dec 7 14:41:25 2019 +0800
feat: add monitor
---
.travis.yml | 15 ++++
src/errors/ajaxErrors.ts | 63 +++++++++++++++++
src/errors/jsErrors.ts | 36 ++++++++++
src/monitor.ts | 29 +++++++-
src/services/TaskQueue.ts | 33 +++++++++
src/services/baseMonitor.ts | 152 +++++++++++++++++++++++++++++++++++++++++
src/services/config.ts | 28 ++++++++
src/services/reportLog.ts | 89 ++++++++++++++++++++++++
src/services/type.d.ts | 0
src/services/utils.ts | 38 +++++++++++
src/{monitor.ts => types.d.ts} | 16 +++--
test/index.html | 26 +++++++
12 files changed, 518 insertions(+), 7 deletions(-)
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..557edf3
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,15 @@
+language: node_js
+
+node_js:
+ - "8"
+
+env:
+ matrix:
+ - TEST_TYPE=build
+
+
+install:
+ - npm install
+
+script:
+ - npm run build
diff --git a/src/errors/ajaxErrors.ts b/src/errors/ajaxErrors.ts
new file mode 100644
index 0000000..6333430
--- /dev/null
+++ b/src/errors/ajaxErrors.ts
@@ -0,0 +1,63 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import { ErrorsCategory } from '../services/config';
+export default class XHRError {
+
+ constructor() {
+ this.handleError();
+ }
+
+ handleError(){
+ if(!window.XMLHttpRequest){
+ return;
+ }
+ let xhrSend = XMLHttpRequest.prototype.send;
+ let _handleEvent = (event: Event) => {
+ try {
+ if (event && event.currentTarget && event.currentTarget.status !== 200) {
+ // this.category = ErrorsCategory.AJAX_ERROR;
+ // this.msg = event.target.response;
+ // this.url = event.target.responseURL;
+ // this.error = {
+ // status: event.target.status,
+ // statusText: event.target.statusText
+ // };
+ // this.recordError();
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ };
+ XMLHttpRequest.prototype.send = function(){
+ if (this.addEventListener){
+ this.addEventListener('error', _handleEvent);
+ this.addEventListener('load', _handleEvent);
+ this.addEventListener('abort', _handleEvent);
+ } else {
+ let tempStateChange = this.onreadystatechange;
+ this.onreadystatechange = function(event: any){
+ tempStateChange.apply(this,arguments);
+ if (this.readyState === 4) {
+ _handleEvent(event);
+ }
+ }
+ }
+ return xhrSend.apply(this,arguments);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/errors/jsErrors.ts b/src/errors/jsErrors.ts
new file mode 100644
index 0000000..b16035f
--- /dev/null
+++ b/src/errors/jsErrors.ts
@@ -0,0 +1,36 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+export default class JSErrors{
+
+ constructor() {
+ this.handleErrors();
+ }
+
+ private handleErrors() {
+ window.onerror = (msg, url, line, col, error) => {
+ try {
+ console.log('msg', msg);
+ console.log(url);
+ console.log(line);
+ console.log(error);
+ } catch(error) {
+ console.log("js errors",error);
+ }
+ }
+ }
+}
diff --git a/src/monitor.ts b/src/monitor.ts
index c84030c..5b0b817 100644
--- a/src/monitor.ts
+++ b/src/monitor.ts
@@ -14,10 +14,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import JSErrors from './errors/jsErrors';
+import { TClientMonitor, TErrorsType } from './types';
export default class ClientMonitor {
- public init() {
- // console.log('enter');
+ private errorTypes: TErrorsType = {
+ jsErrors: true,
+ promiseErrors: true,
+ consoleErrors: false,
+ vueErrors: false,
+ reactErrors: false,
+ ajaxErrors: true,
+ resourceErrors: true,
+ };
+
+ public init(options: TClientMonitor) {
+ this.errorTypes = options;
+ if (this.errorTypes.jsErrors) {
+ this.errorTypes.jsErrors = options.jsErrors;
+ new JSErrors();
+ }
+ if (this.errorTypes.promiseErrors) {
+ this.errorTypes.promiseErrors = options.promiseErrors || this.errorTypes.promiseErrors;
+ }
+ if (this.errorTypes.resourceErrors) {
+ this.errorTypes.resourceErrors = options.resourceErrors;
+ }
+ if (this.errorTypes.ajaxErrors) {
+ this.errorTypes.ajaxErrors = options.ajaxErrors || this.errorTypes.ajaxErrors;
+ }
}
}
diff --git a/src/services/TaskQueue.ts b/src/services/TaskQueue.ts
new file mode 100644
index 0000000..4c8e516
--- /dev/null
+++ b/src/services/TaskQueue.ts
@@ -0,0 +1,33 @@
+import API from "./api.js";
+
+/**
+ * 消息队列
+ */
+var TaskQueue = {
+
+ queues:[], //待处理消息列表
+
+ /**
+ * 添加消息
+ * @param {*} reportUrl 上报url
+ * @param {*} data 上报数据
+ */
+ add:function(reportUrl,data){
+ this.queues.push({reportUrl,data});
+ },
+
+ /**
+ * 统一上报
+ */
+ fire:function(){
+ if(!this.queues || this.queues.length === 0){
+ return;
+ }
+ let item = this.queues[0];
+ item.reportUrl && new API(item.reportUrl).report(item.data);
+ this.queues.splice(0,1);
+ this.fire(); //递归
+ }
+};
+
+export default TaskQueue;
\ No newline at end of file
diff --git a/src/services/baseMonitor.ts b/src/services/baseMonitor.ts
new file mode 100644
index 0000000..cd04dc9
--- /dev/null
+++ b/src/services/baseMonitor.ts
@@ -0,0 +1,152 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import { ErrorLevelEnum,ErrorCategoryEnum } from "./config";
+import utils from "./utils";
+import TaskQueue from "./TaskQueue"
+
+/**
+ * 监控基类
+ */
+class BaseMonitor {
+
+ /**
+ * 上报错误地址
+ * @param {*} params { reportUrl,extendsInfo }
+ */
+ constructor(params){
+ this.category = ErrorCategoryEnum.UNKNOW_ERROR; //错误类型
+ this.level = ErrorLevelEnum.INFO; //错误等级
+ this.msg = ""; //错误信息
+ this.url = ""; //错误信息地址
+ this.line = ""; //行数
+ this.col = ""; //列数
+ this.errorObj = ""; //错误堆栈
+
+ this.reportUrl = params.reportUrl; //上报错误地址
+ this.extendsInfo = params.extendsInfo; //扩展信息
+ }
+
+ /**
+ * 记录错误信息
+ */
+ recordError(){
+ this.handleRecordError();
+ //延迟记录日志
+ setTimeout(()=>{
+ TaskQueue.fire();
+ },100);
+ }
+
+ /**
+ * 处理记录日志
+ */
+ handleRecordError(){
+ try {
+ if(!this.msg){
+ return;
+ }
+ //过滤掉错误上报地址
+ if( this.reportUrl && this.url && this.url.toLowerCase().indexOf(this.reportUrl.toLowerCase())>=0 ){
+ console.log("统计错误接口异常",this.msg);
+ return;
+ }
+ let errorInfo = this.handleErrorInfo();
+
+ console.log("\n````````````````````` "+this.category+" `````````````````````\n",errorInfo)
+
+ //记录日志
+ TaskQueue.add(this.reportUrl,errorInfo);
+
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ /**
+ * 处理错误信息
+ * @param {*} extendsInfo
+ */
+ handleErrorInfo(){
+ let txt = "错误类别: " + this.category + "\r\n";
+ txt += "日志信息: " + this.msg + "\r\n";
+ txt += "url: " + this.url + "\r\n";
+ switch(this.category){
+ case ErrorCategoryEnum.JS_ERROR:
+ txt += "错误行号: " + this.line + "\r\n";
+ txt += "错误列号: " + this.col + "\r\n";
+ if (this.errorObj && this.errorObj.stack) {
+ txt += "错误栈: " + this.errorObj.stack + "\r\n";
+ }
+ break;
+ default:
+ txt += "其他错误: " + JSON.stringify(this.errorObj) + "\r\n";
+ break;
+ }
+ let deviceInfo = this.getDeviceInfo();
+ txt += "设备信息: " + deviceInfo; //设备信息
+ let extendsInfo = this.getExtendsInfo();
+ let recordInfo = extendsInfo;
+ recordInfo.category = this.category; //错误分类
+ recordInfo.logType = this.level; //错误级别
+ recordInfo.logInfo = txt; //错误信息
+ recordInfo.deviceInfo = deviceInfo; //设备信息
+ return recordInfo;
+ }
+
+ /**
+ * 获取扩展信息
+ */
+ getExtendsInfo(){
+ try {
+ let ret = {};
+ let extendsInfo = this.extendsInfo || {};
+ let dynamicParams;
+ if(utils.isFunction(extendsInfo.getDynamic)){
+ dynamicParams = extendsInfo.getDynamic(); //获取动态参数
+ }
+ //判断动态方法返回的参数是否是对象
+ if(utils.isObject(dynamicParams)){
+ extendsInfo = {...extendsInfo,...dynamicParams};
+ }
+ //遍历扩展信息,排除动态方法
+ for(var key in extendsInfo){
+ if(!utils.isFunction(extendsInfo[key])){ //排除获取动态方法
+ ret[key] = extendsInfo[key];
+ }
+ }
+ return ret;
+ } catch (error) {
+ console.log('call getExtendsInfo error',error);
+ return {};
+ }
+ }
+
+ /**
+ * 获取设备信息
+ */
+ getDeviceInfo(){
+ try {
+ let deviceInfo = DeviceInfo.getDeviceInfo();
+ return JSON.stringify(deviceInfo);
+ } catch (error) {
+ console.log(error);
+ return "";
+ }
+ }
+
+}
+export default BaseMonitor;
diff --git a/src/services/config.ts b/src/services/config.ts
new file mode 100644
index 0000000..a2c420a
--- /dev/null
+++ b/src/services/config.ts
@@ -0,0 +1,28 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+export enum ErrorsCategory {
+ AJAX_ERROR = 'ajaxError',
+ RESOURCE_ERROR = 'resourceError',
+ VUE_ERROR = 'vueError',
+ PROMISE_ERROR = 'promiseError',
+ JS_ERROR = 'jsError',
+ CONSOLE_INFO = 'consoleInfo',
+ CONSOLE_WARN = 'consoleWarn',
+ CONSOLE_ERROR = 'consoleError',
+ CROSS_SCRIPT_ERROR = 'crossSrciptError',
+ UNKNOW_ERROR = 'unknowError'
+}
diff --git a/src/services/reportLog.ts b/src/services/reportLog.ts
new file mode 100644
index 0000000..1425f97
--- /dev/null
+++ b/src/services/reportLog.ts
@@ -0,0 +1,89 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+class API {
+
+ constructor(url){
+ this.url = url;
+ }
+
+ /**
+ * 上报信息 (默认方式)
+ */
+ report(data){
+ if(!this.checkUrl(this.url)){
+ console.log("上报信息url地址格式不正确,url=",this.url);
+ return;
+ }
+ console.log("上报地址:"+this.url);
+ this.sendInfo(data);
+ }
+
+ /**
+ * 发送消息
+ */
+ sendInfo(data){
+ try {
+ var xhr = new XMLHttpRequest();
+ xhr.open("POST",this.url,true);
+ //xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
+ xhr.setRequestHeader("Content-Type", "application/json");
+ xhr.send(JSON.stringify(data));
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ /**
+ * 通过img方式上报信息
+ */
+ reportByImg(data){
+ if(!this.checkUrl(this.url)){
+ console.log("上报信息url地址格式不正确,url=",this.url);
+ return;
+ }
+ try {
+ var img = new Image();
+ img.src = this.url+'?v='+new Date().getTime()+'&' + this.formatParams(data);
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ /*
+ *格式化参数
+ */
+ formatParams(data) {
+ var arr = [];
+ for (var name in data) {
+ arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name]));
+ }
+ return arr.join("&");
+ }
+
+ /**
+ * 检测URL
+ */
+ checkUrl(url){
+ if(!url){
+ return false;
+ }
+ var urlRule =/^[hH][tT][tT][pP]([sS]?):\/\//;
+ return urlRule.test(url);
+ }
+
+}
+export default API;
\ No newline at end of file
diff --git a/src/services/type.d.ts b/src/services/type.d.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/services/utils.ts b/src/services/utils.ts
new file mode 100644
index 0000000..cdab32d
--- /dev/null
+++ b/src/services/utils.ts
@@ -0,0 +1,38 @@
+export default {
+
+ type(obj) {
+ return Object.prototype.toString.call(obj).replace(/\[object\s|\]/g, '');
+ },
+
+ isFunction(func) {
+ return this.type(func) === "Function";
+ },
+
+ isArray(list) {
+ return this.type(list) === 'Array';
+ },
+
+ /**
+ * 是否为null
+ * @param {String} str
+ */
+ isNull(str) {
+ return str == undefined || str == '' || str == null;
+ },
+
+ /**
+ * 对象是否为空
+ * @param {*} obj
+ */
+ objectIsNull(obj) {
+ return JSON.stringify(obj) === "{}";
+ },
+
+ /**
+ * 是否是对象
+ * @param {*} obj
+ */
+ isObject(obj){
+ return this.type(obj) === "Object";
+ }
+}
\ No newline at end of file
diff --git a/src/monitor.ts b/src/types.d.ts
similarity index 74%
copy from src/monitor.ts
copy to src/types.d.ts
index c84030c..12f8276 100644
--- a/src/monitor.ts
+++ b/src/types.d.ts
@@ -14,10 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+export interface TClientMonitor extends TErrorsType {
+ reportUrl: string;
+}
-export default class ClientMonitor {
-
- public init() {
- // console.log('enter');
- }
+export interface TErrorsType {
+ jsErrors: boolean;
+ promiseErrors: boolean;
+ consoleErrors: boolean;
+ vueErrors: boolean;
+ reactErrors: boolean;
+ ajaxErrors: boolean;
+ resourceErrors: boolean;
}
diff --git a/test/index.html b/test/index.html
new file mode 100644
index 0000000..10eb0a4
--- /dev/null
+++ b/test/index.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
+ <title>Document</title>
+</head>
+<body>
+ <div>console test</div>
+ <script src="../dist/monitor.js"></script>
+ <script>
+ new ClientMonitor().init({
+ reportUrl: "http://baidu.com",
+ consoleErrors:true,
+ jsErrors: true,
+ promiseErrors: false,
+ vueErrors: false,
+ reactErrors: false,
+ ajaxErrors: false,
+ resourceErrors: false
+ });
+ ss();
+ </script>
+</body>
+</html>
\ No newline at end of file