mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-11 23:01:27 +08:00
235 lines
7.1 KiB
TypeScript
235 lines
7.1 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import GenericVisualizations from "../Metrics/GenericVisualization";
|
|
import { Select, Card, Space, Form } from "antd";
|
|
import { connect } from "react-redux";
|
|
import { StoreState } from "../../store/reducers";
|
|
import { GlobalTime, TraceFilters } from "../../store/actions";
|
|
import { useRoute } from "../RouteProvider";
|
|
import { getFilteredTraceMetrics } from "../../store/actions/MetricsActions";
|
|
import { customMetricsItem } from "../../store/actions/MetricsActions";
|
|
const { Option } = Select;
|
|
|
|
const entity = [
|
|
{
|
|
title: "Calls",
|
|
key: "calls",
|
|
dataindex: "calls",
|
|
},
|
|
{
|
|
title: "Duration",
|
|
key: "duration",
|
|
dataindex: "duration",
|
|
},
|
|
{
|
|
title: "Error",
|
|
key: "error",
|
|
dataindex: "error",
|
|
},
|
|
{
|
|
title: "Status Code",
|
|
key: "status_code",
|
|
dataindex: "status_code",
|
|
},
|
|
];
|
|
|
|
const aggregation_options = [
|
|
{
|
|
linked_entity: "calls",
|
|
default_selected: { title: "Count", dataindex: "count" },
|
|
options_available: [
|
|
{ title: "Count", dataindex: "count" },
|
|
{ title: "Rate (per sec)", dataindex: "rate_per_sec" },
|
|
],
|
|
},
|
|
{
|
|
linked_entity: "duration",
|
|
default_selected: { title: "p99", dataindex: "p99" },
|
|
// options_available: [ {title:'Avg', dataindex:'avg'}, {title:'Max', dataindex:'max'},{title:'Min', dataindex:'min'}, {title:'p50', dataindex:'p50'},{title:'p95', dataindex:'p95'}, {title:'p95', dataindex:'p95'}]
|
|
options_available: [
|
|
{ title: "p50", dataindex: "p50" },
|
|
{ title: "p95", dataindex: "p95" },
|
|
{ title: "p99", dataindex: "p99" },
|
|
],
|
|
},
|
|
{
|
|
linked_entity: "error",
|
|
default_selected: { title: "Count", dataindex: "count" },
|
|
options_available: [
|
|
{ title: "Count", dataindex: "count" },
|
|
{ title: "Rate (per sec)", dataindex: "rate_per_sec" },
|
|
],
|
|
},
|
|
{
|
|
linked_entity: "status_code",
|
|
default_selected: { title: "Count", dataindex: "count" },
|
|
options_available: [{ title: "Count", dataindex: "count" }],
|
|
},
|
|
];
|
|
|
|
interface TraceCustomVisualizationsProps {
|
|
filteredTraceMetrics: customMetricsItem[];
|
|
globalTime: GlobalTime;
|
|
getFilteredTraceMetrics: Function;
|
|
traceFilters: TraceFilters;
|
|
}
|
|
|
|
const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => {
|
|
const [selectedEntity, setSelectedEntity] = useState("calls");
|
|
const [selectedAggOption, setSelectedAggOption] = useState("count");
|
|
const { state } = useRoute();
|
|
const [form] = Form.useForm();
|
|
const selectedStep = "60";
|
|
|
|
// Step should be multiples of 60, 60 -> 1 min
|
|
|
|
useEffect(() => {
|
|
let request_string =
|
|
"service=" +
|
|
props.traceFilters.service +
|
|
"&operation=" +
|
|
props.traceFilters.operation +
|
|
"&maxDuration=" +
|
|
props.traceFilters.latency?.max +
|
|
"&minDuration=" +
|
|
props.traceFilters.latency?.min;
|
|
if (props.traceFilters.tags)
|
|
request_string =
|
|
request_string +
|
|
"&tags=" +
|
|
encodeURIComponent(JSON.stringify(props.traceFilters.tags));
|
|
if (selectedEntity)
|
|
request_string =
|
|
request_string + "&dimension=" + selectedEntity.toLowerCase();
|
|
if (selectedAggOption)
|
|
request_string =
|
|
request_string + "&aggregation_option=" + selectedAggOption.toLowerCase();
|
|
if (selectedStep) request_string = request_string + "&step=" + selectedStep;
|
|
const plusMinus15 = {
|
|
minTime: props.globalTime.minTime - 15 * 60 * 1000000000,
|
|
maxTime: props.globalTime.maxTime + 15 * 60 * 1000000000,
|
|
};
|
|
|
|
/*
|
|
Call the apis only when the route is loaded.
|
|
Check this issue: https://github.com/SigNoz/signoz/issues/110
|
|
*/
|
|
if (state.TRACES.isLoaded) {
|
|
props.getFilteredTraceMetrics(request_string, plusMinus15);
|
|
}
|
|
}, [selectedEntity, selectedAggOption, props.traceFilters, props.globalTime]);
|
|
|
|
//Custom metrics API called if time, tracefilters, selected entity or agg option changes
|
|
|
|
// PNOTE - Can also use 'coordinate' option in antd Select for implementing this - https://ant.design/components/select/
|
|
const handleFormValuesChange = (changedValues: any) => {
|
|
const formFieldName = Object.keys(changedValues)[0];
|
|
if (formFieldName === "entity") {
|
|
const temp_entity = aggregation_options.filter(
|
|
(item) => item.linked_entity === changedValues[formFieldName],
|
|
)[0];
|
|
|
|
form.setFieldsValue({
|
|
agg_options: temp_entity.default_selected.title,
|
|
// PNOTE - TO DO Check if this has the same behaviour as selecting an option?
|
|
});
|
|
|
|
let temp = form.getFieldsValue(["agg_options", "entity"]);
|
|
|
|
setSelectedEntity(temp.entity);
|
|
setSelectedAggOption(temp.agg_options);
|
|
//form.setFieldsValue({ agg_options: aggregation_options.filter( item => item.linked_entity === selectedEntity )[0] }); //reset product selection
|
|
// PNOTE - https://stackoverflow.com/questions/64377293/update-select-option-list-based-on-other-select-field-selection-ant-design
|
|
}
|
|
|
|
if (formFieldName === "agg_options") {
|
|
setSelectedAggOption(changedValues[formFieldName]);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Card>
|
|
<div>Custom Visualizations</div>
|
|
<Form
|
|
form={form}
|
|
onValuesChange={handleFormValuesChange}
|
|
initialValues={{
|
|
agg_options: "Count",
|
|
chart_style: "line",
|
|
interval: "5m",
|
|
group_by: "none",
|
|
}}
|
|
>
|
|
<Space>
|
|
<Form.Item name="entity">
|
|
<Select defaultValue={selectedEntity} style={{ width: 120 }} allowClear>
|
|
{entity.map((item) => (
|
|
<Option key={item.key} value={item.dataindex}>
|
|
{item.title}
|
|
</Option>
|
|
))}
|
|
</Select>
|
|
</Form.Item>
|
|
|
|
<Form.Item name="agg_options">
|
|
<Select style={{ width: 120 }} allowClear>
|
|
{aggregation_options
|
|
.filter((item) => item.linked_entity === selectedEntity)[0]
|
|
.options_available.map((item) => (
|
|
<Option key={item.dataindex} value={item.dataindex}>
|
|
{item.title}
|
|
</Option>
|
|
))}
|
|
</Select>
|
|
</Form.Item>
|
|
|
|
<Form.Item name="chart_style">
|
|
<Select style={{ width: 120 }} allowClear>
|
|
<Option value="line">Line Chart</Option>
|
|
<Option value="bar">Bar Chart</Option>
|
|
<Option value="area">Area Chart</Option>
|
|
</Select>
|
|
</Form.Item>
|
|
|
|
<Form.Item name="interval">
|
|
<Select style={{ width: 120 }} allowClear>
|
|
<Option value="1m">1 min</Option>
|
|
<Option value="5m">5 min</Option>
|
|
<Option value="30m">30 min</Option>
|
|
</Select>
|
|
</Form.Item>
|
|
|
|
{/* Need heading for each option */}
|
|
<Form.Item name="group_by">
|
|
<Select style={{ width: 120 }} allowClear>
|
|
<Option value="none">Group By</Option>
|
|
<Option value="status">Status Code</Option>
|
|
<Option value="protocol">Protocol</Option>
|
|
</Select>
|
|
</Form.Item>
|
|
</Space>
|
|
</Form>
|
|
|
|
<GenericVisualizations chartType="line" data={props.filteredTraceMetrics} />
|
|
{/* This component should take bar or line as an input */}
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
const mapStateToProps = (
|
|
state: StoreState,
|
|
): {
|
|
filteredTraceMetrics: customMetricsItem[];
|
|
globalTime: GlobalTime;
|
|
traceFilters: TraceFilters;
|
|
} => {
|
|
return {
|
|
filteredTraceMetrics: state.metricsData.customMetricsItem,
|
|
globalTime: state.globalTime,
|
|
traceFilters: state.traceFilters,
|
|
};
|
|
};
|
|
|
|
export const TraceCustomVisualizations = connect(mapStateToProps, {
|
|
getFilteredTraceMetrics: getFilteredTraceMetrics,
|
|
})(_TraceCustomVisualizations);
|