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/09/03 09:12:21 UTC
[skywalking] branch master updated: Support HTTP api for browser
recevier (#5429)
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.git
The following commit(s) were added to refs/heads/master by this push:
new 9143f13 Support HTTP api for browser recevier (#5429)
9143f13 is described below
commit 9143f1342d5cbdab1876d39006e7d8d5743d56a6
Author: zhang-wei <pk...@outlook.com>
AuthorDate: Thu Sep 3 17:12:07 2020 +0800
Support HTTP api for browser recevier (#5429)
* support http api
---
docs/en/protocols/Browser-HTTP-API-Protocol.md | 108 +++++++++++
docs/en/protocols/Browser-Protocol.md | 3 +-
.../browser/provider/BrowserModuleProvider.java | 22 ++-
.../{ => grpc}/BrowserPerfServiceHandler.java | 2 +-
.../BrowserErrorLogReportBaseServletHandler.java | 96 ++++++++++
.../BrowserErrorLogReportListServletHandler.java | 70 ++++++++
.../BrowserErrorLogReportSingleServletHandler.java | 56 ++++++
.../rest/BrowserPerfDataReportServletHandler.java | 112 ++++++++++++
.../rest/BrowserReportServletHandlerTest.java | 197 +++++++++++++++++++++
9 files changed, 662 insertions(+), 4 deletions(-)
diff --git a/docs/en/protocols/Browser-HTTP-API-Protocol.md b/docs/en/protocols/Browser-HTTP-API-Protocol.md
new file mode 100644
index 0000000..25d2e16
--- /dev/null
+++ b/docs/en/protocols/Browser-HTTP-API-Protocol.md
@@ -0,0 +1,108 @@
+# HTTP API Protocol
+
+HTTP API Protocol defines the API data format, including api request and response data format.
+They use the HTTP1.1 wrapper of the official [SkyWalking Browser Protocol](Browser-Protocol.md). Read it for more details.
+
+## Performance Data Report
+
+Detail information about data format can be found in [BrowserPerf.proto](https://github.com/apache/skywalking-data-collect-protocol/blob/master/browser/BrowserPerf.proto).
+
+### POST http://localhost:12800/browser/perfData
+
+Send a performance data object with JSON format.
+
+Input:
+
+```json
+{
+ "service": "web",
+ "serviceVersion": "v0.0.1",
+ "pagePath": "/index.html",
+ "redirectTime": 10,
+ "dnsTime": 10,
+ "ttfbTime": 10,
+ "tcpTime": 10,
+ "transTime": 10,
+ "domAnalysisTime": 10,
+ "fptTime": 10,
+ "domReadyTime": 10,
+ "loadPageTime": 10,
+ "resTime": 10,
+ "sslTime": 10,
+ "ttlTime": 10,
+ "firstPackTime": 10,
+ "fmpTime": 10
+}
+```
+
+OutPut:
+
+```json
+
+```
+
+## Error Log Report
+
+Detail information about data format can be found in [BrowserPerf.proto](https://github.com/apache/skywalking-data-collect-protocol/blob/master/Browser/BrowserPerf.proto).
+
+### POST http://localhost:12800/browser/errorLogs
+
+Send an error log object list with JSON format.
+
+Input:
+
+```json
+[
+ {
+ "uniqueId": "55ec6178-3fb7-43ef-899c-a26944407b01",
+ "service": "web",
+ "serviceVersion": "v0.0.1",
+ "pagePath": "/index.html",
+ "category": "ajax",
+ "message": "error",
+ "line": 1,
+ "col": 1,
+ "stack": "error",
+ "errorUrl": "/index.html"
+ },
+ {
+ "uniqueId": "55ec6178-3fb7-43ef-899c-a26944407b02",
+ "service": "web",
+ "serviceVersion": "v0.0.1",
+ "pagePath": "/index.html",
+ "category": "ajax",
+ "message": "error",
+ "line": 1,
+ "col": 1,
+ "stack": "error",
+ "errorUrl": "/index.html"
+ }
+]
+```
+
+### POST http://localhost:12800/browser/errorLog
+
+Send a single error log object with JSON format.
+
+Input:
+
+```json
+{
+ "uniqueId": "55ec6178-3fb7-43ef-899c-a26944407b01",
+ "service": "web",
+ "serviceVersion": "v0.0.1",
+ "pagePath": "/index.html",
+ "category": "ajax",
+ "message": "error",
+ "line": 1,
+ "col": 1,
+ "stack": "error",
+ "errorUrl": "/index.html"
+}
+```
+
+OutPut:
+
+```json
+
+```
diff --git a/docs/en/protocols/Browser-Protocol.md b/docs/en/protocols/Browser-Protocol.md
index 679ecaf..a88a77a 100644
--- a/docs/en/protocols/Browser-Protocol.md
+++ b/docs/en/protocols/Browser-Protocol.md
@@ -4,7 +4,8 @@ Browser protocol describes the data format between [skywalking-client-js](https:
## Overview
-Browser protocol is defined and provided in [gRPC format](https://github.com/apache/skywalking-data-collect-protocol/blob/master/browser/BrowserPerf.proto).
+Browser protocol is defined and provided in [gRPC format](https://github.com/apache/skywalking-data-collect-protocol/blob/master/browser/BrowserPerf.proto),
+also implemented in [HTTP 1.1](Browser-HTTP-API-Protocol.md)
### Send performance data and error log
diff --git a/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/BrowserModuleProvider.java b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/BrowserModuleProvider.java
index bc58caf..aa13728 100644
--- a/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/BrowserModuleProvider.java
+++ b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/BrowserModuleProvider.java
@@ -21,13 +21,17 @@ import org.apache.skywalking.oap.server.configuration.api.ConfigurationModule;
import org.apache.skywalking.oap.server.core.CoreModule;
import org.apache.skywalking.oap.server.core.oal.rt.OALEngineLoaderService;
import org.apache.skywalking.oap.server.core.server.GRPCHandlerRegister;
+import org.apache.skywalking.oap.server.core.server.JettyHandlerRegister;
import org.apache.skywalking.oap.server.library.module.ModuleConfig;
import org.apache.skywalking.oap.server.library.module.ModuleDefine;
import org.apache.skywalking.oap.server.library.module.ModuleProvider;
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
import org.apache.skywalking.oap.server.library.module.ServiceNotProvidedException;
import org.apache.skywalking.oap.server.receiver.browser.module.BrowserModule;
-import org.apache.skywalking.oap.server.receiver.browser.provider.handler.BrowserPerfServiceHandler;
+import org.apache.skywalking.oap.server.receiver.browser.provider.handler.grpc.BrowserPerfServiceHandler;
+import org.apache.skywalking.oap.server.receiver.browser.provider.handler.rest.BrowserErrorLogReportListServletHandler;
+import org.apache.skywalking.oap.server.receiver.browser.provider.handler.rest.BrowserErrorLogReportSingleServletHandler;
+import org.apache.skywalking.oap.server.receiver.browser.provider.handler.rest.BrowserPerfDataReportServletHandler;
import org.apache.skywalking.oap.server.receiver.browser.provider.parser.errorlog.ErrorLogParserListenerManager;
import org.apache.skywalking.oap.server.receiver.browser.provider.parser.errorlog.listener.ErrorLogRecordListener;
import org.apache.skywalking.oap.server.receiver.browser.provider.parser.errorlog.listener.MultiScopesErrorLogAnalysisListener;
@@ -69,10 +73,24 @@ public class BrowserModuleProvider extends ModuleProvider {
GRPCHandlerRegister grpcHandlerRegister = getManager().find(SharingServerModule.NAME)
.provider().getService(GRPCHandlerRegister.class);
-
+ // grpc
grpcHandlerRegister.addHandler(
new BrowserPerfServiceHandler(
getManager(), moduleConfig, perfDataListenerManager(), errorLogListenerManager()));
+
+ // rest
+ JettyHandlerRegister jettyHandlerRegister = getManager().find(SharingServerModule.NAME)
+ .provider()
+ .getService(JettyHandlerRegister.class);
+ // performance
+ jettyHandlerRegister.addHandler(
+ new BrowserPerfDataReportServletHandler(getManager(), moduleConfig, perfDataListenerManager()));
+ // error log
+ ErrorLogParserListenerManager errorLogParserListenerManager = errorLogListenerManager();
+ jettyHandlerRegister.addHandler(
+ new BrowserErrorLogReportSingleServletHandler(getManager(), moduleConfig, errorLogParserListenerManager));
+ jettyHandlerRegister.addHandler(
+ new BrowserErrorLogReportListServletHandler(getManager(), moduleConfig, errorLogParserListenerManager));
}
@Override
diff --git a/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/BrowserPerfServiceHandler.java b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/grpc/BrowserPerfServiceHandler.java
similarity index 99%
rename from oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/BrowserPerfServiceHandler.java
rename to oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/grpc/BrowserPerfServiceHandler.java
index 3b1f633..7be6438 100644
--- a/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/BrowserPerfServiceHandler.java
+++ b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/grpc/BrowserPerfServiceHandler.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.skywalking.oap.server.receiver.browser.provider.handler;
+package org.apache.skywalking.oap.server.receiver.browser.provider.handler.grpc;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
diff --git a/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserErrorLogReportBaseServletHandler.java b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserErrorLogReportBaseServletHandler.java
new file mode 100644
index 0000000..ccf7c80
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserErrorLogReportBaseServletHandler.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+package org.apache.skywalking.oap.server.receiver.browser.provider.handler.rest;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.apm.network.language.agent.v3.BrowserErrorLog;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.server.jetty.JettyHandler;
+import org.apache.skywalking.oap.server.receiver.browser.provider.BrowserServiceModuleConfig;
+import org.apache.skywalking.oap.server.receiver.browser.provider.parser.errorlog.ErrorLogAnalyzer;
+import org.apache.skywalking.oap.server.receiver.browser.provider.parser.errorlog.ErrorLogParserListenerManager;
+import org.apache.skywalking.oap.server.telemetry.TelemetryModule;
+import org.apache.skywalking.oap.server.telemetry.api.CounterMetrics;
+import org.apache.skywalking.oap.server.telemetry.api.HistogramMetrics;
+import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator;
+import org.apache.skywalking.oap.server.telemetry.api.MetricsTag;
+
+@Slf4j
+public abstract class BrowserErrorLogReportBaseServletHandler extends JettyHandler {
+ private final ModuleManager moduleManager;
+ private final BrowserServiceModuleConfig config;
+ private final ErrorLogParserListenerManager errorLogListenerManager;
+
+ private final HistogramMetrics errorLogHistogram;
+ private final CounterMetrics logErrorCounter;
+
+ public BrowserErrorLogReportBaseServletHandler(ModuleManager moduleManager,
+ BrowserServiceModuleConfig config,
+ ErrorLogParserListenerManager errorLogListenerManager) {
+ this.moduleManager = moduleManager;
+ this.config = config;
+ this.errorLogListenerManager = errorLogListenerManager;
+
+ MetricsCreator metricsCreator = moduleManager.find(TelemetryModule.NAME)
+ .provider()
+ .getService(MetricsCreator.class);
+
+ errorLogHistogram = metricsCreator.createHistogramMetric(
+ "browser_error_log_in_latency", "The process latency of browser error log", new MetricsTag.Keys("protocol"),
+ new MetricsTag.Values("grpc")
+ );
+ logErrorCounter = metricsCreator.createCounter(
+ "browser_error_log_analysis_error_count", "The error number of browser error log analysis",
+ new MetricsTag.Keys("protocol"), new MetricsTag.Values("http")
+ );
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse resp) throws ServletException, IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void doPost(final HttpServletRequest req,
+ final HttpServletResponse resp) throws ServletException, IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("receive browser error log");
+ }
+
+ HistogramMetrics.Timer timer = errorLogHistogram.createTimer();
+ try {
+ for (BrowserErrorLog browserErrorLog : parseBrowserErrorLog(req)) {
+ ErrorLogAnalyzer analyzer = new ErrorLogAnalyzer(moduleManager, errorLogListenerManager, config);
+ analyzer.doAnalysis(browserErrorLog);
+ }
+ } catch (Throwable e) {
+ log.error(e.getMessage(), e);
+ logErrorCounter.inc();
+ } finally {
+ timer.finish();
+ }
+ }
+
+ protected abstract List<BrowserErrorLog> parseBrowserErrorLog(HttpServletRequest request) throws IOException;
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserErrorLogReportListServletHandler.java b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserErrorLogReportListServletHandler.java
new file mode 100644
index 0000000..b1aae85
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserErrorLogReportListServletHandler.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package org.apache.skywalking.oap.server.receiver.browser.provider.handler.rest;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.skywalking.apm.network.language.agent.v3.BrowserErrorLog;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils;
+import org.apache.skywalking.oap.server.receiver.browser.provider.BrowserServiceModuleConfig;
+import org.apache.skywalking.oap.server.receiver.browser.provider.parser.errorlog.ErrorLogParserListenerManager;
+
+public class BrowserErrorLogReportListServletHandler extends BrowserErrorLogReportBaseServletHandler {
+ private final Gson gson = new Gson();
+
+ public BrowserErrorLogReportListServletHandler(final ModuleManager moduleManager,
+ final BrowserServiceModuleConfig config,
+ final ErrorLogParserListenerManager errorLogListenerManager) {
+ super(moduleManager, config, errorLogListenerManager);
+ }
+
+ @Override
+ protected List<BrowserErrorLog> parseBrowserErrorLog(final HttpServletRequest request) throws IOException {
+ BufferedReader reader = request.getReader();
+ String line;
+ StringBuilder stringBuilder = new StringBuilder();
+ while ((line = reader.readLine()) != null) {
+ stringBuilder.append(line);
+ }
+ final JsonArray array = gson.fromJson(stringBuilder.toString(), JsonArray.class);
+ if (array.size() == 0) {
+ return Collections.emptyList();
+ }
+
+ final ArrayList<BrowserErrorLog> errorLogs = new ArrayList<>(array.size());
+ for (JsonElement element : array) {
+ BrowserErrorLog.Builder builder = BrowserErrorLog.newBuilder();
+ ProtoBufJsonUtils.fromJSON(element.toString(), builder);
+ errorLogs.add(builder.build());
+ }
+ return errorLogs;
+ }
+
+ @Override
+ public String pathSpec() {
+ return "/browser/errorLogs";
+ }
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserErrorLogReportSingleServletHandler.java b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserErrorLogReportSingleServletHandler.java
new file mode 100644
index 0000000..1054486
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserErrorLogReportSingleServletHandler.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package org.apache.skywalking.oap.server.receiver.browser.provider.handler.rest;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.skywalking.apm.network.language.agent.v3.BrowserErrorLog;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils;
+import org.apache.skywalking.oap.server.receiver.browser.provider.BrowserServiceModuleConfig;
+import org.apache.skywalking.oap.server.receiver.browser.provider.parser.errorlog.ErrorLogParserListenerManager;
+
+public class BrowserErrorLogReportSingleServletHandler extends BrowserErrorLogReportBaseServletHandler {
+ public BrowserErrorLogReportSingleServletHandler(final ModuleManager moduleManager,
+ final BrowserServiceModuleConfig config,
+ final ErrorLogParserListenerManager errorLogListenerManager) {
+ super(moduleManager, config, errorLogListenerManager);
+ }
+
+ @Override
+ protected List<BrowserErrorLog> parseBrowserErrorLog(final HttpServletRequest request) throws IOException {
+ BufferedReader reader = request.getReader();
+ String line;
+ StringBuilder stringBuilder = new StringBuilder();
+ while ((line = reader.readLine()) != null) {
+ stringBuilder.append(line);
+ }
+
+ BrowserErrorLog.Builder builder = BrowserErrorLog.newBuilder();
+ ProtoBufJsonUtils.fromJSON(stringBuilder.toString(), builder);
+ return Collections.singletonList(builder.build());
+ }
+
+ @Override
+ public String pathSpec() {
+ return "/browser/errorLog";
+ }
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserPerfDataReportServletHandler.java b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserPerfDataReportServletHandler.java
new file mode 100644
index 0000000..e0bec01
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserPerfDataReportServletHandler.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+package org.apache.skywalking.oap.server.receiver.browser.provider.handler.rest;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.apm.network.language.agent.v3.BrowserPerfData;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.server.jetty.JettyHandler;
+import org.apache.skywalking.oap.server.library.util.ProtoBufJsonUtils;
+import org.apache.skywalking.oap.server.receiver.browser.provider.BrowserServiceModuleConfig;
+import org.apache.skywalking.oap.server.receiver.browser.provider.parser.performance.PerfDataAnalyzer;
+import org.apache.skywalking.oap.server.receiver.browser.provider.parser.performance.PerfDataParserListenerManager;
+import org.apache.skywalking.oap.server.telemetry.TelemetryModule;
+import org.apache.skywalking.oap.server.telemetry.api.CounterMetrics;
+import org.apache.skywalking.oap.server.telemetry.api.HistogramMetrics;
+import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator;
+import org.apache.skywalking.oap.server.telemetry.api.MetricsTag;
+
+@Slf4j
+public class BrowserPerfDataReportServletHandler extends JettyHandler {
+ private final ModuleManager moduleManager;
+ private final BrowserServiceModuleConfig config;
+ private final PerfDataParserListenerManager perfDataListenerManager;
+
+ private final HistogramMetrics perfHistogram;
+ private final CounterMetrics perfErrorCounter;
+
+ public BrowserPerfDataReportServletHandler(ModuleManager moduleManager,
+ BrowserServiceModuleConfig config,
+ PerfDataParserListenerManager perfDataListenerManager) {
+ this.moduleManager = moduleManager;
+ this.config = config;
+ this.perfDataListenerManager = perfDataListenerManager;
+
+ MetricsCreator metricsCreator = moduleManager.find(TelemetryModule.NAME)
+ .provider()
+ .getService(MetricsCreator.class);
+
+ perfHistogram = metricsCreator.createHistogramMetric(
+ "browser_perf_data_in_latency", "The process latency of browser performance data",
+ new MetricsTag.Keys("protocol"), new MetricsTag.Values("http")
+ );
+ perfErrorCounter = metricsCreator.createCounter(
+ "browser_perf_data_analysis_error_count", "The error number of browser performance data analysis",
+ new MetricsTag.Keys("protocol"), new MetricsTag.Values("http")
+ );
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse resp) throws ServletException, IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected void doPost(final HttpServletRequest req,
+ final HttpServletResponse resp) throws ServletException, IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("receive browser performance data");
+ }
+
+ HistogramMetrics.Timer timer = perfHistogram.createTimer();
+ try {
+ BrowserPerfData browserPerfData = parseBrowserPerfData(req);
+ PerfDataAnalyzer analyzer = new PerfDataAnalyzer(moduleManager, perfDataListenerManager, config);
+ analyzer.doAnalysis(browserPerfData);
+ } catch (Throwable e) {
+ log.error(e.getMessage(), e);
+ perfErrorCounter.inc();
+ } finally {
+ timer.finish();
+ }
+ }
+
+ protected BrowserPerfData parseBrowserPerfData(HttpServletRequest request) throws IOException {
+ BufferedReader reader = request.getReader();
+ String line;
+ StringBuilder stringBuilder = new StringBuilder();
+ while ((line = reader.readLine()) != null) {
+ stringBuilder.append(line);
+ }
+
+ BrowserPerfData.Builder builder = BrowserPerfData.newBuilder();
+ ProtoBufJsonUtils.fromJSON(stringBuilder.toString(), builder);
+ return builder.build();
+ }
+
+ @Override
+ public String pathSpec() {
+ return "/browser/perfData";
+ }
+}
diff --git a/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserReportServletHandlerTest.java b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserReportServletHandlerTest.java
new file mode 100644
index 0000000..b3dd333
--- /dev/null
+++ b/oap-server/server-receiver-plugin/skywalking-browser-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/browser/provider/handler/rest/BrowserReportServletHandlerTest.java
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+package org.apache.skywalking.oap.server.receiver.browser.provider.handler.rest;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.skywalking.apm.network.language.agent.v3.BrowserErrorLog;
+import org.apache.skywalking.apm.network.language.agent.v3.BrowserPerfData;
+import org.apache.skywalking.apm.network.language.agent.v3.ErrorCategory;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.telemetry.TelemetryModule;
+import org.apache.skywalking.oap.server.telemetry.api.MetricsCreator;
+import org.apache.skywalking.oap.server.telemetry.none.MetricsCreatorNoop;
+import org.apache.skywalking.oap.server.telemetry.none.NoneTelemetryProvider;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.internal.util.reflection.Whitebox;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import static org.mockito.Mockito.when;
+
+@RunWith(PowerMockRunner.class)
+@PowerMockIgnore({"javax.management.*"})
+public class BrowserReportServletHandlerTest {
+ @Mock
+ private HttpServletRequest request;
+ @Mock
+ private ModuleManager moduleManager;
+ @Mock
+ private NoneTelemetryProvider telemetryProvider;
+
+ @Before
+ public void init() {
+ Mockito.when(telemetryProvider.getService(MetricsCreator.class))
+ .thenReturn(new MetricsCreatorNoop());
+
+ TelemetryModule telemetryModule = Mockito.spy(TelemetryModule.class);
+ Whitebox.setInternalState(telemetryModule, "loadedProvider", telemetryProvider);
+ Mockito.when(moduleManager.find(TelemetryModule.NAME)).thenReturn(telemetryModule);
+ }
+
+ @Test
+ public void testPerfData() throws IOException {
+ final String json = "{\n" +
+ " \"service\": \"test\",\n" +
+ " \"serviceVersion\": \"v0.0.1\",\n" +
+ " \"pagePath\": \"/e2e-browser\",\n" +
+ " \"redirectTime\": 1,\n" +
+ " \"dnsTime\": 2,\n" +
+ " \"ttfbTime\": 3,\n" +
+ " \"tcpTime\": 4,\n" +
+ " \"transTime\": 5,\n" +
+ " \"domAnalysisTime\": 6,\n" +
+ " \"fptTime\": 7,\n" +
+ " \"domReadyTime\": 8,\n" +
+ " \"loadPageTime\": 9,\n" +
+ " \"resTime\": 10,\n" +
+ " \"sslTime\": 11,\n" +
+ " \"ttlTime\": 12,\n" +
+ " \"firstPackTime\": 13,\n" +
+ " \"fmpTime\": 14\n" +
+ "}";
+ final BrowserPerfDataReportServletHandler reportServletHandler = new BrowserPerfDataReportServletHandler(
+ moduleManager, null, null);
+
+ when(request.getReader()).thenReturn(
+ new BufferedReader(new StringReader(json)));
+ final BrowserPerfData result = reportServletHandler.parseBrowserPerfData(request);
+ Assert.assertEquals("test", result.getService());
+ Assert.assertEquals("v0.0.1", result.getServiceVersion());
+ Assert.assertEquals("/e2e-browser", result.getPagePath());
+ Assert.assertEquals(1, result.getRedirectTime());
+ Assert.assertEquals(2, result.getDnsTime());
+ Assert.assertEquals(3, result.getTtfbTime());
+ Assert.assertEquals(4, result.getTcpTime());
+ Assert.assertEquals(5, result.getTransTime());
+ Assert.assertEquals(6, result.getDomAnalysisTime());
+ Assert.assertEquals(7, result.getFptTime());
+ Assert.assertEquals(8, result.getDomReadyTime());
+ Assert.assertEquals(9, result.getLoadPageTime());
+ Assert.assertEquals(10, result.getResTime());
+ Assert.assertEquals(11, result.getSslTime());
+ Assert.assertEquals(12, result.getTtlTime());
+ Assert.assertEquals(13, result.getFirstPackTime());
+ Assert.assertEquals(14, result.getFmpTime());
+ }
+
+ @Test
+ public void testErrorLogSingle() throws IOException {
+ final String singleJson = "{\n" +
+ " \"uniqueId\": \"55ec6178-3fb7-43ef-899c-a26944407b0e\",\n" +
+ " \"service\": \"test\",\n" +
+ " \"serviceVersion\": \"v0.0.1\",\n" +
+ " \"pagePath\": \"/e2e-browser\",\n" +
+ " \"category\": \"ajax\",\n" +
+ " \"message\": \"test\",\n" +
+ " \"line\": 1,\n" +
+ " \"col\": 1,\n" +
+ " \"stack\": \"e2e\",\n" +
+ " \"errorUrl\": \"/e2e-browser\"\n" +
+ "}";
+
+ final BrowserErrorLogReportSingleServletHandler singleServletHandler = new BrowserErrorLogReportSingleServletHandler(
+ moduleManager, null, null);
+
+ when(request.getReader()).thenReturn(
+ new BufferedReader(new StringReader(singleJson)));
+ final List<BrowserErrorLog> browserErrorLogs = singleServletHandler.parseBrowserErrorLog(request);
+ Assert.assertEquals(1, browserErrorLogs.size());
+ BrowserErrorLog errorLog = browserErrorLogs.get(0);
+ Assert.assertEquals("55ec6178-3fb7-43ef-899c-a26944407b0e", errorLog.getUniqueId());
+ Assert.assertEquals("test", errorLog.getService());
+ Assert.assertEquals("v0.0.1", errorLog.getServiceVersion());
+ Assert.assertEquals("/e2e-browser", errorLog.getPagePath());
+ Assert.assertEquals(ErrorCategory.ajax, errorLog.getCategory());
+ Assert.assertEquals("test", errorLog.getMessage());
+ Assert.assertEquals(1, errorLog.getLine());
+ Assert.assertEquals(1, errorLog.getCol());
+ Assert.assertEquals("e2e", errorLog.getStack());
+ Assert.assertEquals("/e2e-browser", errorLog.getErrorUrl());
+ }
+
+ @Test
+ public void testErrorLogList() throws IOException {
+ final String listJson = "[\n" +
+ " {\n" +
+ " \"uniqueId\": \"55ec6178-3fb7-43ef-899c-a26944407b01\",\n" +
+ " \"service\": \"test\",\n" +
+ " \"serviceVersion\": \"v0.0.1\",\n" +
+ " \"pagePath\": \"/e2e-browser\",\n" +
+ " \"category\": \"ajax\",\n" +
+ " \"message\": \"test\",\n" +
+ " \"line\": 1,\n" +
+ " \"col\": 1,\n" +
+ " \"stack\": \"e2e\",\n" +
+ " \"errorUrl\": \"/e2e-browser\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"uniqueId\": \"55ec6178-3fb7-43ef-899c-a26944407b02\",\n" +
+ " \"service\": \"test\",\n" +
+ " \"serviceVersion\": \"v0.0.1\",\n" +
+ " \"pagePath\": \"/e2e-browser\",\n" +
+ " \"category\": \"ajax\",\n" +
+ " \"message\": \"test\",\n" +
+ " \"line\": 1,\n" +
+ " \"col\": 1,\n" +
+ " \"stack\": \"e2e\",\n" +
+ " \"errorUrl\": \"/e2e-browser\"\n" +
+ " }\n" +
+ "]";
+
+ final BrowserErrorLogReportListServletHandler listServletHandler = new BrowserErrorLogReportListServletHandler(
+ moduleManager, null, null);
+
+ when(request.getReader()).thenReturn(
+ new BufferedReader(new StringReader(listJson)));
+
+ final List<BrowserErrorLog> browserErrorLogs = listServletHandler.parseBrowserErrorLog(request);
+ Assert.assertEquals(2, browserErrorLogs.size());
+ BrowserErrorLog errorLog1 = browserErrorLogs.get(0);
+ Assert.assertEquals("55ec6178-3fb7-43ef-899c-a26944407b01", errorLog1.getUniqueId());
+ Assert.assertEquals("test", errorLog1.getService());
+ Assert.assertEquals("v0.0.1", errorLog1.getServiceVersion());
+ Assert.assertEquals("/e2e-browser", errorLog1.getPagePath());
+ Assert.assertEquals(ErrorCategory.ajax, errorLog1.getCategory());
+ Assert.assertEquals("test", errorLog1.getMessage());
+ Assert.assertEquals(1, errorLog1.getLine());
+ Assert.assertEquals(1, errorLog1.getCol());
+ Assert.assertEquals("e2e", errorLog1.getStack());
+ Assert.assertEquals("/e2e-browser", errorLog1.getErrorUrl());
+ BrowserErrorLog errorLog2 = browserErrorLogs.get(1);
+ Assert.assertEquals("55ec6178-3fb7-43ef-899c-a26944407b02", errorLog2.getUniqueId());
+ }
+}