[WIP] node js performance monitor

This commit is contained in:
Nilanshu
2021-04-05 09:53:57 +05:30
parent d1426b9f5a
commit cb6596b8be
21 changed files with 24538 additions and 22595 deletions

View File

@@ -16,7 +16,14 @@
min-width: 250px;
font-size: 14px;
}
.db-select-in {
width: 150px;
font-size: 14px;
}
.db-select-in-flexible {
min-width: 150px;
font-size: 14px;
}
.db-select-ne {
width: 120px;
font-size: 14px;
@@ -42,6 +49,7 @@
.db-select-nw > div,
.db-select-ne > div,
.db-select-pr > div,
.db-select-in > div,
.db-select-nw-300 > div {
-webkit-flex-flow: unset;
-ms-flex-flow: unset;
@@ -50,7 +58,8 @@
}
.db-select-nw > span + div,
.db-select-pr > span + div {
.db-select-pr > span + div,
.db-select-in > span + div {
border: none;
box-shadow: 0 0 0 1px rgba(50, 50, 93, 0), 0 0 0 1px rgba(50, 151, 211, 0.2),
0 0 0 2px rgba(50, 151, 211, 0.25), 0 1px 1px rgba(0, 0, 0, 0.08);

View File

@@ -0,0 +1,17 @@
export function setStartDate(date) {
return function(dispatch) {
dispatch({
type: 'SET_START_DATE',
payload: date,
});
};
}
export function setEndDate(date) {
return function(dispatch) {
dispatch({
type: 'SET_END_DATE',
payload: date,
});
};
}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import originalMoment from 'moment';
@@ -16,9 +16,13 @@ function DateTimeRangePicker({
style,
}) {
const currentDate = moment();
const [key, setkey] = useState(0);
useEffect(() => {
setkey(key + 1);
}, [currentDateRange.startDate, currentDateRange.endDate]);
return (
<div>
<form id={formId}>
<form id={formId} key={key}>
<ShouldRender if={currentDateRange}>
<div className="db-DateRangeInputWithComparison">
<div
@@ -77,10 +81,10 @@ function DateTimeRangePicker({
DateTimeRangePicker.displayName = 'DateTimeRangePicker';
DateTimeRangePicker.propTypes = {
handleStartDateTimeChange: PropTypes.func,
handleEndDateTimeChange: PropTypes.func,
currentDateRange: PropTypes.object,
formId: PropTypes.string,
handleEndDateTimeChange: PropTypes.func,
handleStartDateTimeChange: PropTypes.func,
style: PropTypes.object,
};

View File

@@ -0,0 +1,162 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Spinner } from '../basic/Loader';
import {
ResponsiveContainer,
AreaChart as Chart,
Area,
CartesianGrid,
Tooltip,
YAxis,
XAxis,
} from 'recharts';
import * as _ from 'lodash';
import moment from 'moment';
const noDataStyle = {
textAlign: 'center',
flexBasis: 1,
};
const CustomTooltip = ({ active, payload }) => {
if (active) {
return (
<div className="custom-tooltip">
{payload[0].payload.name ? (
<>
<h3>{payload[0].payload.name}</h3>
<p className="label">{`${payload[0].name} : ${payload[0].payload.display}`}</p>
</>
) : (
<h3>No data available</h3>
)}
</div>
);
}
return null;
};
CustomTooltip.displayName = 'CustomTooltip';
CustomTooltip.propTypes = {
active: PropTypes.bool,
payload: PropTypes.array,
};
class PerformanceChart extends Component {
calcPercent = (val, total) => {
val = parseFloat(val);
total = parseFloat(total);
if (isNaN(val) || isNaN(total)) {
return 0;
}
if (!total || total === 0) {
return 0;
}
if (!val || val === 0) {
return 0;
}
return (val / total) * 100;
};
parseDate(a) {
return new Date(a).toLocaleString();
}
getTime(a) {
return moment(a).format('LT');
}
render() {
const { type, data, name, symbol, requesting } = this.props;
let processedData = [{ display: '', name: '', v: '' }];
if (requesting) {
return (
<div style={noDataStyle}>
<div
className="Box-root Flex-flex Flex-alignItems--center Flex-justifyContent--center"
style={{
textAlign: 'center',
width: '100%',
fontSize: 14,
fontWeight: '500',
margin: 0,
color: '#4c4c4c',
lineHeight: 1.6,
}}
>
<Spinner style={{ stroke: '#8898aa' }} />{' '}
<span style={{ width: 10 }} />
We&apos;re currently in the process of collecting data
for this monitor. <br />
More info will be available in few minutes
</div>
</div>
);
}
if (data && data.length > 0) {
processedData = data.map(a => {
return {
name: a.intervalDate || this.parseDate(a.createdAt),
v: a.value,
display: `${Math.round(a.value || 0)} ${symbol || 'ms'}`,
xData: a.createdAt,
};
});
}
return (
<ResponsiveContainer width="100%" height={200}>
<Chart data={processedData}>
<Tooltip content={<CustomTooltip />} />
<CartesianGrid vertical={false} strokeDasharray="3 3" />
<YAxis
type="number"
domain={[0, 'dataMax']}
tickCount={5}
axisLine={false}
tickLine={false}
mirror={true}
tickMargin={-10}
/>
<XAxis
axisLine={false}
tickLine={false}
padding={{ left: 30 }}
dataKey={'xData'}
tickFormatter={this.getTime}
tickMargin={10}
/>
<Area
type="linear"
isAnimationActive={false}
name={_.startCase(_.toLower(`${name} ${type}`))}
dataKey="v"
stroke="#000000"
strokeWidth={1.5}
fill="#e2e1f2"
/>
</Chart>
</ResponsiveContainer>
);
}
}
PerformanceChart.displayName = 'PerformanceChart';
PerformanceChart.propTypes = {
data: PropTypes.array,
type: PropTypes.string.isRequired,
name: PropTypes.string,
symbol: PropTypes.string,
requesting: PropTypes.bool,
};
function mapStateToProps(state) {
return {
requesting: state.monitor.fetchMonitorLogsRequest,
};
}
export default connect(mapStateToProps)(PerformanceChart);

View File

@@ -363,9 +363,7 @@ class CreateIncident extends Component {
]}
onChange={(
event,
newValue,
previousValue,
name
newValue
) => {
this.substituteVariables(
newValue,

View File

@@ -186,7 +186,10 @@ class SideNav extends Component {
/project\/([A-Za-z0-9-]+)\/([A-Za-z0-9-]+)\/error-tracker/
) ||
location.pathname.match(
/project\/([A-Za-z0-9-]+)\/([A-Za-z0-9-]+)\/settings\/basic/
/project\/([A-Za-z0-9-]+)\/([0-9]|[a-z])*\/performance-monitor/
) ||
location.pathname.match(
/project\/([A-Za-z0-9-]+)\/([0-9]|[a-z])*\/settings\/basic/
) ||
location.pathname.match(
/project\/([A-Za-z0-9-]+)\/([A-Za-z0-9-]+)\/settings\/advanced/

View File

@@ -0,0 +1,213 @@
import PropTypes from 'prop-types';
import React, { Component, Fragment } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { setStartDate, setEndDate } from '../../actions/performanceMonitoring';
import PerformanceChart from '../basic/performanceChart';
import DateTimeRangePicker from '../basic/DateTimeRangePicker';
import moment from 'moment';
//import ShouldRender from '../../components/basic/ShouldRender';
export class ChartComponent extends Component {
render() {
const {
heading,
title,
subHeading,
startDate,
endDate,
setStartDate,
setEndDate,
} = this.props;
const status = {
display: 'inline-block',
borderRadius: '2px',
height: '8px',
width: '8px',
margin: '0 8px 1px 0',
backgroundColor: 'rgb(117, 211, 128)',
};
return (
<div className="Box-root Margin-bottom--12">
<div className="bs-ContentSection Card-root Card-shadow--medium">
<div className="Box-root">
<div className="bs-ContentSection-content Box-root Box-divider--surface-bottom-1 Flex-flex Flex-alignItems--center Flex-justifyContent--spaceBetween Padding-horizontal--20 Padding-vertical--16">
<div className="Box-root">
<span className="Text-color--inherit Text-display--inline Text-fontSize--16 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
<span>{heading}</span>
</span>
<p>
<span>{subHeading}</span>
</p>
<div
className="db-Trends-controls"
style={{ marginTop: '15px' }}
>
<div className="db-Trends-timeControls">
<DateTimeRangePicker
currentDateRange={{
startDate,
endDate,
}}
handleStartDateTimeChange={val =>
setStartDate(moment(val))
}
handleEndDateTimeChange={val =>
setEndDate(moment(val))
}
formId={`performanceMonitoringDateTime-${heading}`}
/>
</div>
</div>
</div>
<div className="db-ListViewItem-cellContent Box-root">
<span className="db-ListViewItem-text Text-color--cyan Text-display--inline Text-fontSize--14 Text-fontWeight--medium Text-lineHeight--20 Text-typeface--base Text-wrap--wrap">
<div className="Box-root Margin-right--16">
<span className="Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap Text-color--dark">
<span>1.5 ms</span>
</span>
</div>
</span>
<div className="Box-root Flex">
<div className="Box-root Flex-flex">
<div className="db-RadarRulesListUserName Box-root Flex-flex Flex-alignItems--center Flex-direction--row Flex-justifyContent--flexStart">
<div className="Box-root Flex-inlineFlex Flex-alignItems--center">
<span className="Text-display--inline Text-fontSize--14 Text-fontWeight--regular Text-lineHeight--20 Text-typeface--base Text-wrap--wrap Text-color--dark">
<span>App Server</span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div
className="bs-ContentSection-content Box-root Box-background--offset Box-divider--surface-bottom-1 Padding-vertical--2"
style={{ boxShadow: 'none' }}
>
<div>
<div className="bs-Fieldset-wrapper Box-root Margin-bottom--2">
<div
style={{
margin: '30px 20px 10px 20px',
}}
>
<PerformanceChart
type={`url`}
data={[
{
createdAt:
'2020-11-05T07:40:57.765+00:00',
value: 50,
},
{
createdAt:
'2020-11-06T07:40:57.765+00:00',
value: 10,
},
{
createdAt:
'2020-11-07T07:40:57.765+00:00',
value: 100,
},
{
createdAt:
'2020-11-08T07:40:57.765+00:00',
value: 80,
},
{
createdAt:
'2020-11-09T07:40:57.765+00:00',
value: 58,
},
{
createdAt:
'2020-11-09T08:40:57.765+00:00',
value: 70,
},
{
createdAt:
'2020-11-09T09:40:57.765+00:00',
value: 25,
},
{
createdAt:
'2020-11-09T10:40:57.765+00:00',
value: 40,
},
{
createdAt:
'2020-11-10T07:40:57.765+00:00',
value: 39,
},
{
createdAt:
'2020-11-11T07:40:57.765+00:00',
value: 68,
},
]}
name={'response time'}
symbol="ms"
requesting={false}
/>
</div>
<div className="bs-ContentSection-content Box-root Box-divider--surface-bottom-1 Flex-flex Flex-alignItems--center Flex-justifyContent--spaceBetween Padding-horizontal--20">
<div className="Box-root">
{title.map((t, i) => (
<Fragment key={i}>
<span className="Text-color--inherit Text-display--inline Text-fontSize--16 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
<span
style={status}
></span>
<span>{t}</span>
</span>
<span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span>
</Fragment>
))}
</div>
</div>
</div>
</div>
</div>
<div className="bs-ContentSection-footer bs-ContentSection-content Box-root Box-background--white Flex-flex Flex-alignItems--center Flex-justifyContent--spaceBetween Padding-horizontal--20 Padding-vertical--12">
<div className="bs-Tail-copy">
<div className="Box-root Flex-flex Flex-alignItems--stretch Flex-direction--row Flex-justifyContent--flexStart">
<div style={{ height: '20px' }}></div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
ChartComponent.displayName = 'ChartComponent';
ChartComponent.propTypes = {
endDate: PropTypes.any,
heading: PropTypes.any,
setEndDate: PropTypes.any,
setStartDate: PropTypes.any,
startDate: PropTypes.any,
subHeading: PropTypes.any,
title: PropTypes.shape({
map: PropTypes.func,
}),
};
const mapDispatchToProps = dispatch =>
bindActionCreators({ setStartDate, setEndDate }, dispatch);
function mapStateToProps(state) {
return {
currentProject: state.project.currentProject,
startDate: state.performanceMonitoring.dates.startDate,
endDate: state.performanceMonitoring.dates.endDate,
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ChartComponent);

View File

@@ -0,0 +1,262 @@
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { getMonitorLogs } from '../../actions/monitor';
//import MonitorLogsList from '../monitor/MonitorLogsList';
import Select from '../../components/basic/react-select-fyipe';
//import ShouldRender from '../../components/basic/ShouldRender';
//import DateTimeRangePicker from '../basic/DateTimeRangePicker';
//import moment from 'moment';
//const endDate = moment();
//const startDate = moment().subtract(30, 'd');
export class PerformanceView extends Component {
/* constructor(props) {
super(props);
this.props = props;
this.state = {
probeValue: { value: '', label: 'All Probes' },
startDate: startDate,
endDate: endDate,
page: 1,
};
}
prevClicked = (monitorId, skip, limit) => {
const { currentProject, getMonitorLogs } = this.props;
const incidentId = this.props.incidentId ? this.props.incidentId : null;
const start = incidentId ? '' : this.state.startDate.clone().utc();
const end = incidentId ? '' : this.state.endDate.clone().utc();
getMonitorLogs(
currentProject._id,
monitorId,
skip ? parseInt(skip, 10) - 10 : 10,
limit,
start,
end,
this.state.probeValue.value,
incidentId,
this.props.monitorType
);
this.setState({ page: this.state.page - 1 });
if (window.location.href.indexOf('localhost') <= -1) {
this.context.mixpanel.track('Previous Incident Requested', {
projectId: currentProject._id,
});
}
};
nextClicked = (monitorId, skip, limit) => {
const { currentProject, getMonitorLogs } = this.props;
const incidentId = this.props.incidentId ? this.props.incidentId : null;
const start = incidentId ? '' : this.state.startDate.clone().utc();
const end = incidentId ? '' : this.state.endDate.clone().utc();
getMonitorLogs(
currentProject._id,
monitorId,
skip ? parseInt(skip, 10) + 10 : 10,
limit,
start,
end,
this.state.probeValue.value,
incidentId,
this.props.monitorType
);
this.setState({ page: this.state.page + 1 });
if (window.location.href.indexOf('localhost') <= -1) {
this.context.mixpanel.track('Next Incident Requested', {
projectId: currentProject._id,
});
}
};
handleStartDateTimeChange = val => {
const startDate = moment(val);
this.handleDateChange(startDate, this.state.endDate);
};
handleEndDateTimeChange = val => {
const endDate = moment(val);
this.handleDateChange(this.state.startDate, endDate);
};
handleDateChange = (startDate, endDate) => {
const { currentProject, getMonitorLogs, monitorId } = this.props;
this.setState({
startDate,
endDate,
});
getMonitorLogs(
currentProject._id,
monitorId,
0,
10,
startDate.clone().utc(),
endDate.clone().utc(),
this.state.probeValue.value,
null,
this.props.monitorType
);
};
handleTimeChange = (startDate, endDate) => {
const { currentProject, getMonitorLogs, monitorId } = this.props;
getMonitorLogs(
currentProject._id,
monitorId,
0,
10,
startDate.clone().utc(),
endDate.clone().utc(),
this.state.probeValue.value,
null,
this.props.monitorType
);
};
handleProbeChange = data => {
this.setState({ probeValue: data });
const { currentProject, getMonitorLogs, monitorId } = this.props;
getMonitorLogs(
currentProject._id,
monitorId,
0,
10,
this.state.startDate.clone().utc(),
this.state.endDate.clone().utc(),
data.value,
null,
this.props.monitorType
);
};*/
render() {
/* const probeOptions =
this.props.probes && this.props.probes.length > 0
? this.props.probes.map(p => {
return { value: p._id, label: p.probeName };
})
: [];
probeOptions.unshift({ value: '', label: 'All Probes' });*/
return (
<div
className="Box-root Card-shadow--medium"
tabIndex="0"
onKeyDown={this.handleKeyBoard}
>
<div className="db-Trends-header Box-background--white Box-divider--surface-bottom-1">
<div
className="db-Trends-controls"
style={{ justifyContent: 'space-evenly' }}
>
<div
className="bs-Fieldset-row"
style={{ padding: '1px' }}
>
<label
className="bs-Fieldset-label"
style={{ flex: '1' }}
>
Transaction Type
</label>
<div className="bs-Fieldset-fields">
<Select
name="transaction_type"
value={{ value: '', label: 'All' }}
placeholder="Web"
className="db-select-in"
id="transaction_type"
isDisabled={false}
style={{ height: '28px' }}
options={[
{
value: 'a',
label: 'hello',
},
]}
/>
</div>
</div>
<div
className="bs-Fieldset-row"
style={{ padding: '1px' }}
>
<label
className="bs-Fieldset-label"
style={{ flex: '1' }}
>
Compare With
</label>
<div className="bs-Fieldset-fields">
<Select
name="compare_with"
value={{ value: '', label: 'All' }}
placeholder="All"
className="db-select-in"
id="compare_with"
isDisabled={false}
style={{ height: '28px' }}
options={[
{
value: 'a',
label: 'hello',
},
]}
/>
</div>
</div>
<div
className="bs-Fieldset-row"
style={{ padding: '1px' }}
>
<label
className="bs-Fieldset-label"
style={{ flex: '1' }}
>
Instances
</label>
<div className="bs-Fieldset-fields">
<Select
name="instances"
value={{ value: '', label: 'All' }}
placeholder="All"
className="db-select-in"
id="instances"
isDisabled={false}
style={{ height: '28px' }}
options={[
{
value: 'a',
label: 'hello',
},
]}
/>
</div>
</div>
</div>
</div>
</div>
);
}
}
PerformanceView.displayName = 'PerformanceView';
PerformanceView.propTypes = {};
const mapDispatchToProps = dispatch =>
bindActionCreators({ getMonitorLogs }, dispatch);
function mapStateToProps(state, props) {
const monitorId = props.monitorId ? props.monitorId : null;
return {
monitorLogs: monitorId ? state.monitor.monitorLogs[monitorId] : {},
probes: state.probe.probes.data,
currentProject: state.project.currentProject,
};
}
PerformanceView.contextTypes = {
mixpanel: PropTypes.object,
};
export default connect(mapStateToProps, mapDispatchToProps)(PerformanceView);

View File

@@ -0,0 +1,49 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
//import PropTypes from 'prop-types';
import { getMonitorLogs } from '../../actions/monitor';
import ChartComponent from './ChartComponent';
export class WebTransactionsChart extends Component {
render() {
const { heading, title, subHeading } = this.props;
return (
<div
className="Box-root Card-shadow--medium"
tabIndex="0"
onKeyDown={this.handleKeyBoard}
style={{ marginTop: '10px' }}
>
<ChartComponent
heading={heading}
title={title}
subHeading={subHeading}
/>
</div>
);
}
}
WebTransactionsChart.displayName = 'WebTransactionsChart';
WebTransactionsChart.propTypes = {
heading: PropTypes.any,
subHeading: PropTypes.any,
title: PropTypes.any,
};
const mapDispatchToProps = dispatch =>
bindActionCreators({ getMonitorLogs }, dispatch);
function mapStateToProps(state) {
return {
currentProject: state.project.currentProject,
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(WebTransactionsChart);

View File

@@ -0,0 +1,173 @@
import React, { Component } from 'react';
import BreadCrumbItem from '../components/breadCrumb/BreadCrumbItem';
import Dashboard from '../components/Dashboard';
import getParentRoute from '../utils/getParentRoute';
import Fade from 'react-reveal/Fade';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import PerformanceView from '../components/performanceMonitor/PerformanceView';
import WebTransactionsChart from '../components/performanceMonitor/WebTransactionsChart';
//import ShouldRender from '../../components/basic/ShouldRender';
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
class PerformanceMonitoring extends Component {
state = {
tabIndex: 0,
};
tabSelected = index => {
const tabSlider = document.getElementById('tab-slider');
tabSlider.style.transform = `translate(calc(${tabSlider.offsetWidth}px*${index}), 0px)`;
this.setState({
tabIndex: index,
});
};
render() {
const {
location: { pathname },
component,
} = this.props;
const componentName = component ? component.name : '';
return (
<Dashboard>
<Fade>
<BreadCrumbItem
route={getParentRoute(pathname)}
name={componentName}
/>
<BreadCrumbItem
route={pathname}
name="Performance Monitoring"
/>
<Tabs
selectedTabClassName={'custom-tab-selected'}
onSelect={tabIndex => this.tabSelected(tabIndex)}
selectedIndex={this.state.tabIndex}
>
<div className="Flex-flex Flex-direction--columnReverse">
<TabList
id="customTabList"
className={'custom-tab-list'}
>
<Tab className={'custom-tab custom-tab-2'}>
Charts
</Tab>
<Tab className={'custom-tab custom-tab-2'}>
Data
</Tab>
<div
id="tab-slider"
className="custom-tab-2"
></div>
</TabList>
</div>
<TabPanel>
<Fade>
<div className="Box-root Margin-bottom--12">
<div>
<div>
<PerformanceView />
</div>
</div>
<div>
<div>
<WebTransactionsChart
heading="Web Transactions Time"
title={[
'Node.js',
'Response time',
]}
subHeading="shows graph of web transactions initiated through http requests"
/>
</div>
</div>
<div>
<div>
<WebTransactionsChart
heading="Throughput"
title={['Web.throughput']}
subHeading="shows graph of number of web transactions per minute"
/>
</div>
</div>
<div>
<div>
<WebTransactionsChart
heading="Error rate"
title={[
'Web errors',
'All errors',
]}
subHeading="shows graph of errors occuring per minute"
/>
</div>
</div>
<div>
<div>
<WebTransactionsChart
heading="Apdex Score"
title={[
'App server',
'End user',
]}
subHeading="shows graph of satisfied requests against total requests"
/>
</div>
</div>
</div>
</Fade>
</TabPanel>
<TabPanel>
<Fade>
<div className="Box-root Margin-bottom--12">
<div>
<div>
<WebTransactionsChart
heading="Web Transactions Time"
title={['Node.js']}
subHeading="shows graph of web transactions initiated through http requests"
/>
</div>
</div>
</div>
</Fade>
</TabPanel>
</Tabs>
</Fade>
</Dashboard>
);
}
}
PerformanceMonitoring.displayName = 'PerformanceMonitoring';
const mapDispatchToProps = dispatch => {
return bindActionCreators({}, dispatch);
};
const mapStateToProps = (state, ownProps) => {
const { componentId } = ownProps.match.params;
const currentProject = state.project.currentProject;
let component;
state.component.componentList.components.forEach(item => {
item.components.forEach(c => {
if (String(c._id) === String(componentId)) {
component = c;
}
});
});
return {
currentProject,
component,
componentId,
};
};
PerformanceMonitoring.propTypes = {
component: PropTypes.shape({
name: PropTypes.any,
}),
location: PropTypes.any,
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(PerformanceMonitoring);

View File

@@ -57,6 +57,7 @@ import ComponentSettingsAdvanced from './ComponentSettingsAdvanced';
import CallRouting from './CallRouting';
import DomainSettings from './DomainSettings';
import Groups from './Group';
import PerformanceMonitoring from './PerformanceMonitoring';
export default {
ChangePassword,
@@ -107,4 +108,5 @@ export default {
CallRouting,
DomainSettings,
Groups,
PerformanceMonitoring,
};

View File

@@ -46,6 +46,7 @@ import customField from './customField';
import monitorCustomField from './monitorCustomField';
import callRouting from './callRouting';
import groups from './groups';
import performanceMonitoring from './performanceMonitoring';
const appReducer = combineReducers({
routing: routerReducer,
form: formReducer,
@@ -94,6 +95,7 @@ const appReducer = combineReducers({
customField,
monitorCustomField,
callRouting,
performanceMonitoring,
});
export default (state, action) => {

View File

@@ -0,0 +1,31 @@
import moment from 'moment';
const initialState = {
dates: {
startDate: moment().subtract(30, 'd'),
endDate: moment(),
},
};
export default (state = initialState, action) => {
switch (action.type) {
case 'SET_START_DATE':
return Object.assign({}, state, {
dates: {
...state.dates,
startDate: action.payload,
},
});
case 'SET_END_DATE':
return Object.assign({}, state, {
dates: {
...state.dates,
endDate: action.payload,
},
});
default:
return state;
}
};

View File

@@ -52,6 +52,7 @@ const {
CallRouting,
DomainSettings,
Groups,
PerformanceMonitoring,
} = pages;
export const groups = [
@@ -186,6 +187,18 @@ export const groups = [
},
],
},
{
title: 'Performance Monitoring',
path:
'/dashboard/project/:slug/:componentId/performance-monitor',
icon: 'errorTracking',
visible: true,
exact: true,
component: PerformanceMonitoring,
index: 8,
shortcut: 'f+p',
subRoutes: [],
},
{
title: 'Security',
path:
@@ -245,7 +258,7 @@ export const groups = [
shortcut: 'r+n',
},
],
index: 7,
index: 9,
},
{
title: 'Component Settings',
@@ -255,7 +268,7 @@ export const groups = [
exact: true,
component: ComponentSettings,
shortcut: 'f+s',
index: 8,
index: 10,
subRoutes: [
{
title: 'Basic',

31171
js-sdk/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,5 +4,6 @@ import ErrorTracker from './tracker.js';
// we need to build the server-monitor project into the build folder then point to it there
// This way we won't worry about whether we are using module/commonjs syntax
import ServerMonitor from '../build/server-monitor/lib/api';
import PerformanceMonitor from './performanceMonitor.js';
export default { Logger, ErrorTracker, ServerMonitor };
export default { Logger, ErrorTracker, ServerMonitor ,PerformanceMonitor};

View File

@@ -0,0 +1,89 @@
/* eslint-disable no-console */
/*eslint-disable no-unused-vars*/
import Module from 'module';
import FyipeTimelineManager from './timelineManager';
import Util from './util';
import Http from 'http';
import Https from 'https';
import mongooseListener from './utils/mongoose';
class PerformanceMonitor {
#BASE_URL = 'http://localhost:3002/api'; // TODO proper base url config
#timelineObj;
#currentEventId;
#utilObj;
#isWindow;
#options;
constructor(eventId, isWindow, options) {
this.#options = options;
this.#isWindow = isWindow;
this.#timelineObj = new FyipeTimelineManager(options);
this.#utilObj = new Util();
this.#currentEventId = eventId;
if (!this.#isWindow) {
this._setUpHttpsListener();
this._setUpDataBaseListener();
this._setUpIncomingListener();
}
}
_setUpHttpsListener() {
override(Http);
override(Https);
const _this = this;
function override(module) {
const original = module.request;
function wrapper(outgoing) {
// Store a call to the original in req
const req = original.apply(this, arguments);
const emit = req.emit;
const startHrTime = process.hrtime();
req.emit = function(eventName, response) {
switch (eventName) {
case 'response': {
response.on('end', () => {
const elapsedHrTime = process.hrtime(
startHrTime
);
const elapsedTimeInMs =
elapsedHrTime[0] * 1000 +
elapsedHrTime[1] / 1e6;
console.log('outgoing', elapsedTimeInMs);
});
}
}
return emit.apply(this, arguments);
};
// return the original call
return req;
}
module.request = wrapper;
}
}
_logHttpRequestEvent(content, type) {
const timelineObj = {
category: type, // HTTP
data: {
content,
},
type,
eventId: this.#currentEventId,
};
// add timeline to the stack
this.#timelineObj.addToTimeline(timelineObj);
}
_setUpDataBaseListener() {
const load = Module._load;
Module._load = function(request, parent) {
const res = load.apply(this, arguments);
if (request === 'mongoose') {
return mongooseListener(res);
}
return res;
};
}
_setUpIncomingListener() {
return require('./utils/incomingListener');
}
}
export default PerformanceMonitor;

View File

@@ -0,0 +1,56 @@
'use strict';
/* eslint-disable no-console */
/*eslint-disable no-unused-vars*/
const Http = require('http');
//const { performance, PerformanceObserver } = require('perf_hooks');
const { v4: uuidv4 } = require('uuid');
//const obs = new PerformanceObserver((list, observer) => {
// console.log(list.getEntries()[0]);
// performance.clearMarks();
//observer.disconnect();
//});
//obs.observe({ entryTypes: ['measure'] });
const context = new Map();
const emit = Http.Server.prototype.emit;
Http.Server.prototype.emit = function(type) {
if (type === 'request') {
const [req, res] = [arguments[1], arguments[2]];
req.apm = {};
req.apm.uuid = uuidv4();
const startHrTime = process.hrtime();
//performance.mark(`start-${req.apm.uuid}`);
res.on('finish', () => {
const elapsedHrTime = process.hrtime(startHrTime);
const elapsedTimeInMs =
elapsedHrTime[0] * 1000 + elapsedHrTime[1] / 1e6;
console.log('incoming', elapsedTimeInMs);
//performance.mark(`end-${req.apm.uuid}`);
// performance.measure(
// `request-${req.apm.uuid}`,
// `start-${req.apm.uuid}`,
// `end-${req.apm.uuid}`
// );
});
}
return emit.apply(this, arguments);
};
const init = function(asyncId, type, triggerAsyncId) {
if (context.has(triggerAsyncId)) {
context.set(asyncId, context.get(triggerAsyncId));
}
};
const destroy = function(asyncId) {
if (context.has(asyncId)) {
context.delete(asyncId);
}
};
module.exports = { init, destroy };

View File

@@ -0,0 +1,59 @@
'use strict';
/* eslint-disable no-console */
/*eslint-disable no-unused-vars*/
//const { performance } = require('perf_hooks');
const { v4: uuidv4 } = require('uuid');
const wrapAsync = function(orig, name) {
return async function() {
//const uuid = uuidv4();
name = name || `mongoose.${this.op}`; // mongose Query.exec specific
const startHrTime = process.hrtime();
try {
//performance.mark(`start-${uuid}`);
const res = await orig.apply(this, arguments);
//performance.mark(`end-${uuid}`);
//performance.measure(
// `${name}-${uuid}`,
// `start-${uuid}`,
// `end-${uuid}`
//);
const elapsedHrTime = process.hrtime(startHrTime);
const elapsedTimeInMs =
elapsedHrTime[0] * 1000 + elapsedHrTime[1] / 1e6;
console.log(name, elapsedTimeInMs);
return res;
} catch (err) {
const elapsedHrTime = process.hrtime(startHrTime);
const elapsedTimeInMs =
elapsedHrTime[0] * 1000 + elapsedHrTime[1] / 1e6;
console.log(name, elapsedTimeInMs);
//performance.mark(`end-${uuid}`);
//performance.measure(
// `${name}-${uuid}`,
// `start-${uuid}`,
// `end-${uuid}`
// );
throw err;
}
};
};
export default function(mod) {
const proto = Object.getPrototypeOf(mod);
const exec = proto.Query.prototype.exec;
proto.Query.prototype.exec = wrapAsync(exec);
const Model = proto.Model;
const remove = Model.prototype.remove;
Model.prototype.remove = wrapAsync(remove, 'mongoose.remove');
const save = Model.prototype.save;
Model.prototype.save = wrapAsync(save, 'mongoose.save');
return mod;
}

View File

@@ -33,6 +33,7 @@ const serverBuild = {
fs: 'empty',
child_process: 'empty',
net: 'empty',
module: 'empty',
},
};
const webBuild = {

14792
probe/package-lock.json generated

File diff suppressed because it is too large Load Diff