[APM] Transaction breakdown charts#39531
Conversation
26c86e1 to
9bef809
Compare
💔 Build Failed |
ca232ba to
d27fe32
Compare
💔 Build Failed |
💚 Build Succeeded |
1fe4eaf to
85985c0
Compare
85985c0 to
7b6608f
Compare
dgieselaar
left a comment
There was a problem hiding this comment.
I've bailed on refactoring CustomPlot and its sibilings, I ran into some issues w/ react-vis not having typings and it generally just being too much work to get through the process on such short notice. Would definitely like to pick it up some time soon, feel like we can gain a lot by improving our coverage there.
There was a problem hiding this comment.
stroke has to be transparent for a stacked area chart, because we stick a line chart on top of it to achieve the look that we want.
There was a problem hiding this comment.
AFAICT, there's only one way to achieve the look that we want (stroke on just one side of the area), and that is by drawing another line chart on top of it. Seems overkill, but it's the recommendation from the docs:
To create a chart that has a fill, no distinct lines to the left, right or bottom, but a different line style at the top, you may create an area chart with a line chart on top.
There was a problem hiding this comment.
that's unfortunate, but tolerable as long as there's not any noticeable drag to page performance.
There was a problem hiding this comment.
@dgieselaar Doesn't this duplicate the the XAxis, YAxis and HorizontalGridLines? What about just adding an additional LineSeries to the chart?
There was a problem hiding this comment.
Simply syncs time across all charts in its context.
There was a problem hiding this comment.
This sounds similar to
https://github.com/elastic/kibana/blob/7056f6143d7863c2b48ff22ee376402c932bbb60/x-pack/legacy/plugins/apm/public/components/shared/charts/SyncChartGroup/index.tsx
Can those to be combined or do they serve different purposes?
There was a problem hiding this comment.
maybe we can use .json files here too?
There was a problem hiding this comment.
Ensures timeseries that are not in the top 20 KPIs are filtered out.
💔 Build Failed |
7b6608f to
06b9ea7
Compare
|
@dgieselaar A few UI problems I've found in relation to the chart. It seems that the lines are pushed to the left (outside the x-axis area) every now and then. I'm running 15 minutes date range here, not sure how it might be related. Can we add some space / margin on those labels or value text to split them a little apart in this list? |
💚 Build Succeeded |
e5befc2 to
e59ec19
Compare
|
@formgeist both should be fixed now, thanks for catching this! |
e59ec19 to
bd659c0
Compare
💚 Build Succeeded |
There was a problem hiding this comment.
is this spelling of TimeSerie intentional or should it be TimeSeries?
There was a problem hiding this comment.
fixed, changed to TimeSeries
There was a problem hiding this comment.
constants for 'transaction.type' and 'transaction.name'
There was a problem hiding this comment.
changed all of the field names to constants from common/elasticsearch_fieldnames.ts
bd659c0 to
556c3a6
Compare
💚 Build Succeeded |
There was a problem hiding this comment.
In the rest of the code base noHits refers to whether Elasticsearch actually returned any hits. We cannot just look at series.data because we might populate that with empty state data so the graph draws the x and y-axis.
Therefore other chart endpoints return the series.totalHits (coming from ES hits.total):
I think we should do the same here if that makes sense to you?
There was a problem hiding this comment.
It would be more consistent, but I have some doubts about using hits.total to determine whether there is no data. Gut tells me there's a lot of cases where that will be a false positive. Using the actual data passed to the charts to determine if there are any data points by filtering out all non-null values feels like a better heuristic to me. Does that make sense?
There was a problem hiding this comment.
Gut tells me there's a lot of cases where that will be a false positive. Using the actual data passed to the charts to determine if there are any data points by filtering out all non-null values feels like a better heuristic to me. Does that make sense?
That does make sense. My originally assumption was that if there are any hits then the aggregation will contain (timeseries) data, and it's been true for the aggs we do for the transaction charts. But this doesn't hold true for things like derivative aggs which requires at least two data points. So using hits.total as an indicator for data might not be a good idea in general.
If we go the direction you suggest (with a custom check) we should get rid of the noHits, right? Eg:
- https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts#L55
- https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/MetricsChart.tsx#L37
I think it might make sense to go over how we handle this across the different visualizations. It seems like a pretty standard thing for a chart to be able to know whether it has data or not, and would be great if we can stick to one approach.
…ent unnecessary re-renders
56865ed to
714ec19
Compare
714ec19 to
c808e71
Compare
💚 Build Succeeded |
* [APM] Transaction breakdown graphs Closes elastic#36441. * Don't use .keyword now that mapping is correct * Use better heuristic for determining noHits; wrap App in memo to prevent unnecessary re-renders * Remove nested panel around TransactionBreakdown * Fix display issues in charts * Address review feedback * Address additional review feedback
* [APM] Transaction breakdown graphs Closes #36441. * Don't use .keyword now that mapping is correct * Use better heuristic for determining noHits; wrap App in memo to prevent unnecessary re-renders * Remove nested panel around TransactionBreakdown * Fix display issues in charts * Address review feedback * Address additional review feedback
| const xMax = d3.max(flattenedCoordinates, d => d.x); | ||
| const xMin = start ? start : d3.min(flattenedCoordinates, d => d.x); | ||
|
|
||
| const xMax = end ? end : d3.max(flattenedCoordinates, d => d.x); |
There was a problem hiding this comment.
Shouldn't start be called xMin and end be called xMax?
| XY_WIDTH: width | ||
| XY_HEIGHT: height || XY_HEIGHT, | ||
| XY_WIDTH: width, | ||
| ...(stacked ? { stackBy: 'y' } : {}) |
There was a problem hiding this comment.
Does this need to be conditional? Doesn't it only take effect when things are stacked? So setting it to stackBy: 'y' only affects stacked charts.
| } | ||
|
|
||
| export function SharedPlot({ plotValues, ...props }) { | ||
| const { XY_HEIGHT: height, XY_MARGIN: margin, XY_WIDTH: width } = plotValues; |
There was a problem hiding this comment.
I'm generally not a fan of renaming thing like this. If the names are not good enough, why not fix them at the root? Instead we end up with multiple names for the same thing.
| () => { | ||
| return timeseries.map(timeseriesConfig => { | ||
| return { | ||
| title: timeseriesConfig.name, |
There was a problem hiding this comment.
Why is this rename necessary? Shouldn't timeseriesConfig.name be timeseriesConfig.title in the first place? Same with timeseriesConfig.values - shouldn't that be timeseriesConfig.data in the first place?
I actually prefer values over data but then we should change that everywhere.
| } | ||
| }); | ||
|
|
||
| const { timeseries_per_subtype: timeseriesPerSubtype } = response; |
There was a problem hiding this comment.
Why are we using snake case in the first place? All APM endpoints return camel cased data currently.
There was a problem hiding this comment.
|
|
||
| const breakdown = kpis.find( | ||
| b => b.name === legend.name | ||
| ) as typeof kpis[0]; |
There was a problem hiding this comment.
What if no matches are found? Then breakdown will be undefined and cause a runtime error below - or is that not logically possible?
|
|
||
| const kpis = useMemo( | ||
| const kpis = data ? data.kpis : undefined; | ||
| const timeseriesPerSubtype = data ? data.timeseries_per_subtype : undefined; |
There was a problem hiding this comment.
I see this access checks a lot of places which makes the code a bit harder to read. What about setting a default response like we do in:
Could be something like:
const INITIAL_DATA: TransactionBreakdownAPIResponse = {
kpis: [],
timeseries_per_subtype: null
}| timeseries: Array<{ | ||
| name: string; | ||
| color: string; | ||
| values: Array<{ x: number; y: number | null }>; |
There was a problem hiding this comment.
I think this should be consistent with the other charts we have (name -> title and values -> data).
As mentioned somewhere else I prefer values over data but might be a bigger job to change that everywhere.
Instead of having several different interfaces can we make a single timeseries interface that is re-used?
| title: React.ReactNode; | ||
| titleShort?: React.ReactNode; | ||
| data: Array<Coordinate | RectCoordinate>; | ||
| type: string; |
There was a problem hiding this comment.
Isn't this a duplicate of
kibana/x-pack/legacy/plugins/apm/typings/timeseries.ts
Lines 17 to 27 in 1e1153e
There was a problem hiding this comment.
Or rather: can they be combined?



Closes #36441.
To test this with the apm-integration-testing server, take the following steps:
docker/apm-server/DockerfileENV setup.template.overwrite=truebelowFROM ${apm_server_base_image}on line 23~/dev/apm-integration-testing/scripts/compose.py start master --with-opbeans-java --opbeans-java-agent-branch=pr/588/head --force-build --no-kibanaopbeans-javaDefinition of Done for APM UI