Skip to content

Commit 8c48a1d

Browse files
committed
fixed grafana dashboards for docker metrics
1 parent 0e60612 commit 8c48a1d

File tree

24 files changed

+232
-25
lines changed

24 files changed

+232
-25
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import React, { useEffect, useState, useContext } from 'react';
2+
import { HealthContext } from '../context/HealthContext';
3+
import { QueryContext } from '../context/QueryContext';
4+
import GrafanaEventChart from '../charts/GrafanaEventChart';
5+
import { Button } from '@material-ui/core';
6+
import { useParams } from 'react-router-dom';
7+
8+
interface HealthContainerProps {
9+
sizing: string;
10+
colourGenerator: Function;
11+
category: string;
12+
//currentService: string;
13+
}
14+
15+
interface Params {
16+
service: string;
17+
}
18+
19+
interface DataObject {
20+
[key: string]: {
21+
value: string[],
22+
time: string[],
23+
id: string,
24+
token: string
25+
};
26+
}
27+
interface DockerDataObject {
28+
[key: string]: DataObject
29+
}
30+
31+
const DockerHealthContainer: React.FC<HealthContainerProps> = React.memo(props => {
32+
const { healthData } = useContext(HealthContext);
33+
const { selectedMetrics } = useContext(QueryContext);
34+
const { service } = useParams<keyof Params>() as Params;
35+
const [healthChartsArr, setHealthChartsArr] = useState<JSX.Element[]>([]);
36+
const { sizing, colourGenerator, category } = props;
37+
const [currIndex, setCurrIndex] = useState(0);
38+
const [currChunk, setCurrChunk] = useState<JSX.Element[]>([]);
39+
const chunkSize = 7;
40+
let [isGrafana, setIsGrafana] = useState(false);
41+
/**
42+
* This function filters the selectedMetrics array down to only metrics that match the category of this instance of HealthContainer.
43+
* Once that has finished, it then filters the healthData down to the current category and the filteredMetrics.
44+
*/
45+
46+
function nextChunk() {
47+
const nextChunk = healthChartsArr.slice(currIndex, currIndex + chunkSize);
48+
setCurrChunk(nextChunk);
49+
setCurrIndex(currIndex + chunkSize);
50+
}
51+
function prevChunk() {
52+
const prevChunk = healthChartsArr.slice(currIndex - 2 * chunkSize, currIndex - chunkSize);
53+
setCurrChunk(prevChunk);
54+
setCurrIndex(currIndex - chunkSize);
55+
}
56+
57+
const filterSelectedMetricsAndHealthData = (): DockerDataObject => {
58+
// define a filtered health data object for output
59+
// define an array of filteredMetricNames for later use
60+
const filteredHealthData = {};
61+
const filteredMetricNames: string[] = [];
62+
// iterate over the selectedMetrics from QueryContext
63+
selectedMetrics.forEach(metricObj => {
64+
// due to the way the data is stored, each metricObj has a key of category, and an array of selected metrics as a value
65+
const metricCategory = Object.keys(metricObj)[0];
66+
const metricValuesArray = metricObj[metricCategory];
67+
// if the current metricObj's category matches our instance's current category, iterate through its array of values
68+
if (metricCategory === category) {
69+
metricValuesArray.forEach(metricName => {
70+
filteredMetricNames.push(metricName); // add the metricNames to the filteredMetricNames array
71+
});
72+
}
73+
});
74+
/*
75+
Now that we've defined which of the user's selected metrics belong in this category, iterate over the healthData object
76+
and filter it down to the selected category and metrics.
77+
*/
78+
for (const service in healthData) {
79+
filteredHealthData[service] = {};
80+
const categoryObjects = healthData[service];
81+
for (const categoryName in categoryObjects) {
82+
// if the category in healthData matches the category passed down to this HealthContainer, iterate over the related metrics
83+
if (categoryName === category) {
84+
const metricObjects = categoryObjects[categoryName];
85+
for (const metric in metricObjects) {
86+
// if the metric title matches any element in the filtered metrics array, add the metric serviceName to the filteredHealthData object, then add the metrics for that service
87+
if (filteredMetricNames.includes(metric)) {
88+
filteredHealthData[service][metric] = metricObjects[metric];
89+
}
90+
}
91+
}
92+
}
93+
}
94+
return filteredHealthData;
95+
};
96+
97+
// helper function for geting only the names of the metrics
98+
const getIndex = (str: string, substr: string, ind: number): number => {
99+
let Len = str.length,
100+
i = -1;
101+
while (ind-- && i++ < Len) {
102+
i = str.indexOf(substr, i);
103+
if (i < 0) break;
104+
}
105+
return i;
106+
}
107+
108+
// function to generate charts using the type-sorted data
109+
const generateHealthCharts = (sortedData: DockerDataObject): void => {
110+
//onst chartsArray: JSX.Element[] = [];
111+
const grafanaChartsArray: JSX.Element[] = [];
112+
//let parsedName: string = '';
113+
const keymaker = () => {
114+
return Math.floor(Math.random() * 1000);
115+
};
116+
// iterate over the sortedData and create a chart for each data type and each service of that data
117+
for (const dataType in sortedData) {
118+
const metricObjects = sortedData[service];
119+
for (const metricName in metricObjects) {
120+
// pass down the value of the current data type and service
121+
const chartData = metricObjects[metricName];
122+
const token = chartData.token;
123+
// chartsArray.push(
124+
// <HealthChart
125+
// key={'H' + keymaker()}
126+
// dataType={dataType}
127+
// serviceName={serviceName}
128+
// chartData={chartData}
129+
// categoryName={category}
130+
// sizing={sizing}
131+
// colourGenerator={colourGenerator}
132+
// />
133+
// );
134+
console.log("plotting grafana")
135+
grafanaChartsArray.push(
136+
<GrafanaEventChart metricName={metricName} token={token} />);
137+
138+
}
139+
}
140+
console.log(grafanaChartsArray)
141+
setHealthChartsArr(grafanaChartsArray);
142+
setCurrChunk(grafanaChartsArray.slice(currIndex, currIndex + chunkSize));
143+
setCurrIndex(currIndex + chunkSize);
144+
};
145+
146+
useEffect(() => {
147+
// returns an object containing only the healthData for the current category and the metrics the User selected
148+
const filteredHealthData = filterSelectedMetricsAndHealthData();
149+
// returns an object containing the filtered data sorted by data type
150+
//const typeSortedHealthData = healthDataGroupedByDataType(filteredHealthData);
151+
// invoking generateCharts with the sorted data will update healthChartsArr in state with the list of charts to be rendered
152+
generateHealthCharts(filteredHealthData);
153+
}, [category]);
154+
155+
// JJ-ADDITION
156+
return (
157+
<div>
158+
{/* <div id="grafana" onClick={() => { setIsGrafana(!isGrafana) }}>Grafana</div> */}
159+
{service.includes('kafkametrics') || service.includes('kubernetesmetrics') || service.includes('books') || service.includes('customers') || service.includes('frontend') || service.includes('orders')? currChunk : []}
160+
{healthChartsArr.length > chunkSize && (
161+
<>
162+
<Button id="prevCharts" onClick={prevChunk} variant="contained" color="primary" disabled={currIndex <= chunkSize}>
163+
Prev
164+
</Button>
165+
<Button id="nextCharts" onClick={nextChunk} variant="contained" color="primary" disabled={currIndex >= healthChartsArr.length}>
166+
Next
167+
</Button>
168+
</>
169+
)}
170+
</div>
171+
);
172+
});
173+
174+
export default DockerHealthContainer;

app/containers/EventContainer.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useEffect, useState, useContext } from 'react';
22
import { useParams } from 'react-router-dom';
33
import { EventContext } from '../context/EventContext';
4+
import { HealthContext } from '../context/HealthContext';
45
import { QueryContext } from '../context/QueryContext';
56
import EventChart from '../charts/EventChart';
67
import { Button } from '@material-ui/core';

app/containers/GraphsContainer.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import HealthContainer from './HealthContainer';
2020
import ModifyMetrics from './ModifyMetricsContainer';
2121
import * as DashboardContext from '../context/DashboardContext';
2222
import lightAndDark from '../components/Styling';
23+
import DockerHealthContainer from './DockerHealthContainer';
2324

2425
import '../stylesheets/GraphsContainer.scss';
2526
import { Link } from 'react-router-dom';
@@ -133,7 +134,14 @@ const GraphsContainer: React.FC = React.memo(props => {
133134
if (selectedMetrics) {
134135
selectedMetrics.forEach((element, id) => {
135136
const categoryName = Object.keys(element)[0];
136-
const prefix = categoryName === 'Event' ? 'event_' : 'health_';
137+
let prefix;
138+
if (categoryName === 'Event') {
139+
prefix = 'event_';
140+
} else if (categoryName === 'books' || categoryName === 'customers' || categoryName === 'frontend' || categoryName === 'orders'){
141+
prefix = 'docker_';
142+
} else {
143+
prefix = 'health_';
144+
}
137145
buttonList.push(
138146
<button
139147
id={`${prefix}${categoryName}-button`}
@@ -242,6 +250,12 @@ const GraphsContainer: React.FC = React.memo(props => {
242250
<EventContainer colourGenerator={stringToColour} sizing="solo" />
243251
</>
244252

253+
)}
254+
{chart.startsWith('docker_') && (
255+
<>
256+
<DockerHealthContainer colourGenerator={stringToColour} sizing="solo" category={chart.substring(7)} />
257+
</>
258+
245259
)}
246260
{chart === 'docker' && <DockerChart />}
247261
{chart === 'modifyMetrics' && <ModifyMetrics />}

chronos_npm_package/chronos.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ class Chronos {
141141
async docker () {
142142
await utilities.testMetricsQuery(this.config);
143143
if (this.config.database.type === 'MongoDB') {
144-
mongo.connect(this.config);
144+
await mongo.connect(this.config);
145+
await mongo.storeGrafanaAPIKey(this.config);
145146
mongo.serverQuery(this.config);
146147
// return mongo.modifyMetrics(this.config);
147148
} else if (this.config.database.type === 'PostgreSQL') {

chronos_npm_package/controllers/mongo.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ mongo.setQueryOnInterval = async config => {
255255
console.log(`${config.mode} metrics recorded in MongoDB`)
256256
}
257257
});
258-
258+
259259
let allMetrics = await model.find({});
260260
console.log('allMetrics.length: ', allMetrics.length);
261261
console.log("🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 start creating dashboards 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡")
@@ -315,6 +315,7 @@ mongo.createGrafanaDashboards = async (config, parsedArray) => {
315315
console.log('In mongo.createGrafanaDashboards!!!')
316316
console.log('Calling utilities.getGrafanaDatasource()');
317317
const datasource = await utilities.getGrafanaDatasource(config.grafanaAPIKey);
318+
console.log('mongo.createGrafanaDashboards line 318:', datasource);
318319
//console.log('Calling utilities.promMetricsQuery()');
319320
//const parsedArray = await utilities.promMetricsQuery(config);
320321
//const datasource = await utilities.getGrafanaDatasource();

chronos_npm_package/controllers/utilities.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ const helpers = {
337337

338338
// push panel into dashboard object with a line for each metric in promQLQueries object
339339
dashboard.dashboard.panels.push(createGrafanaPanelObject(metric, datasource, graphType));
340-
340+
console.log('utilities.createGrafanaDashboard line 340:', dashboard.dashboard.panels);
341341
try {
342342
// POST request to Grafana Dashboard API to create a dashboard
343343
const dashboardResponse = await axios.post(
@@ -350,7 +350,7 @@ const helpers = {
350350
},
351351
}
352352
);
353-
//console.log("dashboardResponse is: ", dashboardResponse)
353+
console.log("utilities.createGrafanaDashboard line 353 dashboardResponse is: ", dashboardResponse)
354354

355355
// Descriptive error log for developers
356356
if (dashboardResponse.status >= 400) {
@@ -376,6 +376,7 @@ const helpers = {
376376
'Authorization': token
377377
},
378378
});
379+
console.log('utilities.getGrafanaDatasource line 379:', datasourceResponse);
379380
console.log("Successfully fetched datasource from Grafana API")
380381
// Create a datasource object to be used within panels.
381382
const datasource = {

electron/routes/data.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ ipcMain.on('healthRequest', async (message: Electron.IpcMainEvent, service: stri
180180
console.log('result data.ts line 177', result)
181181
// Async event emitter - send response'
182182

183+
console.log('Hi, inside data.ts line 183 - healthRequest. sending health response...');
183184
message.sender.send('healthResponse', JSON.stringify(result));
184185
} catch (error) {
185186
// Catch errors
@@ -199,6 +200,7 @@ ipcMain.on('dockerRequest', async (message, service) => {
199200
// Mongo Database
200201
if (currentDatabaseType === 'MongoDB') {
201202
// Get document count
203+
console.log('Hi, inside data.ts line 203 - dockerRequest. Fetching data...');
202204
let num = await DockerModelFunc(service).countDocuments();
203205
// Get last 50 documents. If less than 50 documents, get all
204206
num = Math.max(num, 50);

examples/docker/books/chronos-config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ module.exports = {
1717
type: process.env.CHRONOS_DB,
1818
URI: process.env.CHRONOS_URI,
1919
},
20-
20+
grafanaAPIKey: process.env.CHRONOS_GRAFANA_API_KEY,
2121
notifications: [],
2222
}

examples/docker/books/chronos_npm_package/chronos.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ class Chronos {
141141
async docker () {
142142
await utilities.testMetricsQuery(this.config);
143143
if (this.config.database.type === 'MongoDB') {
144-
mongo.connect(this.config);
144+
await mongo.connect(this.config);
145+
await mongo.storeGrafanaAPIKey(this.config);
145146
mongo.serverQuery(this.config);
146147
// return mongo.modifyMetrics(this.config);
147148
} else if (this.config.database.type === 'PostgreSQL') {

examples/docker/books/chronos_npm_package/controllers/mongo.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ mongo.setQueryOnInterval = async config => {
255255
console.log(`${config.mode} metrics recorded in MongoDB`)
256256
}
257257
});
258-
258+
259259
let allMetrics = await model.find({});
260260
console.log('allMetrics.length: ', allMetrics.length);
261261
console.log("🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 start creating dashboards 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡 🟡")
@@ -315,6 +315,7 @@ mongo.createGrafanaDashboards = async (config, parsedArray) => {
315315
console.log('In mongo.createGrafanaDashboards!!!')
316316
console.log('Calling utilities.getGrafanaDatasource()');
317317
const datasource = await utilities.getGrafanaDatasource(config.grafanaAPIKey);
318+
console.log('mongo.createGrafanaDashboards line 318:', datasource);
318319
//console.log('Calling utilities.promMetricsQuery()');
319320
//const parsedArray = await utilities.promMetricsQuery(config);
320321
//const datasource = await utilities.getGrafanaDatasource();

0 commit comments

Comments
 (0)