diff --git a/systemtest/approvals/TestRUMErrorSourcemapping.approved.json b/systemtest/approvals/TestRUMErrorSourcemapping.approved.json new file mode 100644 index 00000000000..f18bc666667 --- /dev/null +++ b/systemtest/approvals/TestRUMErrorSourcemapping.approved.json @@ -0,0 +1,312 @@ +{ + "events": [ + { + "@timestamp": "dynamic", + "agent": { + "name": "rum-js", + "version": "0.0.0" + }, + "client": { + "ip": "127.0.0.1" + }, + "ecs": { + "version": "dynamic" + }, + "error": { + "culprit": "webpack:///webpack/bootstrap 6002740481c9666b0d38 in \u003canonymous\u003e", + "exception": [ + { + "message": "Uncaught Error: timeout test error", + "stacktrace": [ + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "context": { + "post": [ + "", + " \t\t// Check if module is in cache", + " \t\tif(installedModules[moduleId])", + " \t\t\treturn installedModules[moduleId].exports;", + "" + ], + "pre": [ + " \t// The module cache", + " \tvar installedModules = {};", + "", + " \t// The require function" + ] + }, + "exclude_from_grouping": false, + "filename": "webpack:///webpack/bootstrap 6002740481c9666b0d38", + "function": "__webpack_require__", + "library_frame": false, + "line": { + "column": 0, + "context": " \tfunction __webpack_require__(moduleId) {", + "number": 5 + }, + "original": { + "abs_path": "http://localhost:8000/test/../test/e2e/general-usecase/bundle.js.map", + "colno": 18, + "filename": "test/e2e/general-usecase/bundle.js.map", + "function": "\u003canonymous\u003e", + "library_frame": true, + "lineno": 1 + }, + "sourcemap": { + "updated": true + } + }, + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "context": { + "post": [ + "", + " \t// __webpack_public_path__", + " \t__webpack_require__.p = \"\";", + "", + " \t// Load entry module and return exports" + ], + "pre": [ + "", + " \t// expose the modules object (__webpack_modules__)", + " \t__webpack_require__.m = modules;", + "", + " \t// expose the module cache" + ] + }, + "exclude_from_grouping": false, + "filename": "webpack:///webpack/bootstrap 6002740481c9666b0d38", + "function": "\u003cunknown\u003e", + "library_frame": false, + "line": { + "column": 0, + "context": " \t__webpack_require__.c = installedModules;", + "number": 33 + }, + "original": { + "abs_path": "http://localhost:8000/test/./e2e/general-usecase/bundle.js.map", + "colno": 181, + "filename": "~/test/e2e/general-usecase/bundle.js.map", + "function": "invokeTask", + "library_frame": false, + "lineno": 1 + }, + "sourcemap": { + "updated": true + } + }, + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "context": { + "post": [ + "", + " \t\t// Check if module is in cache", + " \t\tif(installedModules[moduleId])", + " \t\t\treturn installedModules[moduleId].exports;", + "" + ], + "pre": [ + " \t// The module cache", + " \tvar installedModules = {};", + "", + " \t// The require function" + ] + }, + "exclude_from_grouping": false, + "filename": "webpack:///webpack/bootstrap 6002740481c9666b0d38", + "function": "\u003cunknown\u003e", + "library_frame": false, + "line": { + "column": 0, + "context": " \tfunction __webpack_require__(moduleId) {", + "number": 5 + }, + "original": { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "colno": 15, + "filename": "~/test/e2e/general-usecase/bundle.js.map", + "function": "runTask", + "lineno": 1 + }, + "sourcemap": { + "updated": true + } + }, + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "context": { + "post": [ + "", + "", + "", + "/** WEBPACK FOOTER **", + " ** webpack/bootstrap 6002740481c9666b0d38" + ], + "pre": [ + "", + " \t// __webpack_public_path__", + " \t__webpack_require__.p = \"\";", + "", + " \t// Load entry module and return exports" + ] + }, + "exclude_from_grouping": false, + "filename": "webpack:///webpack/bootstrap 6002740481c9666b0d38", + "function": "moduleId", + "library_frame": false, + "line": { + "column": 0, + "context": " \treturn __webpack_require__(0);", + "number": 39 + }, + "original": { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "colno": 199, + "filename": "~/test/e2e/general-usecase/bundle.js.map", + "function": "invoke", + "lineno": 1 + }, + "sourcemap": { + "updated": true + } + }, + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "context": { + "post": [ + " \t\t\treturn installedModules[moduleId].exports;", + "", + " \t\t// Create a new module (and put it into the cache)", + " \t\tvar module = installedModules[moduleId] = {", + " \t\t\texports: {}," + ], + "pre": [ + "", + " \t// The require function", + " \tfunction __webpack_require__(moduleId) {", + "", + " \t\t// Check if module is in cache" + ] + }, + "exclude_from_grouping": false, + "filename": "webpack:///webpack/bootstrap 6002740481c9666b0d38", + "function": "\u003canonymous\u003e", + "library_frame": false, + "line": { + "column": 0, + "context": " \t\tif(installedModules[moduleId])", + "number": 8 + }, + "original": { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "colno": 33, + "filename": "~/test/e2e/general-usecase/bundle.js.map", + "function": "timer", + "lineno": 1 + }, + "sourcemap": { + "updated": true + } + } + ], + "type": "Error" + } + ], + "grouping_key": "89e23da755c2dd759d2d529e37c92b8f", + "id": "aba2688e033848ce9c4e4005f1caa534", + "log": { + "message": "Uncaught Error: log timeout test error", + "stacktrace": [ + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "context": { + "post": [ + "", + " \t\t// Check if module is in cache", + " \t\tif(installedModules[moduleId])", + " \t\t\treturn installedModules[moduleId].exports;", + "" + ], + "pre": [ + " \t// The module cache", + " \tvar installedModules = {};", + "", + " \t// The require function" + ] + }, + "exclude_from_grouping": false, + "filename": "webpack:///webpack/bootstrap 6002740481c9666b0d38", + "function": "\u003canonymous\u003e", + "library_frame": false, + "line": { + "column": 0, + "context": " \tfunction __webpack_require__(moduleId) {", + "number": 5 + }, + "original": { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "colno": 18, + "filename": "~/test/e2e/general-usecase/bundle.js.map", + "function": "\u003canonymous\u003e", + "lineno": 1 + }, + "sourcemap": { + "updated": true + } + } + ] + }, + "page": { + "referer": "http://localhost:8000/test/e2e/", + "url": "http://localhost:8000/test/e2e/general-usecase/" + } + }, + "event": { + "ingested": "dynamic" + }, + "http": { + "request": { + "referrer": "http://localhost:8000/test/e2e/" + } + }, + "observer": { + "ephemeral_id": "dynamic", + "hostname": "dynamic", + "id": "dynamic", + "type": "apm-server", + "version": "dynamic", + "version_major": "dynamic" + }, + "processor": { + "event": "error", + "name": "error" + }, + "service": { + "name": "apm-agent-js", + "version": "1.0.1" + }, + "source": { + "ip": "127.0.0.1" + }, + "timestamp": { + "us": "dynamic" + }, + "url": { + "domain": "localhost", + "full": "http://localhost:8000/test/e2e/general-usecase/", + "original": "http://localhost:8000/test/e2e/general-usecase/", + "path": "/test/e2e/general-usecase/", + "port": 8000, + "scheme": "http" + }, + "user_agent": { + "device": { + "name": "Other" + }, + "name": "Go-http-client", + "original": "Go-http-client/1.1", + "version": "1.1" + } + } + ] +} diff --git a/systemtest/rum_test.go b/systemtest/rum_test.go index 7e58516e871..559311c2df2 100644 --- a/systemtest/rum_test.go +++ b/systemtest/rum_test.go @@ -18,10 +18,14 @@ package systemtest_test import ( + "bytes" "io" "io/ioutil" + "mime/multipart" "net/http" "net/url" + "os" + "path/filepath" "strings" "testing" @@ -62,3 +66,74 @@ func TestRUMXForwardedFor(t *testing.T) { "@timestamp", "timestamp.us", ) } + +func TestRUMErrorSourcemapping(t *testing.T) { + systemtest.CleanupElasticsearch(t) + srv := apmservertest.NewUnstartedServer(t) + srv.Config.RUM = &apmservertest.RUMConfig{Enabled: true} + err := srv.Start() + require.NoError(t, err) + + uploadSourcemap(t, srv, "../testdata/sourcemap/bundle.js.map", + "http://localhost:8000/test/e2e/../e2e/general-usecase/bundle.js.map", // bundle filepath + "apm-agent-js", // service name + "1.0.1", // service version + ) + systemtest.Elasticsearch.ExpectDocs(t, "apm-*-sourcemap", nil) + + sendRUMEventsPayload(t, srv, "../testdata/intake-v2/errors_rum.ndjson") + result := systemtest.Elasticsearch.ExpectDocs(t, "apm-*-error", nil) + + systemtest.ApproveEvents( + t, t.Name(), result.Hits.Hits, + // RUM timestamps are set by the server based on the time the payload is received. + "@timestamp", "timestamp.us", + ) +} + +func sendRUMEventsPayload(t *testing.T, srv *apmservertest.Server, payloadFile string) { + t.Helper() + + f, err := os.Open(payloadFile) + require.NoError(t, err) + defer f.Close() + + req, _ := http.NewRequest("POST", srv.URL+"/intake/v2/rum/events", f) + req.Header.Add("Content-Type", "application/x-ndjson") + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + respBody, err := ioutil.ReadAll(resp.Body) + require.NoError(t, err) + require.Equal(t, http.StatusAccepted, resp.StatusCode, string(respBody)) +} + +func uploadSourcemap(t *testing.T, srv *apmservertest.Server, sourcemapFile, bundleFilepath, serviceName, serviceVersion string) { + t.Helper() + + var data bytes.Buffer + mw := multipart.NewWriter(&data) + require.NoError(t, mw.WriteField("service_name", serviceName)) + require.NoError(t, mw.WriteField("service_version", serviceVersion)) + require.NoError(t, mw.WriteField("bundle_filepath", bundleFilepath)) + + f, err := os.Open(sourcemapFile) + require.NoError(t, err) + defer f.Close() + sourcemapFileWriter, err := mw.CreateFormFile("sourcemap", filepath.Base(sourcemapFile)) + require.NoError(t, err) + _, err = io.Copy(sourcemapFileWriter, f) + require.NoError(t, err) + require.NoError(t, mw.Close()) + + req, _ := http.NewRequest("POST", srv.URL+"/assets/v1/sourcemaps", &data) + req.Header.Add("Content-Type", mw.FormDataContentType()) + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + respBody, err := ioutil.ReadAll(resp.Body) + require.NoError(t, err) + require.Equal(t, http.StatusAccepted, resp.StatusCode, string(respBody)) +} diff --git a/tests/system/test_integration_sourcemap.py b/tests/system/test_integration_sourcemap.py index 667e654f105..fef3f21bb64 100644 --- a/tests/system/test_integration_sourcemap.py +++ b/tests/system/test_integration_sourcemap.py @@ -62,17 +62,6 @@ def test_duplicated_sourcemap_warning(self): self.assert_no_logged_warnings( ["WARN.*Overriding sourcemap", "WARN.*2 sourcemaps found for service"]) - def test_rum_error(self): - # use an uncleaned path to test that path is cleaned in upload - path = 'http://localhost:8000/test/e2e/../e2e/general-usecase/bundle.js.map' - self.upload_sourcemap(bundle_filepath=path) - self.load_docs_with_template(self.get_error_payload_path(), - self.intake_url, - 'error', - 1) - self.assert_no_logged_warnings() - self.check_rum_error_sourcemap(True) - def test_backend_span(self): # ensure source mapping is not applied to backend events # load event for which a sourcemap would be applied when sent to rum endpoint, @@ -164,17 +153,6 @@ def test_sourcemap_mapping_cache_usage(self): self.assert_no_logged_warnings() self.check_rum_error_sourcemap(True) - def test_rum_error_changed_index(self): - # use an uncleaned path to test that path is cleaned in upload - path = 'http://localhost:8000/test/e2e/../e2e/general-usecase/bundle.js.map' - self.upload_sourcemap(bundle_filepath=path) - self.load_docs_with_template(self.get_error_payload_path(), - self.intake_url, - 'error', - 1) - self.assert_no_logged_warnings() - self.check_rum_error_sourcemap(True) - @integration_test class SourcemappingCacheIntegrationTest(BaseSourcemapTest):