[APM] Convert errors API to typescript (#26801) (#27071)

This commit is contained in:
Søren Louv-Jansen 2018-12-12 22:18:19 +01:00 committed by GitHub
parent d7c1353154
commit 66a432b1c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 1327 additions and 207 deletions

View file

@ -5,8 +5,8 @@
*/
import { get } from 'lodash';
import { Span } from '../typings/Span';
import { Transaction } from '../typings/Transaction';
import { Span } from '../typings/es_schemas/Span';
import { Transaction } from '../typings/es_schemas/Transaction';
import * as constants from './constants';
describe('Transaction v1:', () => {

View file

@ -13,8 +13,966 @@ exports[`DetailView should render with data 1`] = `
Error occurrence
</styled.h2>
<Component
groupId="c00e245c2fbebaf178fc31eeb2bb0250"
serviceName="opbeans-node"
error={
Object {
"@timestamp": "2018-01-09T14:39:00.274Z",
"beat": Object {
"hostname": "8be8857dbeda",
"name": "8be8857dbeda",
"version": "7.0.0-alpha1",
},
"context": Object {
"custom": Object {},
"process": Object {
"argv": Array [
"/usr/local/bin/node",
"/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js",
],
"pid": 36,
"title": "node /app/server.js",
},
"request": Object {
"headers": Object {
"connection": "close",
"host": "opbeans-node:3000",
"user-agent": "workload/2.4.3",
},
"http_version": "1.1",
"method": "GET",
"socket": Object {
"encrypted": false,
"remote_address": "::ffff:172.19.0.7",
},
"url": Object {
"hostname": "opbeans-node",
"pathname": "/is-it-coffee-time",
"port": "3000",
"raw": "/is-it-coffee-time",
},
},
"response": Object {
"finished": false,
"headers": Object {},
"headers_sent": false,
"status_code": 200,
},
"service": Object {
"agent": Object {
"name": "nodejs",
"version": "0.9.0",
},
"framework": Object {
"name": "express",
"version": "4.16.2",
},
"language": Object {
"name": "javascript",
},
"name": "opbeans-node",
"runtime": Object {
"name": "node",
"version": "v6.12.0",
},
},
"system": Object {
"architecture": "x64",
"hostname": "b4cb1df7e088",
"platform": "linux",
},
"tags": Object {
"foo": "bar",
"lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.",
"multi-line": "foo
bar
baz",
"this-is-a-very-long-tag-name-without-any-spaces": "test",
},
"user": Object {},
},
"error": Object {
"culprit": "<anonymous> (server/coffee.js)",
"exception": Object {
"message": "Cannot read property 'level' of undefined",
"stacktrace": Array [
Object {
"absPath": "/app/server/coffee.js",
"context": Object {
"post": Array [
" res.send('Of course!')",
" } else {",
" res.send('You can\\\\'t have any!')",
" }",
"})",
"",
"app.get('/log-error', function (req, res) {",
],
"pre": Array [
"",
"var express = require('express')",
"var apm = require('elastic-apm-node')",
"",
"var app = module.exports = new express.Router()",
"",
"app.get('/is-it-coffee-time', function (req, res) {",
],
},
"filename": "server/coffee.js",
"function": "<anonymous>",
"libraryFrame": false,
"line": Object {
"context": " if (req.paarms.level === 11) {",
"number": 9,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/layer.js",
"context": Object {
"post": Array [
" } catch (err) {",
" next(err);",
" }",
"};",
"",
"/**",
" * Check if this route matches \`path\`, if so",
],
"pre": Array [
"",
" if (fn.length > 3) {",
" // not a standard request handler",
" return next();",
" }",
"",
" try {",
],
},
"filename": "node_modules/express/lib/router/layer.js",
"function": "handle",
"libraryFrame": true,
"line": Object {
"context": " fn(req, res, next);",
"number": 95,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/route.js",
"context": Object {
"post": Array [
" }",
" }",
"};",
"",
"/**",
" * Add a handler for all HTTP verbs to this route.",
" *",
],
"pre": Array [
" if (layer.method && layer.method !== method) {",
" return next(err);",
" }",
"",
" if (err) {",
" layer.handle_error(err, req, res, next);",
" } else {",
],
},
"filename": "node_modules/express/lib/router/route.js",
"function": "next",
"libraryFrame": true,
"line": Object {
"context": " layer.handle_request(req, res, next);",
"number": 137,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/route.js",
"context": Object {
"post": Array [
"",
" function next(err) {",
" // signal to exit route",
" if (err && err === 'route') {",
" return done();",
" }",
"",
],
"pre": Array [
" var method = req.method.toLowerCase();",
" if (method === 'head' && !this.methods['head']) {",
" method = 'get';",
" }",
"",
" req.route = this;",
"",
],
},
"filename": "node_modules/express/lib/router/route.js",
"function": "dispatch",
"libraryFrame": true,
"line": Object {
"context": " next();",
"number": 112,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/layer.js",
"context": Object {
"post": Array [
" } catch (err) {",
" next(err);",
" }",
"};",
"",
"/**",
" * Check if this route matches \`path\`, if so",
],
"pre": Array [
"",
" if (fn.length > 3) {",
" // not a standard request handler",
" return next();",
" }",
"",
" try {",
],
},
"filename": "node_modules/express/lib/router/layer.js",
"function": "handle",
"libraryFrame": true,
"line": Object {
"context": " fn(req, res, next);",
"number": 95,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" }",
"",
" trim_prefix(layer, layerError, layerPath, path);",
" });",
" }",
"",
" function trim_prefix(layer, layerError, layerPath, path) {",
],
"pre": Array [
" // this should be done for the layer",
" self.process_params(layer, paramcalled, req, res, function (err) {",
" if (err) {",
" return next(layerError || err);",
" }",
"",
" if (route) {",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "<anonymous>",
"libraryFrame": true,
"line": Object {
"context": " return layer.handle_request(req, res, next);",
"number": 281,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" }",
"",
" var i = 0;",
" var name;",
" var paramIndex = 0;",
" var key;",
" var paramVal;",
],
"pre": Array [
" var params = this.params;",
"",
" // captured parameters from the layer, keys and values",
" var keys = layer.keys;",
"",
" // fast track",
" if (!keys || keys.length === 0) {",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "process_params",
"libraryFrame": true,
"line": Object {
"context": " return done();",
"number": 335,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" if (err) {",
" return next(layerError || err);",
" }",
"",
" if (route) {",
" return layer.handle_request(req, res, next);",
" }",
],
"pre": Array [
" // Capture one-time layer values",
" req.params = self.mergeParams",
" ? mergeParams(layer.params, parentParams)",
" : layer.params;",
" var layerPath = layer.path;",
"",
" // this should be done for the layer",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "next",
"libraryFrame": true,
"line": Object {
"context": " self.process_params(layer, paramcalled, req, res, function (err) {",
"number": 275,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
"",
" function next(err) {",
" var layerError = err === 'route'",
" ? null",
" : err;",
"",
" // remove added slash",
],
"pre": Array [
" });",
" }",
"",
" // setup basic req values",
" req.baseUrl = parentUrl;",
" req.originalUrl = req.originalUrl || req.url;",
"",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "handle",
"libraryFrame": true,
"line": Object {
"context": " next();",
"number": 174,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" }",
"",
" // mixin Router class functions",
" setPrototypeOf(router, proto)",
"",
" router.params = {};",
" router._params = [];",
],
"pre": Array [
" * @public",
" */",
"",
"var proto = module.exports = function(options) {",
" var opts = options || {};",
"",
" function router(req, res, next) {",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "router",
"libraryFrame": true,
"line": Object {
"context": " router.handle(req, res, next);",
"number": 47,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/layer.js",
"context": Object {
"post": Array [
" } catch (err) {",
" next(err);",
" }",
"};",
"",
"/**",
" * Check if this route matches \`path\`, if so",
],
"pre": Array [
"",
" if (fn.length > 3) {",
" // not a standard request handler",
" return next();",
" }",
"",
" try {",
],
},
"filename": "node_modules/express/lib/router/layer.js",
"function": "handle",
"libraryFrame": true,
"line": Object {
"context": " fn(req, res, next);",
"number": 95,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" }",
" }",
"};",
"",
"/**",
" * Process any parameters for the layer.",
" * @private",
],
"pre": Array [
" }",
"",
" debug('%s %s : %s', layer.name, layerPath, req.originalUrl);",
"",
" if (layerError) {",
" layer.handle_error(layerError, req, res, next);",
" } else {",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "trim_prefix",
"libraryFrame": true,
"line": Object {
"context": " layer.handle_request(req, res, next);",
"number": 317,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" });",
" }",
"",
" function trim_prefix(layer, layerError, layerPath, path) {",
" if (layerPath.length !== 0) {",
" // Validate path breaks on a path separator",
" var c = path[layerPath.length]",
],
"pre": Array [
" return next(layerError || err);",
" }",
"",
" if (route) {",
" return layer.handle_request(req, res, next);",
" }",
"",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "<anonymous>",
"libraryFrame": true,
"line": Object {
"context": " trim_prefix(layer, layerError, layerPath, path);",
"number": 284,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" }",
"",
" var i = 0;",
" var name;",
" var paramIndex = 0;",
" var key;",
" var paramVal;",
],
"pre": Array [
" var params = this.params;",
"",
" // captured parameters from the layer, keys and values",
" var keys = layer.keys;",
"",
" // fast track",
" if (!keys || keys.length === 0) {",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "process_params",
"libraryFrame": true,
"line": Object {
"context": " return done();",
"number": 335,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" if (err) {",
" return next(layerError || err);",
" }",
"",
" if (route) {",
" return layer.handle_request(req, res, next);",
" }",
],
"pre": Array [
" // Capture one-time layer values",
" req.params = self.mergeParams",
" ? mergeParams(layer.params, parentParams)",
" : layer.params;",
" var layerPath = layer.path;",
"",
" // this should be done for the layer",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "next",
"libraryFrame": true,
"line": Object {
"context": " self.process_params(layer, paramcalled, req, res, function (err) {",
"number": 275,
},
},
Object {
"absPath": "/app/server.js",
"context": Object {
"post": Array [
"})",
"",
"app.use(require('./server/coffee'))",
"app.use('/api', require('./server/routes'))",
"app.get('*', function (req, res) {",
" res.sendFile(path.resolve(__dirname, 'client/build', 'index.html'))",
"})",
],
"pre": Array [
"app.use(require('body-parser').json())",
"app.use(express.static('client/build'))",
"app.use(function (req, res, next) {",
" apm.setTag('foo', 'bar')",
" apm.setTag('lorem', 'ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.')",
" apm.setTag('this-is-a-very-long-tag-name-without-any-spaces', 'test')",
" apm.setTag('multi-line', 'foo\\\\nbar\\\\nbaz')",
],
},
"filename": "server.js",
"function": "<anonymous>",
"libraryFrame": false,
"line": Object {
"context": " next()",
"number": 27,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/layer.js",
"context": Object {
"post": Array [
" } catch (err) {",
" next(err);",
" }",
"};",
"",
"/**",
" * Check if this route matches \`path\`, if so",
],
"pre": Array [
"",
" if (fn.length > 3) {",
" // not a standard request handler",
" return next();",
" }",
"",
" try {",
],
},
"filename": "node_modules/express/lib/router/layer.js",
"function": "handle",
"libraryFrame": true,
"line": Object {
"context": " fn(req, res, next);",
"number": 95,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" }",
" }",
"};",
"",
"/**",
" * Process any parameters for the layer.",
" * @private",
],
"pre": Array [
" }",
"",
" debug('%s %s : %s', layer.name, layerPath, req.originalUrl);",
"",
" if (layerError) {",
" layer.handle_error(layerError, req, res, next);",
" } else {",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "trim_prefix",
"libraryFrame": true,
"line": Object {
"context": " layer.handle_request(req, res, next);",
"number": 317,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" });",
" }",
"",
" function trim_prefix(layer, layerError, layerPath, path) {",
" if (layerPath.length !== 0) {",
" // Validate path breaks on a path separator",
" var c = path[layerPath.length]",
],
"pre": Array [
" return next(layerError || err);",
" }",
"",
" if (route) {",
" return layer.handle_request(req, res, next);",
" }",
"",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "<anonymous>",
"libraryFrame": true,
"line": Object {
"context": " trim_prefix(layer, layerError, layerPath, path);",
"number": 284,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" }",
"",
" var i = 0;",
" var name;",
" var paramIndex = 0;",
" var key;",
" var paramVal;",
],
"pre": Array [
" var params = this.params;",
"",
" // captured parameters from the layer, keys and values",
" var keys = layer.keys;",
"",
" // fast track",
" if (!keys || keys.length === 0) {",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "process_params",
"libraryFrame": true,
"line": Object {
"context": " return done();",
"number": 335,
},
},
Object {
"absPath": "/app/node_modules/express/lib/router/index.js",
"context": Object {
"post": Array [
" if (err) {",
" return next(layerError || err);",
" }",
"",
" if (route) {",
" return layer.handle_request(req, res, next);",
" }",
],
"pre": Array [
" // Capture one-time layer values",
" req.params = self.mergeParams",
" ? mergeParams(layer.params, parentParams)",
" : layer.params;",
" var layerPath = layer.path;",
"",
" // this should be done for the layer",
],
},
"filename": "node_modules/express/lib/router/index.js",
"function": "next",
"libraryFrame": true,
"line": Object {
"context": " self.process_params(layer, paramcalled, req, res, function (err) {",
"number": 275,
},
},
Object {
"absPath": "/app/node_modules/elastic-apm-node/lib/instrumentation/modules/express.js",
"context": Object {
"post": Array [
" }",
" }",
" }",
" })",
"",
" return express",
"}",
],
"pre": Array [
" return function serveStatic (req, res, next) {",
" req._elastic_apm_static = true",
"",
" return origServeStatic(req, res, nextHook)",
"",
" function nextHook (err) {",
" if (!err) req._elastic_apm_static = false",
],
},
"filename": "node_modules/elastic-apm-node/lib/instrumentation/modules/express.js",
"function": "nextHook",
"libraryFrame": true,
"line": Object {
"context": " return next.apply(this, arguments)",
"number": 90,
},
},
Object {
"absPath": "/app/node_modules/serve-static/index.js",
"context": Object {
"post": Array [
" })",
"",
" // pipe",
" stream.pipe(res)",
" }",
"}",
"",
],
"pre": Array [
" // forward errors",
" stream.on('error', function error (err) {",
" if (forwardError || !(err.statusCode < 500)) {",
" next(err)",
" return",
" }",
"",
],
},
"filename": "node_modules/serve-static/index.js",
"function": "error",
"libraryFrame": true,
"line": Object {
"context": " next()",
"number": 121,
},
},
Object {
"absPath": "events.js",
"filename": "events.js",
"function": "emitOne",
"libraryFrame": true,
"line": Object {
"number": 96,
},
},
Object {
"absPath": "events.js",
"filename": "events.js",
"function": "emit",
"libraryFrame": true,
"line": Object {
"number": 188,
},
},
Object {
"absPath": "/app/node_modules/send/index.js",
"context": Object {
"post": Array [
" expose: false",
" }))",
" }",
"",
" var res = this.res",
" var msg = statuses[status] || String(status)",
" var doc = createHtmlDocument('Error', escapeHtml(msg))",
],
"pre": Array [
" * @param {Error} [err]",
" * @private",
" */",
"",
"SendStream.prototype.error = function error (status, err) {",
" // emit if listeners instead of responding",
" if (hasListeners(this, 'error')) {",
],
},
"filename": "node_modules/send/index.js",
"function": "error",
"libraryFrame": true,
"line": Object {
"context": " return this.emit('error', createError(status, err, {",
"number": 270,
},
},
Object {
"absPath": "/app/node_modules/send/index.js",
"context": Object {
"post": Array [
" break",
" default:",
" this.error(500, error)",
" break",
" }",
"}",
"",
],
"pre": Array [
" */",
"",
"SendStream.prototype.onStatError = function onStatError (error) {",
" switch (error.code) {",
" case 'ENAMETOOLONG':",
" case 'ENOENT':",
" case 'ENOTDIR':",
],
},
"filename": "node_modules/send/index.js",
"function": "onStatError",
"libraryFrame": true,
"line": Object {
"context": " this.error(404, error)",
"number": 421,
},
},
Object {
"absPath": "/app/node_modules/send/index.js",
"context": Object {
"post": Array [
" : self.error(404)",
" }",
"",
" var p = path + '.' + self._extensions[i++]",
"",
" debug('stat \\"%s\\"', p)",
" fs.stat(p, function (err, stat) {",
],
"pre": Array [
" self.emit('file', path, stat)",
" self.send(path, stat)",
" })",
"",
" function next (err) {",
" if (self._extensions.length <= i) {",
" return err",
],
},
"filename": "node_modules/send/index.js",
"function": "next",
"libraryFrame": true,
"line": Object {
"context": " ? self.onStatError(err)",
"number": 736,
},
},
Object {
"absPath": "/app/node_modules/send/index.js",
"context": Object {
"post": Array [
" }",
" if (err) return self.onStatError(err)",
" if (stat.isDirectory()) return self.redirect(path)",
" self.emit('file', path, stat)",
" self.send(path, stat)",
" })",
"",
],
"pre": Array [
" var i = 0",
" var self = this",
"",
" debug('stat \\"%s\\"', path)",
" fs.stat(path, function onstat (err, stat) {",
" if (err && err.code === 'ENOENT' && !extname(path) && path[path.length - 1] !== sep) {",
" // not found, check extensions",
],
},
"filename": "node_modules/send/index.js",
"function": "onstat",
"libraryFrame": true,
"line": Object {
"context": " return next(err)",
"number": 725,
},
},
Object {
"absPath": "/app/node_modules/elastic-apm-node/lib/instrumentation/index.js",
"context": Object {
"post": Array [
" ins.currentTransaction = prev",
" return result",
" }",
"}",
"",
"Instrumentation.prototype._recoverTransaction = function (trans) {",
" if (this.currentTransaction === trans) return",
],
"pre": Array [
" var trans = this.currentTransaction",
"",
" return elasticAPMCallbackWrapper",
"",
" function elasticAPMCallbackWrapper () {",
" var prev = ins.currentTransaction",
" ins.currentTransaction = trans",
],
},
"filename": "node_modules/elastic-apm-node/lib/instrumentation/index.js",
"function": "elasticAPMCallbackWrapper",
"libraryFrame": true,
"line": Object {
"context": " var result = original.apply(this, arguments)",
"number": 116,
},
},
Object {
"absPath": "fs.js",
"filename": "fs.js",
"function": "FSReqWrap.oncomplete",
"libraryFrame": true,
"line": Object {
"number": 123,
},
},
],
"type": "TypeError",
},
"groupingKey": "c00e245c2fbebaf178fc31eeb2bb0250",
"id": "c5e55dfc-09cc-4e0d-ace3-1ba4233f66eb",
},
"processor": Object {
"event": "error",
"name": "error",
},
}
}
>
<EuiButtonEmpty
color="primary"
@ -1102,7 +2060,6 @@ baz",
"name": "error",
},
},
"groupId": "c00e245c2fbebaf178fc31eeb2bb0250",
"occurrencesCount": 18,
},
"status": "SUCCESS",

View file

@ -919,8 +919,7 @@
"version": "7.0.0-alpha1"
}
},
"occurrencesCount": 18,
"groupId": "c00e245c2fbebaf178fc31eeb2bb0250"
"occurrencesCount": 18
},
"status": "SUCCESS"
},

View file

@ -87,7 +87,6 @@ function DetailView({ errorGroup, urlParams, location }) {
return null;
}
const { serviceName } = urlParams;
const stickyProperties = [
{
fieldName: '@timestamp',
@ -129,7 +128,6 @@ function DetailView({ errorGroup, urlParams, location }) {
const tabs = getTabs(context, logStackframes);
const currentTab = getCurrentTab(tabs, urlParams.detailTab);
const occurencesCount = errorGroup.data.occurrencesCount;
const groupId = errorGroup.data.groupId;
const agentName = get(errorGroup.data.error, SERVICE_AGENT_NAME);
return (
@ -143,8 +141,7 @@ function DetailView({ errorGroup, urlParams, location }) {
Error occurrence
</HeaderMedium>
<DiscoverErrorButton
serviceName={serviceName}
groupId={groupId}
error={errorGroup.data.error}
kuery={urlParams.kuery}
>
<EuiButtonEmpty iconType="discoverApp">

View file

@ -12,7 +12,7 @@ import {
} from '@elastic/eui';
import React from 'react';
import { KibanaLink } from 'x-pack/plugins/apm/public/utils/url';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import { DiscoverTransactionButton } from '../../../shared/DiscoverButtons/DiscoverTransactionButton';
function getInfraMetricsQuery(transaction: Transaction) {

View file

@ -12,7 +12,7 @@ import {
TRANSACTION_RESULT,
USER_ID
} from '../../../../../common/constants';
import { Transaction } from '../../../../../typings/Transaction';
import { Transaction } from '../../../../../typings/es_schemas/Transaction';
import { asPercent, asTime } from '../../../../utils/formatters';
// @ts-ignore
import {

View file

@ -14,7 +14,7 @@ import {
import { capitalize, first, get } from 'lodash';
import React from 'react';
import styled from 'styled-components';
import { Transaction } from '../../../../../typings/Transaction';
import { Transaction } from '../../../../../typings/es_schemas/Transaction';
import { IUrlParams } from '../../../../store/urlParams';
import { px, units } from '../../../../style/variables';
import { fromQuery, history, toQuery } from '../../../../utils/url';

View file

@ -8,7 +8,7 @@
import { EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui';
import { capitalize, first, get } from 'lodash';
import React from 'react';
import { Transaction } from '../../../../../typings/Transaction';
import { Transaction } from '../../../../../typings/es_schemas/Transaction';
import { IUrlParams } from '../../../../store/urlParams';
// @ts-ignore
import { fromQuery, history, toQuery } from '../../../../utils/url';

View file

@ -12,7 +12,7 @@ import {
import { StickyProperties } from 'x-pack/plugins/apm/public/components/shared/StickyProperties';
import { TransactionLink } from 'x-pack/plugins/apm/public/components/shared/TransactionLink';
import { KibanaLink } from 'x-pack/plugins/apm/public/utils/url';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
interface Props {
transaction?: Transaction;

View file

@ -27,7 +27,7 @@ import { xcode } from 'react-syntax-highlighter/dist/styles';
import sql from 'react-syntax-highlighter/dist/languages/sql';
import { EuiTitle } from '@elastic/eui';
import { DbContext } from '../../../../../../../../typings/Span';
import { DbContext } from '../../../../../../../../typings/es_schemas/Span';
registerLanguage('sql', sql);

View file

@ -16,7 +16,7 @@ import {
} from '../../../../../../../style/variables';
import { EuiTitle } from '@elastic/eui';
import { HttpContext } from '../../../../../../../../typings/Span';
import { HttpContext } from '../../../../../../../../typings/es_schemas/Span';
const DatabaseStatement = styled.div`
margin-top: ${px(unit)};

View file

@ -11,7 +11,7 @@ import {
SPAN_NAME,
SPAN_TYPE
} from 'x-pack/plugins/apm/common/constants';
import { Span } from '../../../../../../../../typings/Span';
import { Span } from '../../../../../../../../typings/es_schemas/Span';
import { asMillis, asPercent } from '../../../../../../../utils/formatters';
import { StickyProperties } from '../../../../../../shared/StickyProperties';

View file

@ -33,8 +33,8 @@ import { HttpContext } from './HttpContext';
import { StickySpanProperties } from './StickySpanProperties';
import { DiscoverSpanButton } from 'x-pack/plugins/apm/public/components/shared/DiscoverButtons/DiscoverSpanButton';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Span } from '../../../../../../../../typings/Span';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import { Span } from '../../../../../../../../typings/es_schemas/Span';
import { FlyoutTopLevelProperties } from '../FlyoutTopLevelProperties';
const StackTraceContainer = styled.div`

View file

@ -23,7 +23,7 @@ import styled from 'styled-components';
import { DiscoverTransactionButton } from 'x-pack/plugins/apm/public/components/shared/DiscoverButtons/DiscoverTransactionButton';
import { IUrlParams } from 'x-pack/plugins/apm/public/store/urlParams';
import { APM_AGENT_DROPPED_SPANS_DOCS } from 'x-pack/plugins/apm/public/utils/documentation/agents';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import { StickyTransactionProperties } from '../../../StickyTransactionProperties';
import { TransactionPropertiesTableForFlyout } from '../../../TransactionPropertiesTableForFlyout';
import { FlyoutTopLevelProperties } from '../FlyoutTopLevelProperties';

View file

@ -5,8 +5,8 @@
*/
import { groupBy } from 'lodash';
import { Span } from 'x-pack/plugins/apm/typings/Span';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Span } from 'x-pack/plugins/apm/typings/es_schemas/Span';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import {
getClockSkew,
getWaterfallItems,

View file

@ -15,8 +15,8 @@ import {
zipObject
} from 'lodash';
import { colors } from 'x-pack/plugins/apm/public/style/variables';
import { Span } from '../../../../../../../../typings/Span';
import { Transaction } from '../../../../../../../../typings/Transaction';
import { Span } from '../../../../../../../../typings/es_schemas/Span';
import { Transaction } from '../../../../../../../../typings/es_schemas/Transaction';
export interface IWaterfallIndex {
[key: string]: IWaterfallItem;

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import { getAgentMarks } from './get_agent_marks';
describe('getAgentMarks', () => {

View file

@ -5,7 +5,7 @@
*/
import { sortBy } from 'lodash';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
export interface AgentMark {
name: string;

View file

@ -11,7 +11,7 @@ import {
TRACE_ID,
TRANSACTION_ID
} from '../../../../../../common/constants';
import { Transaction } from '../../../../../../typings/Transaction';
import { Transaction } from '../../../../../../typings/es_schemas/Transaction';
import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';

View file

@ -15,7 +15,7 @@ import {
EuiToolTip
} from '@elastic/eui';
import React from 'react';
import { Transaction as ITransaction } from '../../../../../typings/Transaction';
import { Transaction as ITransaction } from '../../../../../typings/es_schemas/Transaction';
import { IUrlParams } from '../../../../store/urlParams';
import { DiscoverTransactionButton } from '../../../shared/DiscoverButtons/DiscoverTransactionButton';
import { TransactionLink } from '../../../shared/TransactionLink';

View file

@ -25,7 +25,7 @@ import python from 'react-syntax-highlighter/dist/languages/python';
import ruby from 'react-syntax-highlighter/dist/languages/ruby';
// @ts-ignore
import { registerLanguage } from 'react-syntax-highlighter/dist/light';
import { Stackframe } from '../../../../typings/APMDoc';
import { Stackframe } from '../../../../typings/es_schemas/APMDoc';
import { FrameHeading } from '../Stacktrace/FrameHeading';
// @ts-ignore
import { Context } from './Context';

View file

@ -9,9 +9,12 @@ import {
ERROR_GROUP_ID,
SERVICE_NAME
} from 'x-pack/plugins/apm/common/constants';
import { APMError } from 'x-pack/plugins/apm/typings/es_schemas/Error';
import { DiscoverButton } from './DiscoverButton';
function getDiscoverQuery(serviceName: string, groupId: string, kuery: string) {
function getDiscoverQuery(error: APMError, kuery: string) {
const serviceName = error.context.service.name;
const groupId = error.error.grouping_key;
let query = `${SERVICE_NAME}:"${serviceName}" AND ${ERROR_GROUP_ID}:"${groupId}"`;
if (kuery) {
query = ` AND ${kuery}`;
@ -30,13 +33,12 @@ function getDiscoverQuery(serviceName: string, groupId: string, kuery: string) {
}
export const DiscoverErrorButton: React.SFC<{
readonly serviceName: string;
readonly groupId: string;
readonly error: APMError;
readonly kuery: string;
}> = ({ serviceName, groupId, kuery, children }) => {
}> = ({ error, kuery, children }) => {
return (
<DiscoverButton
query={getDiscoverQuery(serviceName, groupId, kuery)}
query={getDiscoverQuery(error, kuery)}
children={children}
/>
);

View file

@ -6,7 +6,7 @@
import React from 'react';
import { SPAN_HEX_ID, SPAN_ID } from 'x-pack/plugins/apm/common/constants';
import { Span } from 'x-pack/plugins/apm/typings/Span';
import { Span } from 'x-pack/plugins/apm/typings/es_schemas/Span';
import { DiscoverButton } from './DiscoverButton';
function getDiscoverQuery(span: Span) {

View file

@ -10,7 +10,7 @@ import {
TRACE_ID,
TRANSACTION_ID
} from 'x-pack/plugins/apm/common/constants';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import { DiscoverButton } from './DiscoverButton';
function getDiscoverQuery(transaction: Transaction) {

View file

@ -7,7 +7,7 @@
import { get } from 'lodash';
import React, { Fragment } from 'react';
import styled from 'styled-components';
import { Stackframe } from '../../../../typings/APMDoc';
import { Stackframe } from '../../../../typings/es_schemas/APMDoc';
import { colors, fontFamilyCode, px, units } from '../../../style/variables';
const FileDetails = styled.div`

View file

@ -7,7 +7,7 @@
import { EuiLink } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
import { Stackframe } from '../../../../typings/APMDoc';
import { Stackframe } from 'x-pack/plugins/apm/typings/es_schemas/APMDoc';
import { px, units } from '../../../style/variables';
import { CodePreview } from '../../shared/CodePreview';
// @ts-ignore

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Stackframe } from '../../../../../typings/APMDoc';
import { Stackframe } from '../../../../../typings/es_schemas/APMDoc';
import { getGroupedStackframes, hasSourceLines } from '../stacktraceUtils';
import stacktracesMock from './stacktraces.json';

View file

@ -7,7 +7,7 @@
import { EuiTitle } from '@elastic/eui';
import { isEmpty } from 'lodash';
import React, { PureComponent } from 'react';
import { Stackframe } from '../../../../typings/APMDoc';
import { Stackframe } from '../../../../typings/es_schemas/APMDoc';
import { CodePreview } from '../../shared/CodePreview';
import { EmptyMessage } from '../../shared/EmptyMessage';
// @ts-ignore

View file

@ -5,7 +5,7 @@
*/
import { get, isEmpty } from 'lodash';
import { Stackframe } from '../../../../typings/APMDoc';
import { Stackframe } from 'x-pack/plugins/apm/typings/es_schemas/APMDoc';
interface StackframesGroup {
isLibraryFrame: boolean;

View file

@ -5,7 +5,7 @@
*/
import React from 'react';
import { Transaction } from '../../../typings/Transaction';
import { Transaction } from '../../../typings/es_schemas/Transaction';
import { KibanaLink, legacyEncodeURIComponent } from '../../utils/url';
interface TransactionLinkProps {

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Span } from 'x-pack/plugins/apm/typings/Span';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Span } from 'x-pack/plugins/apm/typings/es_schemas/Span';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import { convertKueryToEsQuery, getAPMIndexPatternForKuery } from '../../kuery';
export async function getEncodedEsQuery(kuery?: string) {

View file

@ -4,7 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { camelizeKeys } from 'humps';
import { ErrorDistributionAPIResponse } from 'x-pack/plugins/apm/server/lib/errors/distribution/get_distribution';
import { ErrorGroupAPIResponse } from 'x-pack/plugins/apm/server/lib/errors/get_error_group';
import { ErrorGroupListAPIResponse } from 'x-pack/plugins/apm/server/lib/errors/get_error_groups';
import { IUrlParams } from '../../../store/urlParams';
import { callApi } from '../callApi';
import { getEncodedEsQuery } from './apm';
@ -24,7 +26,7 @@ export async function loadErrorGroupList({
sortField,
sortDirection
}: ErrorGroupListParams) {
return callApi({
return callApi<ErrorGroupListAPIResponse>({
pathname: `/api/apm/services/${serviceName}/errors`,
query: {
start,
@ -44,8 +46,7 @@ export async function loadErrorGroupDetails({
kuery,
errorGroupId
}: IUrlParams) {
// TODO: add types when error section is converted to ts
const res = await callApi<any>(
return callApi<ErrorGroupAPIResponse>(
{
pathname: `/api/apm/services/${serviceName}/errors/${errorGroupId}`,
query: {
@ -58,11 +59,6 @@ export async function loadErrorGroupDetails({
camelcase: false
}
);
const camelizedRes: any = camelizeKeys(res);
if (res.error.context) {
camelizedRes.error.context = res.error.context;
}
return camelizedRes;
}
export async function loadErrorDistribution({
@ -72,7 +68,7 @@ export async function loadErrorDistribution({
kuery,
errorGroupId
}: IUrlParams) {
return callApi({
return callApi<ErrorDistributionAPIResponse>({
pathname: `/api/apm/services/${serviceName}/errors/${errorGroupId}/distribution`,
query: {
start,

View file

@ -6,7 +6,7 @@
import { TransactionAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/get_transaction';
import { SpanListAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/spans/get_spans';
import { Span } from 'x-pack/plugins/apm/typings/Span';
import { Span } from 'x-pack/plugins/apm/typings/es_schemas/Span';
import { IUrlParams } from '../../../store/urlParams';
import { callApi } from '../callApi';
import { addVersion, getEncodedEsQuery } from './apm';

View file

@ -5,19 +5,33 @@
*/
import React from 'react';
import { Request } from 'react-redux-request';
import { Request, RRRRender } from 'react-redux-request';
import { ErrorDistributionAPIResponse } from 'x-pack/plugins/apm/server/lib/errors/distribution/get_distribution';
import { loadErrorDistribution } from '../../services/rest/apm/error_groups';
import { IReduxState } from '../rootReducer';
import { IUrlParams } from '../urlParams';
// @ts-ignore
import { createInitialDataSelector } from './helpers';
const ID = 'errorDistribution';
const INITIAL_DATA = { buckets: [], totalHits: 0 };
const INITIAL_DATA: ErrorDistributionAPIResponse = {
buckets: [],
totalHits: 0,
bucketSize: 0
};
const withInitialData = createInitialDataSelector(INITIAL_DATA);
export function getErrorDistribution(state) {
export function getErrorDistribution(state: IReduxState) {
return withInitialData(state.reactReduxRequest[ID]);
}
export function ErrorDistributionRequest({ urlParams, render }) {
export function ErrorDistributionRequest({
urlParams,
render
}: {
urlParams: IUrlParams;
render: RRRRender<ErrorDistributionAPIResponse>;
}) {
const { serviceName, start, end, errorGroupId, kuery } = urlParams;
if (!(serviceName && start && end && errorGroupId)) {

View file

@ -5,19 +5,29 @@
*/
import React from 'react';
import { createInitialDataSelector } from './helpers';
import { Request } from 'react-redux-request';
import { Request, RRRRender } from 'react-redux-request';
import { ErrorGroupAPIResponse } from 'x-pack/plugins/apm/server/lib/errors/get_error_group';
import { loadErrorGroupDetails } from '../../services/rest/apm/error_groups';
import { IReduxState } from '../rootReducer';
import { IUrlParams } from '../urlParams';
// @ts-ignore
import { createInitialDataSelector } from './helpers';
const ID = 'errorGroupDetails';
const INITIAL_DATA = {};
const INITIAL_DATA: ErrorGroupAPIResponse = {};
const withInitialData = createInitialDataSelector(INITIAL_DATA);
export function getErrorGroupDetails(state) {
export function getErrorGroupDetails(state: IReduxState) {
return withInitialData(state.reactReduxRequest[ID]);
}
export function ErrorGroupDetailsRequest({ urlParams, render }) {
export function ErrorGroupDetailsRequest({
urlParams,
render
}: {
urlParams: IUrlParams;
render: RRRRender<ErrorGroupAPIResponse>;
}) {
const { serviceName, errorGroupId, start, end, kuery } = urlParams;
if (!(serviceName && start && end && errorGroupId)) {

View file

@ -5,19 +5,29 @@
*/
import React from 'react';
import { createInitialDataSelector } from './helpers';
import { Request } from 'react-redux-request';
import { Request, RRRRender } from 'react-redux-request';
import { ErrorGroupListAPIResponse } from 'x-pack/plugins/apm/server/lib/errors/get_error_groups';
import { loadErrorGroupList } from '../../services/rest/apm/error_groups';
import { IReduxState } from '../rootReducer';
import { IUrlParams } from '../urlParams';
// @ts-ignore
import { createInitialDataSelector } from './helpers';
const ID = 'errorGroupList';
const INITIAL_DATA = [];
const INITIAL_DATA: ErrorGroupListAPIResponse = [];
const withInitialData = createInitialDataSelector(INITIAL_DATA);
export function getErrorGroupList(state) {
export function getErrorGroupList(state: IReduxState) {
return withInitialData(state.reactReduxRequest[ID]);
}
export function ErrorGroupDetailsRequest({ urlParams, render }) {
export function ErrorGroupDetailsRequest({
urlParams,
render
}: {
urlParams: IUrlParams;
render: RRRRender<ErrorGroupListAPIResponse>;
}) {
const {
serviceName,
start,

View file

@ -6,7 +6,7 @@
import React from 'react';
import { Request, RRRRender } from 'react-redux-request';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import { loadTransaction } from '../../services/rest/apm/transactions';
import { IReduxState } from '../rootReducer';
import { IUrlParams } from '../urlParams';

View file

@ -6,7 +6,7 @@
import React from 'react';
import { RRRRender } from 'react-redux-request';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import { IWaterfall } from '../../components/app/TransactionDetails/Transaction/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers';
import { IUrlParams } from '../urlParams';
import { WaterfallV1Request } from './waterfallV1';

View file

@ -11,8 +11,8 @@ import {
SERVICE_NAME,
TRANSACTION_ID
} from 'x-pack/plugins/apm/common/constants';
import { Span } from 'x-pack/plugins/apm/typings/Span';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Span } from 'x-pack/plugins/apm/typings/es_schemas/Span';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import {
getWaterfall,
IWaterfall

View file

@ -9,7 +9,7 @@ import React from 'react';
import { Request, RRRRender } from 'react-redux-request';
import { TRACE_ID } from 'x-pack/plugins/apm/common/constants';
import { TraceAPIResponse } from 'x-pack/plugins/apm/server/lib/traces/get_trace';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import {
getWaterfall,
IWaterfall

View file

@ -157,12 +157,14 @@ export const getUrlParams = createSelector(
);
export interface IUrlParams {
detailTab?: string;
end?: number;
errorGroupId?: string;
flyoutDetailTab?: string;
detailTab?: string;
kuery?: string;
serviceName?: string;
sortDirection?: string;
sortField?: string;
start?: number;
traceId?: string;
transactionId?: string;

View file

@ -4,13 +4,25 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SERVICE_NAME, ERROR_GROUP_ID } from '../../../../common/constants';
import { BucketAgg } from 'x-pack/plugins/apm/typings/elasticsearch';
import { ERROR_GROUP_ID, SERVICE_NAME } from '../../../../common/constants';
import { Setup } from '../../helpers/setup_request';
export async function getBuckets({ serviceName, groupId, bucketSize, setup }) {
export async function getBuckets({
serviceName,
groupId,
bucketSize,
setup
}: {
serviceName: string;
groupId: string;
bucketSize: number;
setup: Setup;
}) {
const { start, end, esFilterQuery, client, config } = setup;
const params = {
index: config.get('apm_oss.errorIndices'),
index: config.get<string>('apm_oss.errorIndices'),
body: {
size: 0,
query: {
@ -50,7 +62,13 @@ export async function getBuckets({ serviceName, groupId, bucketSize, setup }) {
params.body.query.bool.filter.push(esFilterQuery);
}
const resp = await client('search', params);
interface Aggs {
distribution: {
buckets: Array<BucketAgg<number>>;
};
}
const resp = await client<void, Aggs>('search', params);
const buckets = resp.aggregations.distribution.buckets.map(bucket => ({
key: bucket.key,
@ -58,7 +76,7 @@ export async function getBuckets({ serviceName, groupId, bucketSize, setup }) {
}));
return {
total_hits: resp.hits.total,
totalHits: resp.hits.total,
buckets
};
}

View file

@ -1,28 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { getBuckets } from './get_buckets';
function getBucketSize({ start, end, config }) {
const bucketTargetCount = config.get('xpack.apm.bucketTargetCount');
return Math.floor((end - start) / bucketTargetCount);
}
export async function getDistribution({ serviceName, groupId, setup }) {
const bucketSize = getBucketSize(setup);
const { buckets, total_hits: totalHits } = await getBuckets({
serviceName,
groupId,
bucketSize,
setup
});
return {
total_hits: totalHits,
buckets,
bucket_size: bucketSize
};
}

View file

@ -0,0 +1,46 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { Setup } from '../../helpers/setup_request';
import { getBuckets } from './get_buckets';
function getBucketSize({ start, end, config }: Setup) {
const bucketTargetCount = config.get<number>('xpack.apm.bucketTargetCount');
return Math.floor((end - start) / bucketTargetCount);
}
export interface ErrorDistributionAPIResponse {
totalHits: number;
buckets: Array<{
key: number;
count: number;
}>;
bucketSize: number;
}
export async function getDistribution({
serviceName,
groupId,
setup
}: {
serviceName: string;
groupId: string;
setup: Setup;
}): Promise<ErrorDistributionAPIResponse> {
const bucketSize = getBucketSize(setup);
const { buckets, totalHits } = await getBuckets({
serviceName,
groupId,
bucketSize,
setup
});
return {
totalHits,
buckets,
bucketSize
};
}

View file

@ -4,14 +4,29 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SERVICE_NAME, ERROR_GROUP_ID } from '../../../common/constants';
import { get } from 'lodash';
import { oc } from 'ts-optchain';
import { APMError } from 'x-pack/plugins/apm/typings/es_schemas/Error';
import { ERROR_GROUP_ID, SERVICE_NAME } from '../../../common/constants';
import { Setup } from '../helpers/setup_request';
export async function getErrorGroup({ serviceName, groupId, setup }) {
export interface ErrorGroupAPIResponse {
error?: APMError;
occurrencesCount?: number;
}
export async function getErrorGroup({
serviceName,
groupId,
setup
}: {
serviceName: string;
groupId: string;
setup: Setup;
}): Promise<ErrorGroupAPIResponse> {
const { start, end, esFilterQuery, client, config } = setup;
const params = {
index: config.get('apm_oss.errorIndices'),
index: config.get<string>('apm_oss.errorIndices'),
body: {
size: 1,
query: {
@ -43,11 +58,10 @@ export async function getErrorGroup({ serviceName, groupId, setup }) {
params.body.query.bool.filter.push(esFilterQuery);
}
const resp = await client('search', params);
const resp = await client<APMError>('search', params);
return {
error: get(resp, 'hits.hits[0]._source', {}),
occurrences_count: get(resp, 'hits.total'),
group_id: get(resp, `hits.hits[0]._source.${ERROR_GROUP_ID}`)
error: oc(resp).hits.hits[0]._source(),
occurrencesCount: oc(resp).hits.total()
};
}

View file

@ -4,27 +4,45 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { oc } from 'ts-optchain';
import { APMError } from 'x-pack/plugins/apm/typings/es_schemas/Error';
import {
SERVICE_NAME,
ERROR_GROUP_ID,
ERROR_CULPRIT,
ERROR_EXC_MESSAGE,
ERROR_LOG_MESSAGE,
ERROR_EXC_HANDLED,
PROCESSOR_EVENT
ERROR_EXC_MESSAGE,
ERROR_GROUP_ID,
ERROR_LOG_MESSAGE,
PROCESSOR_EVENT,
SERVICE_NAME
} from '../../../common/constants';
import { get } from 'lodash';
import { Setup } from '../helpers/setup_request';
interface ErrorResponseItems {
message?: string;
occurrenceCount: number;
culprit?: string;
groupId?: string;
latestOccurrenceAt: string;
handled?: boolean;
}
export type ErrorGroupListAPIResponse = ErrorResponseItems[];
export async function getErrorGroups({
serviceName,
sortField,
sortDirection = 'desc',
setup
}) {
}: {
serviceName: string;
sortField: string;
sortDirection: 'desc' | 'asc';
setup: Setup;
}): Promise<ErrorGroupListAPIResponse> {
const { start, end, esFilterQuery, client, config } = setup;
const params = {
index: config.get('apm_oss.errorIndices'),
const params: any = {
index: config.get<string>('apm_oss.errorIndices'),
body: {
size: 0,
query: {
@ -87,23 +105,58 @@ export async function getErrorGroups({
};
}
const resp = await client('search', params);
const hits = get(resp, 'aggregations.error_groups.buckets', []).map(
bucket => {
interface SampleError {
'@timestamp': APMError['@timestamp'];
error: {
log?: {
message: string;
};
exception?: {
handled?: boolean;
message?: string;
};
culprit: APMError['error']['culprit'];
grouping_key: APMError['error']['grouping_key'];
};
}
interface Bucket {
key: string;
doc_count: number;
sample: {
hits: {
total: number;
max_score: number | null;
hits: Array<{
_source: SampleError;
}>;
};
};
}
interface Aggs {
error_groups: {
buckets: Bucket[];
};
}
const resp = await client<void, Aggs>('search', params);
const hits = oc(resp)
.aggregations.error_groups.buckets([])
.map(bucket => {
const source = bucket.sample.hits.hits[0]._source;
const message =
get(source, ERROR_LOG_MESSAGE) || get(source, ERROR_EXC_MESSAGE);
oc(source).error.log.message() || oc(source).error.exception.message();
return {
message,
occurrenceCount: bucket.doc_count,
culprit: get(source, ERROR_CULPRIT),
groupId: get(source, ERROR_GROUP_ID),
culprit: oc(source).error.culprit(),
groupId: oc(source).error.grouping_key(),
latestOccurrenceAt: source['@timestamp'],
handled: get(source, ERROR_EXC_HANDLED)
handled: oc(source).error.exception.handled()
};
}
);
});
return hits;
}

View file

@ -5,7 +5,7 @@
*/
import { oc } from 'ts-optchain';
import { TermsAggsBucket } from 'x-pack/plugins/apm/typings/elasticsearch';
import { BucketAgg } from 'x-pack/plugins/apm/typings/elasticsearch';
import {
SERVICE_AGENT_NAME,
SERVICE_NAME,
@ -65,10 +65,10 @@ export async function getService(
interface Aggs {
types: {
buckets: TermsAggsBucket[];
buckets: BucketAgg[];
};
agents: {
buckets: TermsAggsBucket[];
buckets: BucketAgg[];
};
}

View file

@ -5,7 +5,7 @@
*/
import { oc } from 'ts-optchain';
import { TermsAggsBucket } from 'x-pack/plugins/apm/typings/elasticsearch';
import { BucketAgg } from 'x-pack/plugins/apm/typings/elasticsearch';
import {
PROCESSOR_EVENT,
SERVICE_AGENT_NAME,
@ -85,19 +85,19 @@ export async function getServices(
params.body.query.bool.filter.push(esFilterQuery);
}
interface ServiceBucket extends TermsAggsBucket {
interface ServiceBucket extends BucketAgg {
avg: {
value: number;
};
agents: {
buckets: TermsAggsBucket[];
buckets: BucketAgg[];
};
events: {
buckets: TermsAggsBucket[];
buckets: BucketAgg[];
};
}
interface Aggs extends TermsAggsBucket {
interface Aggs extends BucketAgg {
services: {
buckets: ServiceBucket[];
};

View file

@ -6,8 +6,8 @@
import { SearchParams } from 'elasticsearch';
import { TRACE_ID } from '../../../common/constants';
import { Span } from '../../../typings/Span';
import { Transaction } from '../../../typings/Transaction';
import { Span } from '../../../typings/es_schemas/Span';
import { Transaction } from '../../../typings/es_schemas/Transaction';
import { Setup } from '../helpers/setup_request';
export type TraceAPIResponse = Array<Transaction | Span>;

View file

@ -10,7 +10,7 @@ import {
TRANSACTION_DURATION,
TRANSACTION_NAME
} from '../../../common/constants';
import { Transaction } from '../../../typings/Transaction';
import { Transaction } from '../../../typings/es_schemas/Transaction';
import { Setup } from '../helpers/setup_request';
interface Bucket {

View file

@ -6,7 +6,7 @@
import moment from 'moment';
import { oc } from 'ts-optchain';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import { ESResponse } from './fetcher';
export interface ITransactionGroup {

View file

@ -15,7 +15,7 @@ import {
TRANSACTION_TYPE
} from 'x-pack/plugins/apm/common/constants';
import { Setup } from 'x-pack/plugins/apm/server/lib/helpers/setup_request';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
interface Bucket {
key: number;

View file

@ -6,7 +6,7 @@
import { SearchParams } from 'elasticsearch';
import { oc } from 'ts-optchain';
import { Transaction } from 'x-pack/plugins/apm/typings/Transaction';
import { Transaction } from 'x-pack/plugins/apm/typings/es_schemas/Transaction';
import {
PROCESSOR_EVENT,
TRACE_ID,

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Span } from 'x-pack/plugins/apm/typings/Span';
import { Span } from 'x-pack/plugins/apm/typings/es_schemas/Span';
import {
PROCESSOR_EVENT,
SPAN_START,

View file

@ -1,46 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { APMDocV1, ContextService, Stackframe } from './APMDoc';
export interface Error extends APMDocV1 {
processor: {
name: 'error';
event: 'error';
};
context: {
process?: {
pid: number;
};
service: ContextService;
};
transaction?: {
id: string; // transaction ID is not required in v1
};
error: {
id?: string; // ID is not required in v1
timestamp: string;
culprit: string;
grouping_key: string;
// either exception or log are given
exception?: {
message?: string; // either message or type are given
type?: string;
code?: string;
module?: string;
attributes?: unknown;
handled?: boolean;
stacktrace?: Stackframe[];
};
log?: {
message: string;
param_message?: string;
logger_name?: string;
level?: string;
stacktrace?: Stackframe[];
};
};
}

View file

@ -4,19 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SearchResponse } from 'elasticsearch';
export interface TermsAggsBucket {
key: string;
export interface BucketAgg<T = string> {
key: T;
doc_count: number;
}
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
export type TopHits<T> = Omit<
SearchResponse<T>,
'took' | 'timed_out' | '_shards'
>;
declare module 'elasticsearch' {
// extending SearchResponse to be able to have typed aggregations
export interface AggregationSearchResponse<T, U = void>

View file

@ -0,0 +1,84 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { APMDocV1, APMDocV2, ContextService, Stackframe } from './APMDoc';
interface Agent {
hostname: string;
type: string;
version: string;
}
interface Processor {
name: 'error';
event: 'error';
}
interface Context {
process?: {
pid: number;
};
service: ContextService;
}
interface Exception {
message?: string; // either message or type are given
type?: string;
code?: string;
module?: string;
attributes?: unknown;
handled?: boolean;
stacktrace?: Stackframe[];
}
interface Log {
message: string;
param_message?: string;
logger_name?: string;
level?: string;
stacktrace?: Stackframe[];
}
interface ErrorV1 extends APMDocV1 {
version: 'v1';
agent: Agent;
processor: Processor;
context: Context;
transaction?: {
id: string; // transaction ID is not required in v1
};
error: {
id?: string; // ID is not required in v1
timestamp: string;
culprit: string;
grouping_key: string;
// either exception or log are given
exception?: Exception;
log?: Log;
};
}
interface ErrorV2 extends APMDocV2 {
version: 'v2';
agent: Agent;
processor: Processor;
context: Context;
transaction: {
id: string; // transaction ID is required in v2
};
error: {
id: string; // ID is required in v2
timestamp: string;
culprit: string;
grouping_key: string;
// either exception or log are given
exception?: Exception;
log?: Log;
};
}
// Not calling it "Error" to avoid clashes with types for native Error
export type APMError = ErrorV1 | ErrorV2;