Merge pull request #3 from Fine0830/feat-monitor

Feat: add report ways and support JS errors
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..564fb76
--- /dev/null
+++ b/index.js
@@ -0,0 +1,20 @@
+/**
+ * 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 './src/index';
+
+export default ClientMonitor;
diff --git a/package.json b/package.json
index 3e7f1c5..8efbe41 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,8 @@
 {
   "name": "skywalking-client-js",
-  "version": "1.0.0",
+  "version": "0.1.0",
   "description": "js monitor",
-  "main": "index.js",
-  "author": "Qiuxia Fan",
-  "license": "MIT",
+  "main": "dist/index.js",
   "dependencies": {
     "clean-webpack-plugin": "3.0.0",
     "express": "^4.17.1",
diff --git a/server.js b/server.js
index c2f6683..345cf7e 100644
--- a/server.js
+++ b/server.js
@@ -1,34 +1,35 @@
 /**
-* 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.
-*/
-const express = require('express');
-const webpack = require('webpack');
-const webpackDevMiddleware = require('webpack-dev-middleware');
+ * 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.
+ */
+const express = require("express");
+const webpack = require("webpack");
+const webpackDevMiddleware = require("webpack-dev-middleware");
 
 const app = express();
-const config = require('./webpack.config.js');
+const config = require("./webpack.config.js");
 const compiler = webpack(config);
 
 // Tell express to use the webpack-dev-middleware and use the webpack.config.js
 // configuration file as a base.
-app.use(webpackDevMiddleware(compiler, {
-  publicPath: config.output.publicPath
-}));
-
+app.use(
+  webpackDevMiddleware(compiler, {
+    publicPath: config.output.publicPath
+  })
+);
 // Serve the files on port 3000.
-app.listen(3000, function () {
-  console.log('Example app listening on port 3000!\n');
-});
\ No newline at end of file
+app.listen(3000, function() {
+  console.log("Example app listening on port 3000!\n");
+});
diff --git a/src/errors/jsErrors.ts b/src/errors/jsErrors.ts
new file mode 100644
index 0000000..cb7756d
--- /dev/null
+++ b/src/errors/jsErrors.ts
@@ -0,0 +1,39 @@
+/**
+ * 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 BaseMonitor from '../services/base';
+import { GradeTypeEnum } from '../services/constant';
+import { ErrorsCategory } from '../services/constant';
+
+class JSErrors extends BaseMonitor {
+  public handleErrors(options: {reportUrl: string}) {
+    window.onerror = (message, url, line, col, error) => {
+      this.logInfo = {
+        reportUrl: options.reportUrl,
+        category: ErrorsCategory.JS_ERROR,
+        grade: GradeTypeEnum.ERROR,
+        errorUrl: url,
+        line,
+        col,
+        errorInfo: error,
+        message,
+      };
+      this.traceInfo();
+    };
+  }
+}
+export default new JSErrors();
diff --git a/src/monitor.ts b/src/monitor.ts
index c84030c..b24faf0 100644
--- a/src/monitor.ts
+++ b/src/monitor.ts
@@ -14,10 +14,36 @@
  * 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 {
+const ClientMonitor = {
+  errorTypes: {
+    jsErrors: true,
+    promiseErrors: true,
+    consoleErrors: false,
+    vueErrors: false,
+    reactErrors: false,
+    ajaxErrors: true,
+    resourceErrors: true,
+  } as TErrorsType,
 
-  public init() {
-    // console.log('enter');
-  }
-}
+  register(options: TClientMonitor & TErrorsType) {
+    this.errorTypes = options;
+    if (this.errorTypes.jsErrors) {
+      this.errorTypes.jsErrors = options.jsErrors;
+      JSErrors.handleErrors({reportUrl: options.reportUrl});
+    }
+    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;
+    }
+  },
+};
+
+export default ClientMonitor;
diff --git a/src/services/base.ts b/src/services/base.ts
new file mode 100644
index 0000000..5437912
--- /dev/null
+++ b/src/services/base.ts
@@ -0,0 +1,78 @@
+/**
+ * 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 Task from './task';
+import { ErrorsCategory, GradeTypeEnum } from './constant';
+import { errorInfoFeilds } from './types';
+
+export default class BaseMonitor {
+  public logInfo: errorInfoFeilds & {reportUrl: string} = {
+    category: ErrorsCategory.UNKNOW_ERROR,
+    grade: GradeTypeEnum.INFO,
+    errorUrl: '',
+    line: 0,
+    col: 0,
+    errorInfo: '',
+    message: '',
+    reportUrl: '',
+  };
+
+  public traceInfo() {
+    this.handleRecordError();
+    setTimeout(() => {
+      Task.fireTasks();
+    }, 100);
+  }
+
+  private handleRecordError() {
+    try {
+      if (!this.logInfo.message) {
+        return;
+      }
+      if (this.logInfo.reportUrl && this.logInfo.errorUrl &&
+        this.logInfo.errorUrl.toLowerCase().includes(this.logInfo.reportUrl.toLowerCase())) {
+        return;
+      }
+      const errorInfo = this.handleErrorInfo();
+
+      Task.addTask(this.logInfo.reportUrl, errorInfo);
+
+    } catch (error) {
+      // console.log(error);
+    }
+  }
+
+  private handleErrorInfo() {
+    let message = `error category:${this.logInfo.category}\r\n log info:${this.logInfo.message}\r\n
+      error url: ${this.logInfo.errorUrl}\r\n `;
+    switch (this.logInfo.category) {
+        case ErrorsCategory.JS_ERROR:
+          message += `error line number: ${this.logInfo.line}\r\n error col number:${this.logInfo.col}\r\n`;
+          if (this.logInfo.errorInfo && this.logInfo.errorInfo.stack) {
+            message += `error stack: ${this.logInfo.errorInfo.stack}\r\n`;
+          }
+          break;
+        default:
+          message += `other error: ${this.logInfo.errorInfo}\r\n`;
+          break;
+    }
+    const recordInfo = {
+      ...this.logInfo,
+      message,
+    };
+    return recordInfo;
+  }
+}
diff --git a/src/services/constant.ts b/src/services/constant.ts
new file mode 100644
index 0000000..8a1b711
--- /dev/null
+++ b/src/services/constant.ts
@@ -0,0 +1,33 @@
+/**
+ * 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',
+}
+export enum GradeTypeEnum {
+  INFO = 'Info',
+  WARNING = 'Warning',
+  ERROR = 'Error',
+}
diff --git a/src/services/report.ts b/src/services/report.ts
new file mode 100644
index 0000000..a6e080a
--- /dev/null
+++ b/src/services/report.ts
@@ -0,0 +1,68 @@
+/**
+ * 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 Report {
+
+  private url: string = '';
+
+  constructor(url: string) {
+    this.url = url;
+  }
+
+  public sendByXhr(data: any) {
+    if (!this.checkUrl(this.url)) {
+      return;
+    }
+    try {
+      const xhr = new XMLHttpRequest();
+      xhr.open('POST', this.url, true);
+      xhr.setRequestHeader('Content-Type', 'application/json');
+      console.log(data);
+      xhr.send(JSON.stringify(data));
+    } catch (error) {
+    //   console.log(error);
+    }
+  }
+
+  private reportByImg(data: any) {
+    if (!this.checkUrl(this.url)) {
+      return;
+    }
+    try {
+      const imgObj = new Image();
+
+      imgObj.src = `${this.url}?v=${new Date().getTime()}&${this.formatParams(data)}`;
+    } catch (error) {
+        // console.log(error);
+    }
+  }
+
+  private formatParams(data: any) {
+    return Object.keys(data).map((name: string) =>
+    `${encodeURIComponent(name)}=${encodeURIComponent(data[name])}`,
+    ).join('&');
+  }
+
+  private checkUrl(url: string) {
+    if (!url) {
+      return;
+    }
+    const urlRule = /^[hH][tT][tT][pP]([sS]?):\/\//;
+    return urlRule.test(url);
+  }
+}
+export default Report;
diff --git a/src/services/task.ts b/src/services/task.ts
new file mode 100644
index 0000000..4f51a17
--- /dev/null
+++ b/src/services/task.ts
@@ -0,0 +1,39 @@
+/**
+ * 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 Report from './report';
+
+class TaskQueue {
+  private queues: any[] = [];
+
+  public addTask(reportUrl: string, data: any) {
+    this.queues.push({reportUrl, data});
+  }
+
+  public fireTasks() {
+    if (!this.queues || !this.queues.length) {
+      return;
+    }
+    const item = this.queues[0];
+    if (item.reportUrl) {
+      new Report(item.reportUrl).sendByXhr(item.data);
+    }
+    this.queues.splice(0, 1);
+    this.fireTasks();
+  }
+}
+
+export default new TaskQueue();
diff --git a/src/services/types.d.ts b/src/services/types.d.ts
new file mode 100644
index 0000000..d296077
--- /dev/null
+++ b/src/services/types.d.ts
@@ -0,0 +1,44 @@
+/**
+ * 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 interface TraceFields {
+  reportUrl: string;
+  serviceName?: string;
+  errorInfo: errorInfoFeilds;
+  performanceInfo?: performanceInfoFields;
+  resources?: any;
+  pageId?: string;
+  deviceInfo?: any;
+}
+export interface performanceInfoFields {
+  redirectTime: string;
+  dnsTime: string;
+  reqTime: string;
+  analysisTime: string;
+  blankTime: string;
+  domReadyTime: string;
+  loadPage: string;
+}
+
+export interface errorInfoFeilds {
+  category: string;
+  grade: string;
+  message: any;
+  errorUrl: string;
+  line: number; 
+  col: number;
+  errorInfo: any;
+}
diff --git a/src/types.d.ts b/src/types.d.ts
new file mode 100644
index 0000000..b7f6058
--- /dev/null
+++ b/src/types.d.ts
@@ -0,0 +1,30 @@
+/**
+ * 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 interface TClientMonitor {
+  reportUrl: string;
+  modulesName?: string;
+}
+
+export interface TErrorsType {
+  jsErrors: boolean;
+  promiseErrors: boolean;
+  consoleErrors: boolean;
+  vueErrors: boolean;
+  reactErrors: boolean;
+  ajaxErrors: boolean;
+  resourceErrors: boolean;
+}
diff --git a/webpack.config.js b/webpack.config.js
index b37677e..812f1d0 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -35,7 +35,7 @@
     extensions: [ '.tsx', '.ts', '.js' ]
   },
   output: {
-    filename: 'monitor.js',
+    filename: 'index.js',
     path: path.resolve(__dirname, 'dist'),
     publicPath: '/'
   },