code cleanup, updated in global date time component

This commit is contained in:
Pranay Prateek 2021-01-07 19:59:48 +05:30
parent 72f2ac0fea
commit e1fd9cec65
32 changed files with 98 additions and 636 deletions

View File

@ -19,7 +19,6 @@ export const updateTimeInterval = (interval:string, datetimeRange?:[number,numbe
let maxTime: number = 0; let maxTime: number = 0;
let minTime: number = 0; let minTime: number = 0;
console.log('interval', interval, typeof(interval))
// if interval string is custom, then datetimRange should be present and max & min time should be // if interval string is custom, then datetimRange should be present and max & min time should be
// set directly based on that. Assuming datetimeRange values are in ms, and minTime is 0th element // set directly based on that. Assuming datetimeRange values are in ms, and minTime is 0th element
@ -28,13 +27,11 @@ export const updateTimeInterval = (interval:string, datetimeRange?:[number,numbe
maxTime=Date.now()*1000000; // in nano sec maxTime=Date.now()*1000000; // in nano sec
minTime=(Date.now()-15*60*1000)*1000000; minTime=(Date.now()-15*60*1000)*1000000;
console.log('max time 15 in case',maxTime)
break; break;
case '30min': case '30min':
maxTime=Date.now()*1000000; // in nano sec maxTime=Date.now()*1000000; // in nano sec
minTime=(Date.now()-30*60*1000)*1000000; minTime=(Date.now()-30*60*1000)*1000000;
console.log('max time in 30 min case',maxTime)
break; break;
case '1hr': case '1hr':

View File

@ -62,7 +62,6 @@ export interface getFilteredTraceMetricsAction{
export const getServicesList = (globalTime: GlobalTime) => { export const getServicesList = (globalTime: GlobalTime) => {
return async (dispatch: Dispatch) => { return async (dispatch: Dispatch) => {
let request_string = 'services?start='+globalTime.minTime+'&end='+globalTime.maxTime; let request_string = 'services?start='+globalTime.minTime+'&end='+globalTime.maxTime;
console.log(request_string);
const response = await metricsAPI.get<servicesListItem[]>(request_string); const response = await metricsAPI.get<servicesListItem[]>(request_string);
dispatch<getServicesListAction>({ dispatch<getServicesListAction>({

View File

@ -26,7 +26,6 @@ export interface updateTraceFiltersAction {
} }
export const updateTraceFilters = (traceFilters: TraceFilters) => { export const updateTraceFilters = (traceFilters: TraceFilters) => {
console.log('in update trace filters',traceFilters)
return { return {
type: ActionTypes.updateTraceFilters, type: ActionTypes.updateTraceFilters,
payload: traceFilters, payload: traceFilters,

View File

@ -1,9 +1,9 @@
import axios from 'axios'; import axios from 'axios';
export default axios.create({ export default axios.create({
// baseURL: 'http://104.211.113.204:8080/api/v1/', baseURL: 'http://104.211.113.204:8080/api/v1/',
// baseURL: process.env.REACT_APP_QUERY_SERVICE_URL,
baseURL: 'http://localhost:3000/api/v1/', // baseURL: 'http://localhost:3000/api/v1/',
// console.log('in metrics API', process.env.QUERY_SERVICE_URL)
} }
); );

View File

@ -3,14 +3,9 @@ import axios from 'axios';
export default axios.create({ export default axios.create({
// baseURL: 'http://104.211.113.204:8080/api/v1/' // baseURL: 'http://104.211.113.204:8080/api/v1/'
// baseURL: process.env.QUERY_SERVICE_URL,
// console.log('in traces API', process.env.QUERY_SERVICE_URL) baseURL: 'http://localhost:3000/api/v1/', // toggle to this before pushing
baseURL: 'http://localhost:3000/api/v1/',
}); });
//individual trace format
//https://api.signoz.io/api/traces/146754946da0552e
//http://104.211.113.204:8080/api/v1/traces/000000000000000053a5b7a93bc5e08a

View File

@ -45,7 +45,6 @@ const App = () => {
const onCollapse = (): void => { const onCollapse = (): void => {
// console.log(collapsed);
setCollapsed(!collapsed); setCollapsed(!collapsed);
}; };

View File

@ -1,7 +1,8 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Modal, Col, Row, DatePicker, TimePicker} from 'antd'; import { Modal, DatePicker} from 'antd';
import { Store } from 'antd/lib/form/interface';
import {DateTimeRangeType} from '../actions' import {DateTimeRangeType} from '../actions'
import { Moment } from 'moment'
import moment from 'moment';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
@ -23,14 +24,17 @@ const CustomDateTimeModal: React.FC<CustomDateTimeModalProps> = ({ //destructuri
const [customDateTimeRange, setCustomDateTimeRange]=useState<DateTimeRangeType>(); const [customDateTimeRange, setCustomDateTimeRange]=useState<DateTimeRangeType>();
function handleRangePickerOk(date_time: DateTimeRangeType) { function handleRangePickerOk(date_time: DateTimeRangeType) {
console.log('onhandleRangePickerOk: ', date_time?date_time[0]?.toDate():null,date_time?date_time[1]?.toDate():null );
setCustomDateTimeRange(date_time); setCustomDateTimeRange(date_time);
} }
function disabledDate(current:Moment){
// function handleApplyDateTimeModal (){ if (current > moment()){
// if (customDateTimeRange !== null && customDateTimeRange !== undefined && customDateTimeRange[0] !== null && customDateTimeRange[1] !== null ) return true;
// console.log('in handleApplyDateTimeModal', customDateTimeRange[0].toDate(),customDateTimeRange[1].toDate()) }
// } else {
return false;
}
}
return ( return (
<Modal <Modal
@ -39,27 +43,11 @@ const CustomDateTimeModal: React.FC<CustomDateTimeModalProps> = ({ //destructuri
okText="Apply" okText="Apply"
cancelText="Cancel" cancelText="Cancel"
onCancel={onCancel} onCancel={onCancel}
// style={{ position: "absolute", top: 60, left: parseInt(`${positionleft}`) }} //get position as a prop from parent component, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
style={{ position: "absolute", top: 60, right: 40 }} style={{ position: "absolute", top: 60, right: 40 }}
onOk={() => onCreate(customDateTimeRange?customDateTimeRange:null)} onOk={() => onCreate(customDateTimeRange?customDateTimeRange:null)}
> >
<RangePicker onOk={handleRangePickerOk} showTime /> <RangePicker disabledDate={disabledDate} onOk={handleRangePickerOk} showTime />
{/* <RangePicker renderExtraFooter={() => 'extra footer'} showTime /> */}
{/* <Row>
<Col span={6}> <DatePicker />
</Col>
<Col span={6}>
<TimePicker />
</Col>
<Col span={6}>
<DatePicker />
</Col>
<Col span={6}>
<TimePicker />
</Col>
</Row> */}
</Modal> </Modal>
); );

View File

@ -13,12 +13,6 @@ import FormItem from 'antd/lib/form/FormItem';
import {DateTimeRangeType} from '../actions' import {DateTimeRangeType} from '../actions'
import {
RedoOutlined,
} from '@ant-design/icons';
const { Option } = Select; const { Option } = Select;
@ -30,8 +24,6 @@ interface DateTimeSelectorProps extends RouteComponentProps<any> {
currentpath?:string; currentpath?:string;
updateTimeInterval: Function; updateTimeInterval: Function;
globalTime: GlobalTime; globalTime: GlobalTime;
// urlTime:string;
} }
@ -43,11 +35,9 @@ const _DateTimeSelector = (props:DateTimeSelectorProps) => {
const [form_dtselector] = Form.useForm(); const [form_dtselector] = Form.useForm();
console.log('props in DateTimeSelector',props)
const handleOnSelect = (value:string) => const handleOnSelect = (value:string) =>
{ {
console.log(value);
if (value === 'custom') if (value === 'custom')
{ {
setCustomDTPickerVisible(true); setCustomDTPickerVisible(true);
@ -58,8 +48,6 @@ const _DateTimeSelector = (props:DateTimeSelectorProps) => {
search: '?time='+value, search: '?time='+value,
}) //pass time in URL query param for all choices except custom in datetime picker }) //pass time in URL query param for all choices except custom in datetime picker
props.updateTimeInterval(value); props.updateTimeInterval(value);
// console.log('in handle on select urlTime',props.urlTime)
// console.log('global time', props.globalTime.maxTime,props.globalTime.minTime)
setTimeInterval(value); setTimeInterval(value);
setRefreshButtonHidden(false); // for normal intervals, show refresh button setRefreshButtonHidden(false); // for normal intervals, show refresh button
} }
@ -68,14 +56,13 @@ const _DateTimeSelector = (props:DateTimeSelectorProps) => {
//function called on clicking apply in customDateTimeModal //function called on clicking apply in customDateTimeModal
const handleOk = (dateTimeRange:DateTimeRangeType) => const handleOk = (dateTimeRange:DateTimeRangeType) =>
{ {
console.log('in handleOk of DateTimeSelector',dateTimeRange);
// pass values in ms [minTime, maxTime] // pass values in ms [minTime, maxTime]
if (dateTimeRange!== null && dateTimeRange!== undefined && dateTimeRange[0]!== null && dateTimeRange[1]!== null ) if (dateTimeRange!== null && dateTimeRange!== undefined && dateTimeRange[0]!== null && dateTimeRange[1]!== null )
{ {
props.updateTimeInterval('custom',[dateTimeRange[0].valueOf(),dateTimeRange[1].valueOf()]) props.updateTimeInterval('custom',[dateTimeRange[0].valueOf(),dateTimeRange[1].valueOf()])
//setting globaltime //setting globaltime
setRefreshButtonHidden(true); setRefreshButtonHidden(true);
form_dtselector.setFieldsValue({interval:(dateTimeRange[0].valueOf().toString()+'-'+dateTimeRange[1].valueOf().toString()) ,}) form_dtselector.setFieldsValue({interval:(dateTimeRange[0].format("YYYY/MM/DD HH:mm")+'-'+dateTimeRange[1].format("YYYY/MM/DD HH:mm")) ,})
} }
setCustomDTPickerVisible(false); setCustomDTPickerVisible(false);
} }
@ -97,7 +84,6 @@ const _DateTimeSelector = (props:DateTimeSelectorProps) => {
const handleRefresh = () => const handleRefresh = () =>
{ {
console.log('refreshing time interval', timeInterval)
props.updateTimeInterval(timeInterval); props.updateTimeInterval(timeInterval);
} }
if (props.location.pathname.startsWith('/usage-explorer')) { if (props.location.pathname.startsWith('/usage-explorer')) {
@ -110,14 +96,14 @@ const _DateTimeSelector = (props:DateTimeSelectorProps) => {
<Space> <Space>
<Form form={form_dtselector} layout='inline' initialValues={{ interval:'15min', }} style={{marginTop: 10, marginBottom:10}}> <Form form={form_dtselector} layout='inline' initialValues={{ interval:'15min', }} style={{marginTop: 10, marginBottom:10}}>
<FormItem name='interval'> <FormItem name='interval'>
<Select style={{ width: 300 }} onSelect={handleOnSelect} > <Select onSelect={handleOnSelect} >
<Option value="custom">Custom</Option> <Option value="custom">Custom</Option>
<Option value="15min">15 min</Option> <Option value="15min">Last 15 min</Option>
<Option value="30min">30 min</Option> <Option value="30min">Last 30 min</Option>
<Option value="1hr">1 hour</Option> <Option value="1hr">Last 1 hour</Option>
<Option value="6hr">6 hour</Option> <Option value="6hr">Last 6 hour</Option>
<Option value="1day">1 day</Option> <Option value="1day">Last 1 day</Option>
<Option value="1week">1 week</Option> <Option value="1week">Last 1 week</Option>
</Select> </Select>
</FormItem> </FormItem>
@ -140,14 +126,11 @@ const _DateTimeSelector = (props:DateTimeSelectorProps) => {
} }
} }
//const mapStateToProps = (state: StoreState, ownProps: RouteComponentProps<any> -- ownProps is of type RouteComponentProps<any>
const mapStateToProps = (state: StoreState ): { globalTime: GlobalTime} => { const mapStateToProps = (state: StoreState ): { globalTime: GlobalTime} => {
return { globalTime : state.globalTime }; return { globalTime : state.globalTime };
// Check if we should pass as urltime or how do we handle this
}; };
// the name mapStateToProps is only a convention
// take state and map it to props which are accessible inside this component
export const DateTimeSelector = connect(mapStateToProps, { export const DateTimeSelector = connect(mapStateToProps, {
updateTimeInterval: updateTimeInterval, updateTimeInterval: updateTimeInterval,

View File

@ -14,9 +14,6 @@ const breadcrumbNameMap :any = { // PNOTE - TO DO - Remove any and do typecheck
'/traces': 'Traces', '/traces': 'Traces',
'/service-map': 'Service Map', '/service-map': 'Service Map',
'/usage-explorer': 'Usage Explorer', '/usage-explorer': 'Usage Explorer',
// only top level things should be mapped here, rest should be taken dynamically from url
// Does this work if url has 2 levels of dynamic parameters? - Currently we have only 1 level
// this structure ignores query params like time -- which is good
}; };
@ -57,21 +54,4 @@ const ShowBreadcrumbs = withRouter(props => {
); );
}); });
// const ShowBreadcrumbs = () => {
// return (
// <Breadcrumb style={{ margin: '16px 0' , fontSize: 12 }}>
// <Breadcrumb.Item>
// <Link to="/">Home</Link></Breadcrumb.Item>
// <Breadcrumb.Item><Link to="/application">Application</Link></Breadcrumb.Item>
// </Breadcrumb>
// //programmatically populate it with links
// );
// }
export default ShowBreadcrumbs; export default ShowBreadcrumbs;

View File

@ -14,7 +14,7 @@ zIndex:10;
position:absolute; position:absolute;
top:${props => props.ycoordinate}px; top:${props => props.ycoordinate}px;
left:${props => props.xcoordinate}px; left:${props => props.xcoordinate}px;
font-size:10px; font-size:12px;
border-radius:2px; border-radius:2px;
`; `;
@ -32,8 +32,6 @@ padding-right:4px;
// PNOTE - Check if this should be the case // PNOTE - Check if this should be the case
const theme = 'dark'; const theme = 'dark';
interface ErrorRateChartProps extends RouteComponentProps<any> { interface ErrorRateChartProps extends RouteComponentProps<any> {
data : metricItem[], data : metricItem[],
} }
@ -47,7 +45,6 @@ class ErrorRateChart extends React.Component<ErrorRateChartProps>{
constructor(props: ErrorRateChartProps) { constructor(props: ErrorRateChartProps) {
super(props); super(props);
console.log('React CreatRef', React.createRef());
this.chartRef = React.createRef(); this.chartRef = React.createRef();
} }
@ -64,25 +61,11 @@ class ErrorRateChart extends React.Component<ErrorRateChartProps>{
onClickhandler = async(e:any,event:any) => { onClickhandler = async(e:any,event:any) => {
//PNOTE - e has all key values from mouse event, event has only reference to handler functions
// PNOTE - https://github.com/emn178/angular2-chartjs/issues/29 - for listening only to element points
// var firstPoin = this.chart.current.getElementAtEvent(e)
console.log('chartref',this.chartRef.current.chartInstance);
var firstPoint; var firstPoint;
if(this.chartRef){ if(this.chartRef){
firstPoint = this.chartRef.current.chartInstance.getElementAtEvent(e)[0]; firstPoint = this.chartRef.current.chartInstance.getElementAtEvent(e)[0];
} }
console.log('firstPoint', firstPoint);
// if (firstPoint) {
// var label = myChart.data.labels[firstPoint._index];
// var value = myChart.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
// }
if (firstPoint) if (firstPoint)
{// PNOTE - TODO - Is await needed in this expression? {// PNOTE - TODO - Is await needed in this expression?
await this.setState({ await this.setState({
@ -92,19 +75,13 @@ class ErrorRateChart extends React.Component<ErrorRateChartProps>{
// graphInfo:{...event} // graphInfo:{...event}
}) })
} }
// console.log(this.state.graphInfo.payload.timestamp)
//this.props.applicationTimeStamp(e.payload.x)
// this.props.history.push('/traces?timestamp=' + e.payload.timestamp + '&service=' + this.props.service.name)
} }
gotoTracesHandler=()=>{ gotoTracesHandler=()=>{
console.log('in gotoTraces handler')
this.props.history.push('/traces') this.props.history.push('/traces')
// this.props.history.push('/traces?timestamp=' + this.state.graphInfo.payload.timestamp + '&service=' + this.props.service.name)
} }
gotoAlertsHandler=()=>{ gotoAlertsHandler=()=>{
console.log('in gotoAlerts handler')
this.props.history.push('/service-map') this.props.history.push('/service-map')
// PNOTE - Keeping service map for now, will replace with alerts when alert page is made // PNOTE - Keeping service map for now, will replace with alerts when alert page is made
} }
@ -147,25 +124,20 @@ class ErrorRateChart extends React.Component<ErrorRateChartProps>{
tooltips: { tooltips: {
mode: 'label', mode: 'label',
bodyFontSize: 10, bodyFontSize: 12,
titleFontSize: 10, titleFontSize: 12,
callbacks: { callbacks: {
label: function(tooltipItem, data) { label: function(tooltipItem, data) {
if (typeof(tooltipItem.yLabel) === 'number') if (typeof(tooltipItem.yLabel) === 'number')
{ {
return data.datasets![tooltipItem.datasetIndex!].label +' : '+ tooltipItem.yLabel.toFixed(3); return data.datasets![tooltipItem.datasetIndex!].label +' : '+ tooltipItem.yLabel.toFixed(2);
} }
else else
{ {
return ''; return '';
} }
// return data.datasets![tooltipItem.datasetIndex!].label +' : '+ tooltipItem.yLabel!.Fixed(3);
// not able to do toFixed(3) in typescript as string|number type is not working with toFixed(3) function for
// as toFixed() function only works with numbers
// using type of check gives issues in 'label' variable name
//!That's the non-null assertion operator. It is a way to tell the compiler "this expression cannot be null or undefined here, so don't complain about the possibility of it being null or undefined." Sometimes the type checker is unable to make that determination itself.
}, },
}, },
@ -214,26 +186,15 @@ class ErrorRateChart extends React.Component<ErrorRateChartProps>{
} }
GraphTracePopUp = () => { GraphTracePopUp = () => {
console.log('state in GraphTracePopPup',this.state);
if (this.state.showpopUp){ if (this.state.showpopUp){
return( return(
// <div className='applicationpopup' style={{top:`${this.state.ycoordinate}px`,zIndex:10,position:'absolute',left:`${this.state.xcoordinate}px`,backgroundColor:'white',border:'1px solid grey'}}>
// <p style={{color:'black'}} onClick={this.gotoTracesHandler}>View Traces</p>
// <p style={{color:'black'}}> Set Alerts</p>
// </div>
// <ChartPopUpUnique>
// <p style={{color:'black'}} onClick={this.gotoTracesHandler}>View Traces</p>
// <p style={{color:'black'}}> Set Alerts</p>
// </ChartPopUpUnique>
<ChartPopUpUnique xcoordinate={this.state.xcoordinate} ycoordinate={this.state.ycoordinate}> <ChartPopUpUnique xcoordinate={this.state.xcoordinate} ycoordinate={this.state.ycoordinate}>
<PopUpElements onClick={this.gotoTracesHandler}>View Traces</PopUpElements> <PopUpElements onClick={this.gotoTracesHandler}>View Traces</PopUpElements>
<PopUpElements onClick={this.gotoAlertsHandler}>Set Alerts</PopUpElements> <PopUpElements onClick={this.gotoAlertsHandler}>Set Alerts</PopUpElements>
</ChartPopUpUnique> </ChartPopUpUnique>
) )
} }
else else
@ -245,18 +206,6 @@ class ErrorRateChart extends React.Component<ErrorRateChartProps>{
render(){ render(){
const ndata = this.props.data; const ndata = this.props.data;
// console.log("in chartJS line render function")
// console.log(ndata);
// const data_charts = data.map( s => ({label:s.ts, value:parseFloat(s.val)}) );
// if(this.chartRef.ctx)
// {
// var gradient = this.chartRef.ctx.createLinearGradient(0, 0, 0, 400);
// gradient.addColorStop(0, 'rgba(250,174,50,1)');
// gradient.addColorStop(1, 'rgba(250,174,50,0)');
// }
const data_chartJS = (canvas:any) => { const data_chartJS = (canvas:any) => {
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
@ -267,9 +216,6 @@ class ErrorRateChart extends React.Component<ErrorRateChartProps>{
datasets: [{ datasets: [{
label: 'Errors per sec', label: 'Errors per sec',
data: ndata.map(s => s.errorRate), data: ndata.map(s => s.errorRate),
// backgroundColor:'#000000',
// fill: true,
// backgroundColor: gradient,
pointRadius: 0.5, pointRadius: 0.5,
borderColor: 'rgba(227, 74, 51,1)', // Can also add transparency in border color borderColor: 'rgba(227, 74, 51,1)', // Can also add transparency in border color
borderWidth: 2, borderWidth: 2,

View File

@ -14,38 +14,14 @@ interface GenericVisualizationsProps {
} }
const GenericVisualizations = (props: GenericVisualizationsProps) => { const GenericVisualizations = (props: GenericVisualizationsProps) => {
// const [serviceName, setServiceName] = useState('Frontend'); //default value of service name
const data = { const data = {
labels: props.data.map(s => new Date(s.timestamp/1000000)), labels: props.data.map(s => new Date(s.timestamp/1000000)),
datasets: [{ datasets: [{
data: props.data.map(s => s.value), data: props.data.map(s => s.value),
// label: "Africa",
borderColor: 'rgba(250,174,50,1)',// for line chart borderColor: 'rgba(250,174,50,1)',// for line chart
backgroundColor: props.chartType==='bar'?'rgba(250,174,50,1)':'', // for bar chart, don't assign backgroundcolor if its not a bar chart, may be relevant for area graph though backgroundColor: props.chartType==='bar'?'rgba(250,174,50,1)':'', // for bar chart, don't assign backgroundcolor if its not a bar chart, may be relevant for area graph though
}, },
// {
// data: [282,350,411,502,635,809,947,1402,3700,5267,282,350,411,502,635,809,947,1402,3700,5267],
// label: "Asia",
// borderColor: 'rgba(227, 74, 51, 1.0)',
// backgroundColor: props.chartType==='bar'?'rgba(227, 74, 51, 1.0)':'',
// },
//{
// data: [168,170,178,190,203,276,408,547,675,734],
// label: "Europe",
// borderColor: "#3cba9f",
// fill: false
// }, {
// data: [40,20,10,16,24,38,74,167,508,784],
// label: "Latin America",
// borderColor: "#e8c3b9",
// fill: false
// }, {
// data: [6,3,2,2,7,26,82,172,312,433],
// label: "North America",
// borderColor: "#c45850",
// fill: false
// }
] ]
}; };
@ -66,10 +42,6 @@ const GenericVisualizations = (props: GenericVisualizationsProps) => {
}], }],
xAxes: [{ xAxes: [{
type: 'time', type: 'time',
// time: {
// unit: 'second'
// },
//PNOTE - How to enable distribution == linear?
// distribution: 'linear', // distribution: 'linear',
//'linear': data are spread according to their time (distances can vary) //'linear': data are spread according to their time (distances can vary)
// From https://www.chartjs.org/docs/latest/axes/cartesian/time.html // From https://www.chartjs.org/docs/latest/axes/cartesian/time.html

View File

@ -14,7 +14,7 @@ zIndex:10;
position:absolute; position:absolute;
top:${props => props.ycoordinate}px; top:${props => props.ycoordinate}px;
left:${props => props.xcoordinate}px; left:${props => props.xcoordinate}px;
font-size:10px; font-size:12px;
border-radius:2px; border-radius:2px;
`; `;
@ -29,40 +29,12 @@ padding-right:4px;
`; `;
// const data_charts = { const theme = 'dark';
// labels: ['1', '2', '3', '4', '5', '6'],
// datasets: [
// {
// // label: '# of Votes',
// data: [12, 19, 3, 5, 2, 3],
// fill: false,
// backgroundColor: 'rgb(255, 99, 132)',
// borderColor: 'rgba(255, 99, 132, 0.2)',
// },
// ],
// }
// const onClickHandler = (e) => {
// console.log("ChartJS chart clicked");
// console.log(e);
// };
// export interface ApiData {
// 0: number, // has epoch timestamp
// 1: string, // has value of the metric as a string
// }
const theme = 'dark';
// PNOTE - accessing history object in typescript - https://stackoverflow.com/questions/49342390/typescript-how-to-add-type-check-for-history-object-in-react
// withRouter is used to pass on history object as prop - https://stackoverflow.com/questions/43107912/how-to-access-history-object-in-new-react-router-v4
interface LatencyLineChartProps extends RouteComponentProps<any> { interface LatencyLineChartProps extends RouteComponentProps<any> {
data : metricItem[], data : metricItem[],
// chartRef: any, popupClickHandler: Function,
// chartReference :ChartJSLineChart,
// data passed to ChartJSLineChart component is an array if json objects
} }
interface LatencyLineChart { interface LatencyLineChart {
@ -74,86 +46,60 @@ class LatencyLineChart extends React.Component<LatencyLineChartProps>{
constructor(props: LatencyLineChartProps) { constructor(props: LatencyLineChartProps) {
super(props); super(props);
console.log('React CreatRef', React.createRef());
this.chartRef = React.createRef(); this.chartRef = React.createRef();
} }
state = { state = {
// data: props.data,
xcoordinate:0, xcoordinate:0,
ycoordinate:0, ycoordinate:0,
showpopUp:false, showpopUp:false,
firstpoint_ts:0,
// graphInfo:{} // graphInfo:{}
} }
onClickhandler = async(e:any,event:any) => { onClickhandler = async(e:any,event:any) => {
console.log('e graph', e)
// console.log('event graph',event)
//PNOTE - e has all key values from mouse event, event has only reference to handler functions
// PNOTE - https://github.com/emn178/angular2-chartjs/issues/29 - for listening only to element points
// var firstPoin = this.chart.current.getElementAtEvent(e)
console.log('chartref',this.chartRef.current.chartInstance);
var firstPoint; var firstPoint;
if(this.chartRef){ if(this.chartRef){
firstPoint = this.chartRef.current.chartInstance.getElementAtEvent(e)[0]; firstPoint = this.chartRef.current.chartInstance.getElementAtEvent(e)[0];
} }
console.log('firstPoint', firstPoint);
// if (firstPoint) {
// var label = myChart.data.labels[firstPoint._index];
// var value = myChart.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
// }
if (firstPoint) if (firstPoint)
{// PNOTE - TODO - Is await needed in this expression? {
await this.setState({ this.setState({
xcoordinate:e.offsetX+20, xcoordinate:e.offsetX+20,
ycoordinate:e.offsetY, ycoordinate:e.offsetY,
showpopUp:true, showpopUp:true,
firstpoint_ts:this.props.data[firstPoint._index].timestamp,
// graphInfo:{...event} // graphInfo:{...event}
}) })
} }
// console.log(this.state.graphInfo.payload.timestamp) else
//this.props.applicationTimeStamp(e.payload.x) {
// this.props.history.push('/traces?timestamp=' + e.payload.timestamp + '&service=' + this.props.service.name) // if clicked outside of the graph line, then firstpoint is undefined -> close popup.
// Only works for clicking in the same chart - as click handler only detects clicks in that chart
this.setState({
showpopUp:false,
})
}
} }
gotoTracesHandler=()=>{ gotoTracesHandler=(xc:any)=>{
console.log('in gotoTraces handler')
this.props.history.push('/traces') this.props.history.push('/traces')
// this.props.history.push('/traces?timestamp=' + this.state.graphInfo.payload.timestamp + '&service=' + this.props.service.name)
} }
gotoAlertsHandler=()=>{ gotoAlertsHandler=()=>{
console.log('in gotoAlerts handler')
this.props.history.push('/service-map') this.props.history.push('/service-map')
// PNOTE - Keeping service map for now, will replace with alerts when alert page is made // PNOTE - Keeping service map for now, will replace with alerts when alert page is made
} }
options_charts: ChartOptions = { options_charts: ChartOptions = {
// onClick: function(evt, element) {
// // console.log(evt);
// },
//- PNOTE - TO DO -- element is of type ChartElement, how to define its type
// https://gitlab.com/signoz-frontend/sample-project/-/blob/darkthemechanges/src/Components/Application/Graphs/SimpleLineChart.js
// Code for popup
// onClick: function(evt, element :any[]) {
// if (element.length > 0) {
// var ind = element[0]._index;
// console.log(element)
// alert(ind);
// }
// },
onClick: this.onClickhandler, onClick: this.onClickhandler,
maintainAspectRatio: true, maintainAspectRatio: true,
@ -168,8 +114,6 @@ class LatencyLineChart extends React.Component<LatencyLineChartProps>{
fontFamily: 'Arial', fontFamily: 'Arial',
fontStyle: 'regular', fontStyle: 'regular',
fontColor:theme === 'dark'? 'rgb(200, 200, 200)':'rgb(20, 20, 20)' , fontColor:theme === 'dark'? 'rgb(200, 200, 200)':'rgb(20, 20, 20)' ,
}, },
legend: { legend: {
@ -189,26 +133,20 @@ class LatencyLineChart extends React.Component<LatencyLineChartProps>{
tooltips: { tooltips: {
mode: 'label', mode: 'label',
bodyFontSize: 10, bodyFontSize: 12,
titleFontSize: 10, titleFontSize: 12,
callbacks: { callbacks: {
label: function(tooltipItem, data) { label: function(tooltipItem, data) {
if (typeof(tooltipItem.yLabel) === 'number') if (typeof(tooltipItem.yLabel) === 'number')
{ {
return data.datasets![tooltipItem.datasetIndex!].label +' : '+ tooltipItem.yLabel.toFixed(3); return data.datasets![tooltipItem.datasetIndex!].label +' : '+ tooltipItem.yLabel.toFixed(2);
} }
else else
{ {
return ''; return '';
} }
// return data.datasets![tooltipItem.datasetIndex!].label +' : '+ tooltipItem.yLabel!.Fixed(3);
// not able to do toFixed(3) in typescript as string|number type is not working with toFixed(3) function for
// as toFixed() function only works with numbers
// using type of check gives issues in 'label' variable name
//!That's the non-null assertion operator. It is a way to tell the compiler "this expression cannot be null or undefined here, so don't complain about the possibility of it being null or undefined." Sometimes the type checker is unable to make that determination itself.
}, },
}, },
}, },
@ -224,12 +162,6 @@ class LatencyLineChart extends React.Component<LatencyLineChartProps>{
maxTicksLimit: 6, maxTicksLimit: 6,
}, },
// scaleLabel: {
// display: true,
// labelString: 'latency in ms',
// fontSize: 6,
// padding: 4,
// },
gridLines: { gridLines: {
// You can change the color, the dash effect, the main axe color, etc. // You can change the color, the dash effect, the main axe color, etc.
borderDash: [1, 4], borderDash: [1, 4],
@ -258,22 +190,11 @@ class LatencyLineChart extends React.Component<LatencyLineChartProps>{
} }
GraphTracePopUp = () => { GraphTracePopUp = () => {
console.log('state in GraphTracePopPup',this.state);
if (this.state.showpopUp){ if (this.state.showpopUp){
return( return(
// <div className='applicationpopup' style={{top:`${this.state.ycoordinate}px`,zIndex:10,position:'absolute',left:`${this.state.xcoordinate}px`,backgroundColor:'white',border:'1px solid grey'}}>
// <p style={{color:'black'}} onClick={this.gotoTracesHandler}>View Traces</p>
// <p style={{color:'black'}}> Set Alerts</p>
// </div>
// <ChartPopUpUnique>
// <p style={{color:'black'}} onClick={this.gotoTracesHandler}>View Traces</p>
// <p style={{color:'black'}}> Set Alerts</p>
// </ChartPopUpUnique>
<ChartPopUpUnique xcoordinate={this.state.xcoordinate} ycoordinate={this.state.ycoordinate}> <ChartPopUpUnique xcoordinate={this.state.xcoordinate} ycoordinate={this.state.ycoordinate}>
<PopUpElements onClick={this.gotoTracesHandler}>View Traces</PopUpElements> <PopUpElements onClick={() => this.props.popupClickHandler(this.state.firstpoint_ts)}>View Traces</PopUpElements>
<PopUpElements onClick={this.gotoAlertsHandler}>Set Alerts</PopUpElements> <PopUpElements onClick={this.gotoAlertsHandler}>Set Alerts</PopUpElements>
</ChartPopUpUnique> </ChartPopUpUnique>
@ -289,18 +210,6 @@ class LatencyLineChart extends React.Component<LatencyLineChartProps>{
render(){ render(){
const ndata = this.props.data; const ndata = this.props.data;
// console.log("in chartJS line render function")
// console.log(ndata);
// const data_charts = data.map( s => ({label:s.ts, value:parseFloat(s.val)}) );
// if(this.chartRef.ctx)
// {
// var gradient = this.chartRef.ctx.createLinearGradient(0, 0, 0, 400);
// gradient.addColorStop(0, 'rgba(250,174,50,1)');
// gradient.addColorStop(1, 'rgba(250,174,50,0)');
// }
const data_chartJS = (canvas:any) => { const data_chartJS = (canvas:any) => {
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
@ -311,9 +220,6 @@ class LatencyLineChart extends React.Component<LatencyLineChartProps>{
datasets: [{ datasets: [{
label: 'p99 Latency', label: 'p99 Latency',
data: ndata.map(s => s.p99/1000000), //converting latency from nano sec to ms data: ndata.map(s => s.p99/1000000), //converting latency from nano sec to ms
// backgroundColor:'#000000',
// fill: true,
// backgroundColor: gradient,
pointRadius: 0.5, pointRadius: 0.5,
borderColor: 'rgba(250,174,50,1)', // Can also add transparency in border color borderColor: 'rgba(250,174,50,1)', // Can also add transparency in border color
borderWidth: 2, borderWidth: 2,
@ -321,9 +227,6 @@ class LatencyLineChart extends React.Component<LatencyLineChartProps>{
{ {
label: 'p90 Latency', label: 'p90 Latency',
data: ndata.map(s => s.p90/1000000), //converting latency from nano sec to ms data: ndata.map(s => s.p90/1000000), //converting latency from nano sec to ms
// backgroundColor:'#dd0000',
// fill: true,
// backgroundColor: 'rgba(227, 74, 51, 1.0)',
pointRadius: 0.5, pointRadius: 0.5,
borderColor: 'rgba(227, 74, 51, 1.0)', borderColor: 'rgba(227, 74, 51, 1.0)',
borderWidth: 2, borderWidth: 2,
@ -331,9 +234,6 @@ class LatencyLineChart extends React.Component<LatencyLineChartProps>{
{ {
label: 'p50 Latency', label: 'p50 Latency',
data: ndata.map(s => s.p50/1000000), //converting latency from nano sec to ms data: ndata.map(s => s.p50/1000000), //converting latency from nano sec to ms
// backgroundColor:'#dd0000',
// fill: true,
// backgroundColor: 'rgba(227, 74, 51, 1.0)',
pointRadius: 0.5, pointRadius: 0.5,
borderColor: 'rgba(57, 255, 20, 1.0)', borderColor: 'rgba(57, 255, 20, 1.0)',
borderWidth: 2, borderWidth: 2,

View File

@ -14,7 +14,7 @@ zIndex:10;
position:absolute; position:absolute;
top:${props => props.ycoordinate}px; top:${props => props.ycoordinate}px;
left:${props => props.xcoordinate}px; left:${props => props.xcoordinate}px;
font-size:10px; font-size:12px;
border-radius:2px; border-radius:2px;
`; `;
@ -29,40 +29,10 @@ padding-right:4px;
`; `;
// const data_charts = { const theme = 'dark';
// labels: ['1', '2', '3', '4', '5', '6'],
// datasets: [
// {
// // label: '# of Votes',
// data: [12, 19, 3, 5, 2, 3],
// fill: false,
// backgroundColor: 'rgb(255, 99, 132)',
// borderColor: 'rgba(255, 99, 132, 0.2)',
// },
// ],
// }
// const onClickHandler = (e) => {
// console.log("ChartJS chart clicked");
// console.log(e);
// };
// export interface ApiData {
// 0: number, // has epoch timestamp
// 1: string, // has value of the metric as a string
// }
const theme = 'dark';
// PNOTE - accessing history object in typescript - https://stackoverflow.com/questions/49342390/typescript-how-to-add-type-check-for-history-object-in-react
// withRouter is used to pass on history object as prop - https://stackoverflow.com/questions/43107912/how-to-access-history-object-in-new-react-router-v4
interface RequestRateChartProps extends RouteComponentProps<any> { interface RequestRateChartProps extends RouteComponentProps<any> {
data : metricItem[], data : metricItem[],
// chartRef: any,
// chartReference :ChartJSLineChart,
// data passed to ChartJSLineChart component is an array if json objects
} }
interface RequestRateChart { interface RequestRateChart {
@ -74,14 +44,10 @@ class RequestRateChart extends React.Component<RequestRateChartProps>{
constructor(props: RequestRateChartProps) { constructor(props: RequestRateChartProps) {
super(props); super(props);
console.log('React CreatRef', React.createRef());
this.chartRef = React.createRef(); this.chartRef = React.createRef();
} }
state = { state = {
// data: props.data,
xcoordinate:0, xcoordinate:0,
ycoordinate:0, ycoordinate:0,
showpopUp:false, showpopUp:false,
@ -90,27 +56,12 @@ class RequestRateChart extends React.Component<RequestRateChartProps>{
onClickhandler = async(e:any,event:any) => { onClickhandler = async(e:any,event:any) => {
console.log('e graph', e)
// console.log('event graph',event)
//PNOTE - e has all key values from mouse event, event has only reference to handler functions
// PNOTE - https://github.com/emn178/angular2-chartjs/issues/29 - for listening only to element points
// var firstPoin = this.chart.current.getElementAtEvent(e)
console.log('chartref',this.chartRef.current.chartInstance);
var firstPoint; var firstPoint;
if(this.chartRef){ if(this.chartRef){
firstPoint = this.chartRef.current.chartInstance.getElementAtEvent(e)[0]; firstPoint = this.chartRef.current.chartInstance.getElementAtEvent(e)[0];
} }
console.log('firstPoint', firstPoint);
// if (firstPoint) {
// var label = myChart.data.labels[firstPoint._index];
// var value = myChart.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
// }
if (firstPoint) if (firstPoint)
{// PNOTE - TODO - Is await needed in this expression? {// PNOTE - TODO - Is await needed in this expression?
await this.setState({ await this.setState({
@ -120,40 +71,20 @@ class RequestRateChart extends React.Component<RequestRateChartProps>{
// graphInfo:{...event} // graphInfo:{...event}
}) })
} }
// console.log(this.state.graphInfo.payload.timestamp)
//this.props.applicationTimeStamp(e.payload.x)
// this.props.history.push('/traces?timestamp=' + e.payload.timestamp + '&service=' + this.props.service.name)
} }
gotoTracesHandler=()=>{ gotoTracesHandler=()=>{
console.log('in gotoTraces handler')
this.props.history.push('/traces') this.props.history.push('/traces')
// this.props.history.push('/traces?timestamp=' + this.state.graphInfo.payload.timestamp + '&service=' + this.props.service.name)
} }
gotoAlertsHandler=()=>{ gotoAlertsHandler=()=>{
console.log('in gotoAlerts handler')
this.props.history.push('/service-map') this.props.history.push('/service-map')
// PNOTE - Keeping service map for now, will replace with alerts when alert page is made // PNOTE - Keeping service map for now, will replace with alerts when alert page is made
} }
options_charts: ChartOptions = { options_charts: ChartOptions = {
// onClick: function(evt, element) {
// // console.log(evt);
// },
//- PNOTE - TO DO -- element is of type ChartElement, how to define its type
// https://gitlab.com/signoz-frontend/sample-project/-/blob/darkthemechanges/src/Components/Application/Graphs/SimpleLineChart.js
// Code for popup
// onClick: function(evt, element :any[]) {
// if (element.length > 0) {
// var ind = element[0]._index;
// console.log(element)
// alert(ind);
// }
// },
onClick: this.onClickhandler, onClick: this.onClickhandler,
maintainAspectRatio: true, maintainAspectRatio: true,
@ -168,8 +99,6 @@ class RequestRateChart extends React.Component<RequestRateChartProps>{
fontFamily: 'Arial', fontFamily: 'Arial',
fontStyle: 'regular', fontStyle: 'regular',
fontColor:theme === 'dark'? 'rgb(200, 200, 200)':'rgb(20, 20, 20)' , fontColor:theme === 'dark'? 'rgb(200, 200, 200)':'rgb(20, 20, 20)' ,
}, },
legend: { legend: {
@ -182,32 +111,25 @@ class RequestRateChart extends React.Component<RequestRateChartProps>{
fontSize: 10, fontSize: 10,
boxWidth : 10, boxWidth : 10,
usePointStyle : true, usePointStyle : true,
} }
}, },
tooltips: { tooltips: {
mode: 'label', mode: 'label',
bodyFontSize: 10, bodyFontSize: 12,
titleFontSize: 10, titleFontSize: 12,
callbacks: { callbacks: {
label: function(tooltipItem, data) { label: function(tooltipItem, data) {
if (typeof(tooltipItem.yLabel) === 'number') if (typeof(tooltipItem.yLabel) === 'number')
{ {
return data.datasets![tooltipItem.datasetIndex!].label +' : '+ tooltipItem.yLabel.toFixed(3); return data.datasets![tooltipItem.datasetIndex!].label +' : '+ tooltipItem.yLabel.toFixed(2);
} }
else else
{ {
return ''; return '';
} }
// return data.datasets![tooltipItem.datasetIndex!].label +' : '+ tooltipItem.yLabel!.Fixed(3);
// not able to do toFixed(3) in typescript as string|number type is not working with toFixed(3) function for
// as toFixed() function only works with numbers
// using type of check gives issues in 'label' variable name
//!That's the non-null assertion operator. It is a way to tell the compiler "this expression cannot be null or undefined here, so don't complain about the possibility of it being null or undefined." Sometimes the type checker is unable to make that determination itself.
}, },
}, },
@ -224,12 +146,7 @@ class RequestRateChart extends React.Component<RequestRateChartProps>{
maxTicksLimit: 6, maxTicksLimit: 6,
}, },
// scaleLabel: {
// display: true,
// labelString: 'latency in ms',
// fontSize: 6,
// padding: 4,
// },
gridLines: { gridLines: {
// You can change the color, the dash effect, the main axe color, etc. // You can change the color, the dash effect, the main axe color, etc.
borderDash: [1, 4], borderDash: [1, 4],
@ -256,26 +173,13 @@ class RequestRateChart extends React.Component<RequestRateChartProps>{
} }
GraphTracePopUp = () => { GraphTracePopUp = () => {
console.log('state in GraphTracePopPup',this.state);
if (this.state.showpopUp){ if (this.state.showpopUp){
return( return(
// <div className='applicationpopup' style={{top:`${this.state.ycoordinate}px`,zIndex:10,position:'absolute',left:`${this.state.xcoordinate}px`,backgroundColor:'white',border:'1px solid grey'}}>
// <p style={{color:'black'}} onClick={this.gotoTracesHandler}>View Traces</p>
// <p style={{color:'black'}}> Set Alerts</p>
// </div>
// <ChartPopUpUnique>
// <p style={{color:'black'}} onClick={this.gotoTracesHandler}>View Traces</p>
// <p style={{color:'black'}}> Set Alerts</p>
// </ChartPopUpUnique>
<ChartPopUpUnique xcoordinate={this.state.xcoordinate} ycoordinate={this.state.ycoordinate}> <ChartPopUpUnique xcoordinate={this.state.xcoordinate} ycoordinate={this.state.ycoordinate}>
<PopUpElements onClick={this.gotoTracesHandler}>View Traces</PopUpElements> <PopUpElements onClick={this.gotoTracesHandler}>View Traces</PopUpElements>
<PopUpElements onClick={this.gotoAlertsHandler}>Set Alerts</PopUpElements> <PopUpElements onClick={this.gotoAlertsHandler}>Set Alerts</PopUpElements>
</ChartPopUpUnique> </ChartPopUpUnique>
) )
} }
else else
@ -287,18 +191,6 @@ class RequestRateChart extends React.Component<RequestRateChartProps>{
render(){ render(){
const ndata = this.props.data; const ndata = this.props.data;
// console.log("in chartJS line render function")
// console.log(ndata);
// const data_charts = data.map( s => ({label:s.ts, value:parseFloat(s.val)}) );
// if(this.chartRef.ctx)
// {
// var gradient = this.chartRef.ctx.createLinearGradient(0, 0, 0, 400);
// gradient.addColorStop(0, 'rgba(250,174,50,1)');
// gradient.addColorStop(1, 'rgba(250,174,50,0)');
// }
const data_chartJS = (canvas:any) => { const data_chartJS = (canvas:any) => {
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
@ -309,19 +201,14 @@ class RequestRateChart extends React.Component<RequestRateChartProps>{
datasets: [{ datasets: [{
label: 'Request per sec', label: 'Request per sec',
data: ndata.map(s => s.callRate), data: ndata.map(s => s.callRate),
// backgroundColor:'#000000',
// fill: true,
// backgroundColor: gradient,
pointRadius: 0.5, pointRadius: 0.5,
borderColor: 'rgba(250,174,50,1)', // Can also add transparency in border color borderColor: 'rgba(250,174,50,1)', // Can also add transparency in border color
borderWidth: 2, borderWidth: 2,
}, },
]} ]}
}; };
return( return(
<div> <div>
{this.GraphTracePopUp()} {this.GraphTracePopUp()}

View File

@ -1,10 +1,10 @@
import React,{useEffect} from 'react'; import React,{useEffect} from 'react';
import { Tabs, Card, Row, Col} from 'antd'; import { Tabs, Card, Row, Col} from 'antd';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { useParams } from "react-router-dom"; import { useParams, RouteComponentProps } from "react-router-dom";
import { withRouter } from "react-router";
import { getServicesMetrics, metricItem, getTopEndpoints, topEndpointListItem, GlobalTime, updateTimeInterval } from '../../actions';
import { getServicesMetrics, metricItem, getTopEndpoints, topEndpointListItem, GlobalTime } from '../../actions';
import { StoreState } from '../../reducers' import { StoreState } from '../../reducers'
import LatencyLineChart from "./LatencyLineChart" import LatencyLineChart from "./LatencyLineChart"
import RequestRateChart from './RequestRateChart' import RequestRateChart from './RequestRateChart'
@ -13,25 +13,30 @@ import TopEndpointsTable from './TopEndpointsTable';
const { TabPane } = Tabs; const { TabPane } = Tabs;
interface ServicesMetricsProps { interface ServicesMetricsProps extends RouteComponentProps<any>{
serviceMetrics: metricItem[], serviceMetrics: metricItem[],
getServicesMetrics: Function, getServicesMetrics: Function,
topEndpointsList: topEndpointListItem[], topEndpointsList: topEndpointListItem[],
getTopEndpoints: Function, getTopEndpoints: Function,
globalTime: GlobalTime, globalTime: GlobalTime,
updateTimeInterval: Function,
} }
const _ServiceMetrics = (props: ServicesMetricsProps) => { const _ServiceMetrics = (props: ServicesMetricsProps) => {
const params = useParams<{ servicename?: string; }>(); const params = useParams<{ servicename?: string; }>();
console.log('service name',params.servicename);
useEffect( () => { useEffect( () => {
props.getServicesMetrics(params.servicename,props.globalTime); props.getServicesMetrics(params.servicename,props.globalTime);
props.getTopEndpoints(params.servicename,props.globalTime); props.getTopEndpoints(params.servicename,props.globalTime);
}, [props.globalTime,params.servicename]); }, [props.globalTime,params.servicename]);
const onTracePopupClick = (timestamp:number) => {
props.updateTimeInterval('custom',[(timestamp/1000000)-5*60*1000,(timestamp/1000000)])// updateTimeInterval takes second range in ms -- give -5 min to selected time,
props.history.push('/traces')
}
return ( return (
<Tabs defaultActiveKey="1"> <Tabs defaultActiveKey="1">
<TabPane tab="Application Metrics" key="1"> <TabPane tab="Application Metrics" key="1">
@ -39,7 +44,7 @@ const _ServiceMetrics = (props: ServicesMetricsProps) => {
<Row gutter={32} style={{ margin: 20 }}> <Row gutter={32} style={{ margin: 20 }}>
<Col span={12} > <Col span={12} >
<Card bodyStyle={{padding:10}}> <Card bodyStyle={{padding:10}}>
<LatencyLineChart data={props.serviceMetrics} /> <LatencyLineChart data={props.serviceMetrics} popupClickHandler={onTracePopupClick} />
</Card> </Card>
</Col> </Col>
@ -88,11 +93,11 @@ const mapStateToProps = (state: StoreState): { serviceMetrics: metricItem[], top
return { serviceMetrics : state.serviceMetrics, topEndpointsList: state.topEndpointsList, globalTime:state.globalTime}; return { serviceMetrics : state.serviceMetrics, topEndpointsList: state.topEndpointsList, globalTime:state.globalTime};
}; };
// the name mapStateToProps is only a convention
// take state and map it to props which are accessible inside this component
export const ServiceMetrics = connect(mapStateToProps, { export const ServiceMetrics = withRouter(connect(mapStateToProps, {
getServicesMetrics: getServicesMetrics, getServicesMetrics: getServicesMetrics,
getTopEndpoints: getTopEndpoints, getTopEndpoints: getTopEndpoints,
})(_ServiceMetrics); updateTimeInterval: updateTimeInterval,
})(_ServiceMetrics));

View File

@ -24,10 +24,6 @@ padding-right:40px;
.ant-table tfoot>tr>td, .ant-table tfoot>tr>th, .ant-table-tbody>tr>td, .ant-table-thead>tr>th { padding: 10px; }; .ant-table tfoot>tr>td, .ant-table tfoot>tr>th, .ant-table-tbody>tr>td, .ant-table-thead>tr>th { padding: 10px; };
`; `;
//styling antd with styled components - https://codesandbox.io/s/8x1r670rxj
const columns = [ const columns = [
@ -69,7 +65,6 @@ const _ServicesTable = (props: ServicesTableProps) => {
const search = useLocation().search; const search = useLocation().search;
const time_interval = new URLSearchParams(search).get('time'); const time_interval = new URLSearchParams(search).get('time');
console.log(time_interval)
useEffect( () => { useEffect( () => {
props.getServicesList(props.globalTime); props.getServicesList(props.globalTime);
@ -79,7 +74,6 @@ const _ServicesTable = (props: ServicesTableProps) => {
return( return(
<Wrapper> <Wrapper>
{console.log(props.servicesList)}
<Table dataSource={props.servicesList} columns={columns} pagination={false} /> <Table dataSource={props.servicesList} columns={columns} pagination={false} />
</Wrapper> </Wrapper>
@ -88,11 +82,8 @@ const _ServicesTable = (props: ServicesTableProps) => {
} }
const mapStateToProps = (state: StoreState): { servicesList: servicesListItem[], globalTime: GlobalTime } => { const mapStateToProps = (state: StoreState): { servicesList: servicesListItem[], globalTime: GlobalTime } => {
// console.log(state);
return { servicesList : state.servicesList, globalTime:state.globalTime}; return { servicesList : state.servicesList, globalTime:state.globalTime};
}; };
// the name mapStateToProps is only a convention
// take state and map it to props which are accessible inside this component
export const ServicesTable = connect(mapStateToProps, { export const ServicesTable = connect(mapStateToProps, {
getServicesList: getServicesList, getServicesList: getServicesList,

View File

@ -26,9 +26,7 @@ const TopEndpointsTable = (props: TopEndpointsTableProps) => {
title: 'Name', title: 'Name',
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
// sorter: (a:any, b:any) => a.startTime - b.startTime,
// sortDirections: ['descend', 'ascend'],
//PNOTE - TO DO - Change this to API link if available
render: (text :string) => <NavLink to={'/' + text}>{text}</NavLink>, render: (text :string) => <NavLink to={'/' + text}>{text}</NavLink>,
}, },
@ -61,21 +59,15 @@ const TopEndpointsTable = (props: TopEndpointsTableProps) => {
dataIndex: 'numCalls', dataIndex: 'numCalls',
key: 'numCalls', key: 'numCalls',
sorter: (a:any, b:any) => a.numCalls - b.numCalls, sorter: (a:any, b:any) => a.numCalls - b.numCalls,
// sortDirections: ['descend', 'ascend'],
// render: (value: number) => value.toFixed(2),
}, },
]; ];
return( return(
<Wrapper> <Wrapper>
<h6> Top Endpoints</h6> <h6> Top Endpoints</h6>
<Table dataSource={props.data} columns={columns} pagination={false} /> <Table dataSource={props.data} columns={columns} pagination={false} />
</Wrapper> </Wrapper>
) )
} }
export default TopEndpointsTable; export default TopEndpointsTable;

View File

@ -6,7 +6,6 @@ const ServiceMap = () => {
return ( return (
<div> <div>
{/* <div> Service Map </div> */}
<ServiceGraph /> <ServiceGraph />
</div> </div>

View File

@ -15,7 +15,6 @@ interface FilterStateDisplayProps {
const _FilterStateDisplay = (props: FilterStateDisplayProps) => { const _FilterStateDisplay = (props: FilterStateDisplayProps) => {
function handleCloseTag(value:string) { function handleCloseTag(value:string) {
console.log('on close tag', value)
if (value==='service') if (value==='service')
props.updateTraceFilters({...props.traceFilters,service:''}) props.updateTraceFilters({...props.traceFilters,service:''})
if (value==='operation') if (value==='operation')
@ -28,7 +27,6 @@ const _FilterStateDisplay = (props: FilterStateDisplayProps) => {
} }
function handleCloseTagElement(item:TagItem){ function handleCloseTagElement(item:TagItem){
console.log('tag item closed in handle closeTagElement', item)
props.updateTraceFilters({...props.traceFilters,tags:props.traceFilters.tags?.filter(elem => elem !== item)}) props.updateTraceFilters({...props.traceFilters,tags:props.traceFilters.tags?.filter(elem => elem !== item)})
} }
@ -56,7 +54,6 @@ const _FilterStateDisplay = (props: FilterStateDisplayProps) => {
onClose={e => {handleCloseTag('maxLatency');}}> onClose={e => {handleCloseTag('maxLatency');}}>
maxLatency:{(parseInt(props.traceFilters.latency!.max)/1000000).toString()}ms maxLatency:{(parseInt(props.traceFilters.latency!.max)/1000000).toString()}ms
</Tag> } </Tag> }
{console.log('tagfilters before showing on card',props.traceFilters.tags)}
{props.traceFilters.tags === undefined? null: props.traceFilters.tags.map( item => ( {props.traceFilters.tags === undefined? null: props.traceFilters.tags.map( item => (
<Tag style={{fontSize:14, padding: 8}} closable <Tag style={{fontSize:14, padding: 8}} closable
onClose={e => {handleCloseTagElement(item);}}> onClose={e => {handleCloseTagElement(item);}}>

View File

@ -2,11 +2,6 @@ import React from 'react';
import { Modal, Form, InputNumber, Col, Row} from 'antd'; import { Modal, Form, InputNumber, Col, Row} from 'antd';
import { Store } from 'antd/lib/form/interface'; import { Store } from 'antd/lib/form/interface';
// interface Values {
// title: string;
// description: string;
// modifier: string;
// }
interface LatencyModalFormProps { interface LatencyModalFormProps {
visible: boolean; visible: boolean;
@ -32,7 +27,6 @@ const LatencyModalForm: React.FC<LatencyModalFormProps> = ({
.validateFields() .validateFields()
.then(values => { .then(values => {
form.resetFields(); form.resetFields();
// onCreate({title:"hello",description:'good',modifier:'public'});
onCreate(values); // giving error for values onCreate(values); // giving error for values
}) })
.catch(info => { .catch(info => {

View File

@ -17,7 +17,6 @@ interface SelectedSpanDetailsProps {
const SelectedSpanDetails = (props: SelectedSpanDetailsProps) => { const SelectedSpanDetails = (props: SelectedSpanDetailsProps) => {
const callback = (key:any) => { const callback = (key:any) => {
console.log(key);
} }
return ( return (

View File

@ -67,7 +67,6 @@ interface TraceCustomVisualizationsProps {
const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => { const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => {
const [selectedEntity, setSelectedEntity] = useState('calls'); const [selectedEntity, setSelectedEntity] = useState('calls');
// const [defaultOption, setDefaultOption]=useState('count');
const [selectedAggOption, setSelectedAggOption] = useState('count'); const [selectedAggOption, setSelectedAggOption] = useState('count');
const [selectedStep, setSelectedStep] = useState('60'); const [selectedStep, setSelectedStep] = useState('60');
// Step should be multiples of 60, 60 -> 1 min // Step should be multiples of 60, 60 -> 1 min
@ -98,40 +97,27 @@ const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
function handleChange(value:string) { function handleChange(value:string) {
console.log(value); // console.log(value);
} }
// function handleChangeEntity(value:string) {
// // console.log(value);
// // setSelectedEntity(value);
// setDefaultOption(aggregation_options.filter((item) => item.linked_entity === selectedEntity)[0].default_selected)
// }
function handleFinish(value:string) { function handleFinish(value:string) {
console.log(value); // console.log(value);
} }
// PNOTE - Can also use 'coordinate' option in antd Select for implementing this - https://ant.design/components/select/ // PNOTE - Can also use 'coordinate' option in antd Select for implementing this - https://ant.design/components/select/
const handleFormValuesChange = (changedValues:any) => { const handleFormValuesChange = (changedValues:any) => {
const formFieldName = Object.keys(changedValues)[0]; const formFieldName = Object.keys(changedValues)[0];
// console.log('Object keys',Object.keys(changedValues) );
if (formFieldName === 'entity') { if (formFieldName === 'entity') {
// const a = selectedEntity; // why is selected entity not set instantly??
// setDefaultOption(aggregation_options.filter((item) => item.linked_entity === selectedEntity)[0].default_selected)
const temp_entity = aggregation_options.filter((item) => item.linked_entity === changedValues[formFieldName])[0]; const temp_entity = aggregation_options.filter((item) => item.linked_entity === changedValues[formFieldName])[0];
form.setFieldsValue( { form.setFieldsValue( {
// agg_options : aggregation_options.filter((item) => item.linked_entity === selectedEntity)[0].default_selected,
agg_options : temp_entity.default_selected.title, agg_options : temp_entity.default_selected.title,
// PNOTE - TO DO Check if this has the same behaviour as selecting an option? // PNOTE - TO DO Check if this has the same behaviour as selecting an option?
}) })
let temp = form.getFieldsValue(['agg_options','entity']); let temp = form.getFieldsValue(['agg_options','entity']);
console.log('custom metric field values',temp,temp.entity,temp.agg_options);
setSelectedEntity(temp.entity); setSelectedEntity(temp.entity);
setSelectedAggOption(temp.agg_options); setSelectedAggOption(temp.agg_options);
@ -146,13 +132,6 @@ const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => {
} }
// Make api calls here and display data
// PNOTE - TO DO - API CALL location - may be through action creators and set states in redux store?
// PNOTE - Change this
// API call via useEffects after monitoring traces
} }
@ -167,12 +146,10 @@ const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => {
form={form} form={form}
onFinish={handleFinish} onFinish={handleFinish}
onValuesChange={handleFormValuesChange} onValuesChange={handleFormValuesChange}
// initialValues={ agg_options: 'Count'}
initialValues={{ agg_options: 'Count', chart_style:'line', interval:'5m', group_by:'none' }} initialValues={{ agg_options: 'Count', chart_style:'line', interval:'5m', group_by:'none' }}
> >
<Space> <Space>
<Form.Item name="entity"> <Form.Item name="entity">
{/* <Select defaultValue={selectedEntity} style={{ width: 120 }} onChange={handleChangeEntity} allowClear> */}
<Select defaultValue={selectedEntity} style={{ width: 120 }} allowClear> <Select defaultValue={selectedEntity} style={{ width: 120 }} allowClear>
{entity.map((item) => ( {entity.map((item) => (
@ -197,11 +174,7 @@ const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => {
} }
</Select> </Select>
</Form.Item> </Form.Item>
{/* <Select defaultValue="count" style={{ width: 120 }} onChange={handleChange} allowClear>
<Option value="count">Count</Option>
<Option value="sume">Sum</Option>
<Option value="rate">Rate</Option>
</Select> */}
<Form.Item name="chart_style"> <Form.Item name="chart_style">
<Select style={{ width: 120 }} onChange={handleChange} allowClear> <Select style={{ width: 120 }} onChange={handleChange} allowClear>
<Option value="line">Line Chart</Option> <Option value="line">Line Chart</Option>
@ -239,14 +212,10 @@ const _TraceCustomVisualizations = (props: TraceCustomVisualizationsProps) => {
} }
const mapStateToProps = (state: StoreState): { filteredTraceMetrics: customMetricsItem[] , globalTime: GlobalTime, traceFilters: TraceFilters} => { const mapStateToProps = (state: StoreState): { filteredTraceMetrics: customMetricsItem[] , globalTime: GlobalTime, traceFilters: TraceFilters} => {
// console.log(state);
return { filteredTraceMetrics : state.filteredTraceMetrics, globalTime: state.globalTime,traceFilters:state.traceFilters }; return { filteredTraceMetrics : state.filteredTraceMetrics, globalTime: state.globalTime,traceFilters:state.traceFilters };
}; };
// the name mapStateToProps is only a convention
// take state and map it to props which are accessible inside this component
export const TraceCustomVisualizations = connect(mapStateToProps, { export const TraceCustomVisualizations = connect(mapStateToProps, {
getFilteredTraceMetrics: getFilteredTraceMetrics, getFilteredTraceMetrics: getFilteredTraceMetrics,
})(_TraceCustomVisualizations); })(_TraceCustomVisualizations);
// export default TraceCustomVisualizations;

View File

@ -7,14 +7,11 @@ import { TraceList } from './TraceList';
const TraceDetail = () => { const TraceDetail = () => {
// const [serviceName, setServiceName] = useState('Frontend'); //default value of service name
return ( return (
<div> <div>
{/* <div>Tracing Detail Page</div> */}
<TraceFilter /> <TraceFilter />
{/* <TraceFilter servicename={serviceName} /> */}
<TraceCustomVisualizations /> <TraceCustomVisualizations />
<TraceList /> <TraceList />
</div> </div>

View File

@ -1,5 +1,5 @@
import React,{useEffect, useState} from 'react'; import React,{useEffect, useState} from 'react';
import { Select, Button, Input, Tag, Card, Form, AutoComplete} from 'antd'; import { Select, Button, Input, Form, AutoComplete} from 'antd';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Store } from 'antd/lib/form/interface'; import { Store } from 'antd/lib/form/interface';
import styled from 'styled-components'; import styled from 'styled-components';
@ -43,7 +43,6 @@ const _TraceFilter = (props: TraceFilterProps) => {
useEffect( () => { useEffect( () => {
metricsAPI.get<string[]>('services/list').then(response => { metricsAPI.get<string[]>('services/list').then(response => {
// console.log(response.data);
setServiceList( response.data ); setServiceList( response.data );
}); });
}, []); }, []);
@ -57,18 +56,9 @@ const _TraceFilter = (props: TraceFilterProps) => {
request_string=request_string+'&tags='+encodeURIComponent(JSON.stringify(props.traceFilters.tags)); request_string=request_string+'&tags='+encodeURIComponent(JSON.stringify(props.traceFilters.tags));
props.fetchTraces(props.globalTime, request_string) props.fetchTraces(props.globalTime, request_string)
console.log('stringified traceFilters redux state',encodeURIComponent(JSON.stringify(props.traceFilters.tags)));
}, [props.traceFilters,props.globalTime]); }, [props.traceFilters,props.globalTime]);
// useEffect( () => {
// tagKeyOptions.map(s => options.push({'value':s.tagKeys}));
// console.log('tagoptions USeEffect',options)
// }, [tagKeyOptions]);
// Use effects run in the order they are specified
useEffect ( () => { useEffect ( () => {
let latencyButtonText = 'Latency'; let latencyButtonText = 'Latency';
@ -115,17 +105,14 @@ const _TraceFilter = (props: TraceFilterProps) => {
} }
function handleChangeService(value:string) { function handleChangeService(value:string) {
console.log(value);
let service_request='/service/'+value+'/operations'; let service_request='/service/'+value+'/operations';
metricsAPI.get<string[]>(service_request).then(response => { metricsAPI.get<string[]>(service_request).then(response => {
console.log('operations',response.data);
// form_basefilter.resetFields(['operation',]) // form_basefilter.resetFields(['operation',])
setOperationsList( response.data ); setOperationsList( response.data );
}); });
let tagkeyoptions_request='tags?service='+value; let tagkeyoptions_request='tags?service='+value;
metricsAPI.get<TagKeyOptionItem[]>(tagkeyoptions_request).then(response => { metricsAPI.get<TagKeyOptionItem[]>(tagkeyoptions_request).then(response => {
console.log('tag key options',response.data);
setTagKeyOptions( response.data ); setTagKeyOptions( response.data );
}); });
@ -140,24 +127,14 @@ const _TraceFilter = (props: TraceFilterProps) => {
const onLatencyModalApply = (values: Store) => { const onLatencyModalApply = (values: Store) => {
setModalVisible(false); setModalVisible(false);
console.log('Received values of form: ', values);
//Latency modal form returns null if the value entered is not a number or string etc or empty
props.updateTraceFilters({...props.traceFilters,latency:{min:values.min?(parseInt(values.min)*1000000).toString():"", max:values.max?(parseInt(values.max)*1000000).toString():""}}) props.updateTraceFilters({...props.traceFilters,latency:{min:values.min?(parseInt(values.min)*1000000).toString():"", max:values.max?(parseInt(values.max)*1000000).toString():""}})
// setLatencyFilterValues()
} }
const onTagFormSubmit = (values:any) => { const onTagFormSubmit = (values:any) => {
console.log(values);
// setTagKeyValueApplied(tagKeyValueApplied => [...tagKeyValueApplied, values.tag_key+' '+values.operator+' '+values.tag_value]);
// making API calls with only tags data for testing, for last tagform submit
// ideally all tags including service, operations, tags, latency selected should be in one state and used
// to make API call and update trace list
let request_tags= 'service=frontend&tags='+encodeURIComponent(JSON.stringify([{"key":values.tag_key,"value":values.tag_value,"operator":values.operator}])) let request_tags= 'service=frontend&tags='+encodeURIComponent(JSON.stringify([{"key":values.tag_key,"value":values.tag_value,"operator":values.operator}]))
// props.fetchTraces(request_tags)
// form field names are tag_key & tag_value
// data structure has key, value & operator
if (props.traceFilters.tags){ // If there are existing tag filters present if (props.traceFilters.tags){ // If there are existing tag filters present
props.updateTraceFilters( props.updateTraceFilters(
@ -183,9 +160,6 @@ const _TraceFilter = (props: TraceFilterProps) => {
} }
const onTagClose = (value:string) => { const onTagClose = (value:string) => {
console.log(value);
// setJoinList(joinList.filter((e)=>(e !== name)))
// removing closed tag from the tagKeyValueApplied array
setTagKeyValueApplied(tagKeyValueApplied.filter( e => (e !== value))); setTagKeyValueApplied(tagKeyValueApplied.filter( e => (e !== value)));
} }
@ -206,9 +180,7 @@ const _TraceFilter = (props: TraceFilterProps) => {
// PNOTE - Remove any // PNOTE - Remove any
const handleApplyFilterForm = (values:any) => { const handleApplyFilterForm = (values:any) => {
console.log('values are', values);
console.log(typeof(values.service))
console.log(typeof(values.operation))
let request_params: string =''; let request_params: string ='';
if (typeof values.service !== undefined && typeof(values.operation) !== undefined) if (typeof values.service !== undefined && typeof(values.operation) !== undefined)
{ {
@ -224,12 +196,8 @@ const _TraceFilter = (props: TraceFilterProps) => {
} }
request_params=request_params+'&minDuration='+latencyFilterValues.min+'&maxDuration='+latencyFilterValues.max; request_params=request_params+'&minDuration='+latencyFilterValues.min+'&maxDuration='+latencyFilterValues.max;
console.log(request_params);
// props.fetchTraces(request_params)
// console.log(props.inputTag)
// props.updateTagFilters([{key:props.inputTag, value: props.inputTag }]);
setTagKeyValueApplied(tagKeyValueApplied => [...tagKeyValueApplied, 'service eq'+values.service, 'operation eq '+values.operation, 'maxduration eq '+ (parseInt(latencyFilterValues.max)/1000000).toString(), 'minduration eq '+(parseInt(latencyFilterValues.min)/1000000).toString()]); setTagKeyValueApplied(tagKeyValueApplied => [...tagKeyValueApplied, 'service eq'+values.service, 'operation eq '+values.operation, 'maxduration eq '+ (parseInt(latencyFilterValues.max)/1000000).toString(), 'minduration eq '+(parseInt(latencyFilterValues.min)/1000000).toString()]);
props.updateTraceFilters({'service':values.service,'operation':values.operation,'latency':latencyFilterValues}) props.updateTraceFilters({'service':values.service,'operation':values.operation,'latency':latencyFilterValues})
} }
@ -238,7 +206,7 @@ const _TraceFilter = (props: TraceFilterProps) => {
return ( return (
<div> <div>
<div>Filter Traces</div> <div>Filter Traces</div>
<div>{JSON.stringify(props.traceFilters)}</div> {/* <div>{JSON.stringify(props.traceFilters)}</div> */}
<Form form={form_basefilter} layout='inline' onFinish={handleApplyFilterForm} initialValues={{ service:'', operation:'',latency:'Latency',}} style={{marginTop: 10, marginBottom:10}}> <Form form={form_basefilter} layout='inline' onFinish={handleApplyFilterForm} initialValues={{ service:'', operation:'',latency:'Latency',}} style={{marginTop: 10, marginBottom:10}}>
<FormItem rules={[{ required: true }]} name='service'> <FormItem rules={[{ required: true }]} name='service'>
@ -250,13 +218,6 @@ const _TraceFilter = (props: TraceFilterProps) => {
<FormItem name='operation'> <FormItem name='operation'>
<Select showSearch style={{ width: 180 }} onChange={handleChangeOperation} placeholder='Select Operation' allowClear> <Select showSearch style={{ width: 180 }} onChange={handleChangeOperation} placeholder='Select Operation' allowClear>
{operationList.map( item => <Option value={item}>{item}</Option>)} {operationList.map( item => <Option value={item}>{item}</Option>)}
{/* We need to URL encode before making API call on form submission */}
{/* <Option value="HTTP%20GET">HTTP GET</Option>
<Option value="HTTP%20GET%20%2Fdispatch">HTTP GET /dispatch</Option>
<Option value="HTTP%20GET%3A%20%2Froute">HTTP GET: /route</Option>
<Option value="%2Fdriver.DriverService%2FFindNearest">/driver.DriverService/FindNearest</Option>
<Option value="HTTP%20GET%20%2Fcustomer">HTTP GET /customer</Option> */}
</Select> </Select>
</FormItem> </FormItem>
@ -271,10 +232,7 @@ const _TraceFilter = (props: TraceFilterProps) => {
<FilterStateDisplay /> <FilterStateDisplay />
{/* <Card style={{padding: 6, marginTop: 10, marginBottom: 10}} bodyStyle={{padding: 6}}>
<Tag style={{fontSize:14, padding: 8}} closable> status:200 </Tag><Tag style={{fontSize:14, padding: 8}} closable> customerid:123 </Tag>
{tagKeyValueApplied.map( item => <Tag key={item} style={{fontSize:14, padding: 8}} onClose={() => onTagClose(item)} closable> {item} </Tag>)}
</Card> */}
{/* // What will be the empty state of card when there is no Tag , it should show something */} {/* // What will be the empty state of card when there is no Tag , it should show something */}
<InfoWrapper>Select Service to get Tag suggestions </InfoWrapper> <InfoWrapper>Select Service to get Tag suggestions </InfoWrapper>
@ -282,8 +240,7 @@ const _TraceFilter = (props: TraceFilterProps) => {
<Form form={form} layout='inline' onFinish={onTagFormSubmit} initialValues={{operator:'equals'}} style={{marginTop: 10, marginBottom:10}}> <Form form={form} layout='inline' onFinish={onTagFormSubmit} initialValues={{operator:'equals'}} style={{marginTop: 10, marginBottom:10}}>
<FormItem rules={[{ required: true }]} name='tag_key'> <FormItem rules={[{ required: true }]} name='tag_key'>
{/* <Input style={{ width: 160, textAlign: 'center' }} placeholder="Tag Key" /> */}
{/* Not using tag count data to show in options */}
<AutoComplete <AutoComplete
options={tagKeyOptions.map((s) => { return ({'value' : s.tagKeys}) })} options={tagKeyOptions.map((s) => { return ({'value' : s.tagKeys}) })}
@ -296,14 +253,12 @@ const _TraceFilter = (props: TraceFilterProps) => {
} }
placeholder="Tag Key" placeholder="Tag Key"
/> />
{/* // ! means that we are saying object can't be undefined */}
</FormItem> </FormItem>
<FormItem name='operator'> <FormItem name='operator'>
<Select style={{ width: 120, textAlign: 'center' }}> <Select style={{ width: 120, textAlign: 'center' }}>
<Option value="equals">EQUAL</Option> <Option value="equals">EQUAL</Option>
<Option value="contains">CONTAINS</Option> <Option value="contains">CONTAINS</Option>
{/* <Option value="not-in">NOT IN</Option> */}
</Select> </Select>
</FormItem> </FormItem>
@ -329,7 +284,6 @@ const _TraceFilter = (props: TraceFilterProps) => {
} }
const mapStateToProps = (state: StoreState): { traceFilters: TraceFilters, globalTime: GlobalTime } => { const mapStateToProps = (state: StoreState): { traceFilters: TraceFilters, globalTime: GlobalTime } => {
// console.log(state);
return { traceFilters: state.traceFilters, globalTime: state.globalTime }; return { traceFilters: state.traceFilters, globalTime: state.globalTime };
}; };

View File

@ -8,7 +8,6 @@ import * as d3Tip from 'd3-tip';
//import * as d3Tip from 'd3-tip'; //import * as d3Tip from 'd3-tip';
// PNOTE - uninstall @types/d3-tip. issues with importing d3-tip https://github.com/Caged/d3-tip/issues/181 // PNOTE - uninstall @types/d3-tip. issues with importing d3-tip https://github.com/Caged/d3-tip/issues/181
// import styled from 'styled-components';
import './TraceGraph.css' import './TraceGraph.css'
import { spanToTreeUtil } from '../../utils/spanToTree' import { spanToTreeUtil } from '../../utils/spanToTree'
@ -31,7 +30,6 @@ const _TraceGraph = (props: TraceGraphProps) => {
const [clickedSpanTags,setClickedSpanTags]=useState([]) const [clickedSpanTags,setClickedSpanTags]=useState([])
useEffect( () => { useEffect( () => {
console.log('inside initial fetch trace for flamegraph')
props.fetchTraceItem(params.id); props.fetchTraceItem(params.id);
}, []); }, []);
@ -45,29 +43,12 @@ const _TraceGraph = (props: TraceGraphProps) => {
// if this monitoring of props.traceItem.data is removed then zoom on click doesn't work // if this monitoring of props.traceItem.data is removed then zoom on click doesn't work
// Doesn't work if only do initial check, works if monitor an element - as it may get updated in sometime // Doesn't work if only do initial check, works if monitor an element - as it may get updated in sometime
// PNOTE - Is this being called multiple times?
//PNOTE - Do we fetch trace data again based on id or do we call again using rest calls
// d3-flame-graph repository -- https://github.com/spiermar/d3-flame-graph
// const tip = d3.tip().attr('class', 'd3-tip').html(function(d) { return d; });
// const tip = d3Tip.default().attr('class', 'd3-flame-graph-tip').html(function(d:any) { console.log(d);return 'Name -> '+d.data.name+'<BR>Duration -> '+d.data.value});
//https://stackoverflow.com/questions/5934928/javascript-return-value-for-tooltip -> How to display tooltip
//const tip = d3Tip.default().attr('class', 'd3-tip').html(function(d:any) { console.log(d);return <FlamegraphTooltip>{d.data.name}</FlamegraphTooltip>});
// var tip = flamegraph.tooltip.defaultFlamegraphTooltip().html(function(d) { return "name: " + d.data.name + ", value: " + d.data.value; });
const tip = d3Tip.default().attr('class', 'd3-tip').html(function(d:any) { return d.data.name+'<br>duration: '+d.data.value}); const tip = d3Tip.default().attr('class', 'd3-tip').html(function(d:any) { return d.data.name+'<br>duration: '+d.data.value});
// PNOTE - Used this example for tooltip styling
const onClick = (z:any) => { const onClick = (z:any) => {
// props.tagsInfo(d.data.tags)
// let new_tags = z.data.tags;
// let new_tags = [{
// key: 'Ankit',
// type: 'testin',
// value: 'Nothing',
// }]
// setClickedSpanTags(new_tags);
// setNum(9);
setClickedSpanTags(z.data.tags); setClickedSpanTags(z.data.tags);
console.log(`Clicked on ${z.data.name}, id: "${z.id}"`); console.log(`Clicked on ${z.data.name}, id: "${z.id}"`);
} }
@ -86,25 +67,7 @@ const _TraceGraph = (props: TraceGraphProps) => {
// .title("Trace Flame graph") // .title("Trace Flame graph")
.differential(false) .differential(false)
.selfValue(true); //sets span width based on value - which is mapped to duration .selfValue(true); //sets span width based on value - which is mapped to duration
//PNOTE
// used inverted() instead of passing a function in Sort
// .sort(function(a :pushDStree,b :pushDStree) :number{
// if(a.startTime < b.startTime) return -1;
// else return 1;
// })
//removed transition ease - easeCubic as it was giving type error. d3.easeCubic is the default transition easing function
//Example to sort in reverse order
//.sort(function(a,b){ return d3.descending(a.name, b.name);})
// PNOTE - filter based on traceid - trace should already be in redux-state, will redux-state become very big if all trace reponses are stored in it
//if tree
// d3.select("#chart").datum(tree).call(chart)
const resetZoom = () => { const resetZoom = () => {
chart.resetZoom(); chart.resetZoom();
} }
@ -136,11 +99,9 @@ const _TraceGraph = (props: TraceGraphProps) => {
} }
const mapStateToProps = (state: StoreState): { traceItem: spansWSameTraceIDResponse } => { const mapStateToProps = (state: StoreState): { traceItem: spansWSameTraceIDResponse } => {
// console.log(state);
return { traceItem: state.traceItem }; return { traceItem: state.traceItem };
}; };
// the name mapStateToProps is only a convention
// take state and map it to props which are accessible inside this component
export const TraceGraph = connect(mapStateToProps, { export const TraceGraph = connect(mapStateToProps, {
fetchTraceItem: fetchTraceItem, fetchTraceItem: fetchTraceItem,

View File

@ -29,7 +29,6 @@ const _TraceGraphColumn = (props: TraceGraphColumnProps) => {
sortDirections: ['descend', 'ascend'], sortDirections: ['descend', 'ascend'],
render: (value: number) => (new Date(Math.round(value/1000))).toUTCString() render: (value: number) => (new Date(Math.round(value/1000))).toUTCString()
// render: (value: number) => (new Date(Math.round(value/1000))).toLocaleDateString()+' '+(new Date(Math.round(value/1000))).toLocaleTimeString()
}, },
{ {
title: 'Duration (in ms)', title: 'Duration (in ms)',
@ -49,12 +48,6 @@ const _TraceGraphColumn = (props: TraceGraphColumnProps) => {
let dataSource :TableDataSourceItem[] = []; let dataSource :TableDataSourceItem[] = [];
// PNOTE - Define new array
// if (props.traces.data.length > 0)
// {
// props.traces.data[0].spans.map((item: spanItem, index ) => dataSource.push({startTime: item.startTime, operationName: item.operationName , duration: item.duration, key:index.toString()}) );
// }
if (props.traces[0].events.length > 0) { if (props.traces[0].events.length > 0) {
props.traces[0].events.map((item: (number|string|string[]|pushDStree[])[], index ) => { props.traces[0].events.map((item: (number|string|string[]|pushDStree[])[], index ) => {
@ -66,7 +59,6 @@ const _TraceGraphColumn = (props: TraceGraphColumnProps) => {
return ( return (
<div> <div>
{/* <div>Tracing Graph Column Page</div> */}
<Table dataSource={dataSource} columns={columns} size="middle"/>; <Table dataSource={dataSource} columns={columns} size="middle"/>;
</div> </div>
); );
@ -74,10 +66,8 @@ const _TraceGraphColumn = (props: TraceGraphColumnProps) => {
} }
const mapStateToProps = (state: StoreState): { traces: traceResponseNew } => { const mapStateToProps = (state: StoreState): { traces: traceResponseNew } => {
// console.log(state);
return { traces : state.traces }; return { traces : state.traces };
}; };
// the name mapStateToProps is only a convention
// take state and map it to props which are accessible inside this component
export const TraceGraphColumn = connect(mapStateToProps)(_TraceGraphColumn); export const TraceGraphColumn = connect(mapStateToProps)(_TraceGraphColumn);

View File

@ -51,7 +51,6 @@ const _TraceList = (props: TraceListProps) => {
// new Date() assumes input in milliseconds. Start Time stamp returned by druid api for span list is in ms // new Date() assumes input in milliseconds. Start Time stamp returned by druid api for span list is in ms
// render: (value: number) => (new Date(Math.round(value/1000))).toLocaleDateString()+' '+(new Date(Math.round(value/1000))).toLocaleTimeString()
}, },
{ {
title: 'Duration (in ms)', title: 'Duration (in ms)',
@ -83,9 +82,7 @@ const _TraceList = (props: TraceListProps) => {
if (typeof props.traces[0]!== 'undefined' && props.traces[0].events.length > 0) { if (typeof props.traces[0]!== 'undefined' && props.traces[0].events.length > 0) {
//PNOTE - Template literal should be wrapped in curly braces for it to be evaluated //PNOTE - Template literal should be wrapped in curly braces for it to be evaluated
// return props.traces.data[0].spans.map((item: spanItem) => <div key={item.spanID}>Span ID is {item.spanID} --- Trace id is <NavLink to={`traces/${item.traceID}`}>{item.traceID}</NavLink></div>)
//dataSource.push({spanid:{item.spanID}},traceid:{item.traceID}}
//Populating dataSourceArray
props.traces[0].events.map((item: (number|string|string[]|pushDStree[])[], index ) => { props.traces[0].events.map((item: (number|string|string[]|pushDStree[])[], index ) => {
if (typeof item[0] === 'number' && typeof item[4] === 'string' && typeof item[6] === 'string' && typeof item[1] === 'string' && typeof item[2] === 'string' ) if (typeof item[0] === 'number' && typeof item[4] === 'string' && typeof item[6] === 'string' && typeof item[1] === 'string' && typeof item[2] === 'string' )
dataSource.push({startTime: item[0], operationName: item[4] , duration:parseInt(item[6]), spanid:item[1], traceid:item[2], key:index.toString()}); dataSource.push({startTime: item[0], operationName: item[4] , duration:parseInt(item[6]), spanid:item[1], traceid:item[2], key:index.toString()});
@ -96,13 +93,11 @@ const _TraceList = (props: TraceListProps) => {
return <Table dataSource={dataSource} columns={columns} size="middle"/>; return <Table dataSource={dataSource} columns={columns} size="middle"/>;
} else } else
{ {
//return <Skeleton active />;
return <div> No spans found for given filter!</div> return <div> No spans found for given filter!</div>
} }
};// end of renderTraces };// end of renderTraces
// console.log(props.traces.data);
return( return(
<div> <div>
<div>List of traces with spanID</div> <div>List of traces with spanID</div>
@ -110,23 +105,11 @@ const _TraceList = (props: TraceListProps) => {
</div> </div>
) )
// PNOTE - code snippet -
// return props.traces.data.map((item) => {
// return (
// <div key={item.traceID}>
// {item.traceID}
// </div>
// );
// });
} }
const mapStateToProps = (state: StoreState): { traces: traceResponseNew } => { const mapStateToProps = (state: StoreState): { traces: traceResponseNew } => {
// console.log(state);
return { traces : state.traces }; return { traces : state.traces };
}; };
// the name mapStateToProps is only a convention
// take state and map it to props which are accessible inside this component
export const TraceList = connect(mapStateToProps, { export const TraceList = connect(mapStateToProps, {
fetchTraces: fetchTraces, fetchTraces: fetchTraces,

View File

@ -62,7 +62,6 @@ const _UsageExplorer = (props: UsageExplorerProps) => {
return( return(
<React.Fragment> <React.Fragment>
{/* <div>Usage Explorer </div> */}
{/* PNOTE - TODO - Keep it in reponsive row column tab */} {/* PNOTE - TODO - Keep it in reponsive row column tab */}
<Card style={{ width: "50%" , margin:20 }} bodyStyle={{padding:20 }}> <Card style={{ width: "50%" , margin:20 }} bodyStyle={{padding:20 }}>
<Bar data={data} options={options} /> <Bar data={data} options={options} />
@ -73,11 +72,8 @@ const _UsageExplorer = (props: UsageExplorerProps) => {
} }
const mapStateToProps = (state: StoreState): { usageData: usageDataItem[], globalTime: GlobalTime } => { const mapStateToProps = (state: StoreState): { usageData: usageDataItem[], globalTime: GlobalTime } => {
// console.log(state);
return { usageData : state.usageDate, globalTime: state.globalTime }; return { usageData : state.usageDate, globalTime: state.globalTime };
}; };
// the name mapStateToProps is only a convention
// take state and map it to props which are accessible inside this component
export const UsageExplorer = connect(mapStateToProps, { export const UsageExplorer = connect(mapStateToProps, {
getUsageData: getUsageData, getUsageData: getUsageData,

View File

@ -13,7 +13,7 @@ import { reducers } from './reducers';
const store = createStore(reducers, applyMiddleware(thunk)) const store = createStore(reducers, applyMiddleware(thunk))
const themes = { const themes = {
dark: `${process.env.PUBLIC_URL}/dark-theme.css`, // How is process.env.PUBLIC_URL defined? - even when we are not using gitlab ci dark: `${process.env.PUBLIC_URL}/dark-theme.css`,
light: `${process.env.PUBLIC_URL}/light-theme.css`, light: `${process.env.PUBLIC_URL}/light-theme.css`,
}; };

View File

@ -32,4 +32,3 @@ export const reducers = combineReducers<StoreState>({
filteredTraceMetrics:filteredTraceMetricsReducer, filteredTraceMetrics:filteredTraceMetricsReducer,
}); });
//input state should ideally be in component state rather than redux application state

View File

@ -1,6 +1,5 @@
import { ActionTypes, TraceFilters, updateInputTagAction, updateTraceFiltersAction } from '../actions'; import { ActionTypes, TraceFilters, updateInputTagAction, updateTraceFiltersAction } from '../actions';
// does each reducer process individual states? Yes. Slice of the state
export const traceFiltersReducer = (state:TraceFilters = {'service':'', 'tags':[],'operation':'','latency':{'min':'','max':''}}, action: updateTraceFiltersAction) => { export const traceFiltersReducer = (state:TraceFilters = {'service':'', 'tags':[],'operation':'','latency':{'min':'','max':''}}, action: updateTraceFiltersAction) => {
switch (action.type){ switch (action.type){

View File

@ -2,8 +2,6 @@ import { ActionTypes, Action, traceResponseNew, spanList, spansWSameTraceIDRespo
// PNOTE - Initializing is a must for state variable otherwise it gives an error in reducer // PNOTE - Initializing is a must for state variable otherwise it gives an error in reducer
var spanlistinstance :spanList ={ events: [], segmentID: '', columns: []} ; var spanlistinstance :spanList ={ events: [], segmentID: '', columns: []} ;
// let traceResponseNew_instance : traceResponseNew;
// traceResponseNew_instance = [spanlistinstance];
export const tracesReducer = (state: traceResponseNew = {"0": spanlistinstance} , action: Action) => { export const tracesReducer = (state: traceResponseNew = {"0": spanlistinstance} , action: Action) => {
switch (action.type) { switch (action.type) {
case ActionTypes.fetchTraces: case ActionTypes.fetchTraces:

View File

@ -12,15 +12,12 @@ export const spanToTreeUtil = (spanlist :span[]) :pushDStree => {
// let spans :spanItem[]= trace.spans; // let spans :spanItem[]= trace.spans;
if(spanlist){ if(spanlist){
// console.log('spans',spans.length)
//
// console.log(processes)
// Create a dict with spanIDs as keys // Create a dict with spanIDs as keys
// PNOTE // PNOTE
// Can we now assign different strings as id - Yes // Can we now assign different strings as id - Yes
// https://stackoverflow.com/questions/15877362/declare-and-initialize-a-dictionary-in-typescript // https://stackoverflow.com/questions/15877362/declare-and-initialize-a-dictionary-in-typescript
// SPAN TREE processing is
let mapped_array : {[id: string] : span;} = {}; let mapped_array : {[id: string] : span;} = {};
@ -28,7 +25,6 @@ export const spanToTreeUtil = (spanlist :span[]) :pushDStree => {
mapped_array[spanlist[i][1]] = spanlist[i]; mapped_array[spanlist[i][1]] = spanlist[i];
mapped_array[spanlist[i][1]][10] = []; mapped_array[spanlist[i][1]][10] = [];
} }
// console.log('mapped_array',mapped_array)
for(let id in mapped_array){ for(let id in mapped_array){
let child_span = mapped_array[id]; let child_span = mapped_array[id];
@ -70,8 +66,6 @@ export const spanToTreeUtil = (spanlist :span[]) :pushDStree => {
let references :RefItem[] = []; let references :RefItem[] = [];
refArray.forEach(element => { refArray.forEach(element => {
// element = element.replaceAll("=", ":")
// let refObj = JSON.parse(element)
element = element.replaceAll("{", "").replaceAll("}", "").replaceAll(" ", "") element = element.replaceAll("{", "").replaceAll("}", "").replaceAll(" ", "")
let arr = element.split(",") let arr = element.split(",")
let refItem = {"traceID": "", "spanID": "", "refType": ""}; let refItem = {"traceID": "", "spanID": "", "refType": ""};