The New Relic One SDK's Chart components allow you to easily add data visualizations to your New Relic One nerdpack. The components take care of everything: from plotting to fetching data, but they can also receive data externally to support custom data sets. The purpose of this lab is to create a Nerdlet that displays data from a custom NRQL query using the Chart components.
After completing this lab you should understand:
- The basics for interacting with the Chart components in a custom Nerdlet
Load the prequisites and follow the setup instructions in Setup. Be sure that you've set the appropriate nr1 profile
for the account data you'll be accessing. If you don't know which profile is your default profile, run the following command:
nr1 profiles:default
# select a default profile
You can also, always override the default by appending the command --profile=<profile>
to the end of an nr1
command.
Reminder: Make sure that you're ready to go with your lab1
by ensuring you've run the following commands:
# from the nr1-workshop directory
cd lab1
nr1 nerdpack:uuid -gf
npm install
The Nerdlet code that you create in this exercise will be accessed through a prebuilt launcher that is delivered as part of Lab 1. We will cover the details of launchers in a future exercise.
- Run the
nr1 create
command and choose the optionnerdlet
as well as provide a name oflab1-nerdlet
. See the following:
#Assuming we're in the lab1 directory
nr1 create
✔ What kind of component do you want to create? › nerdpack
✔ Name your nerdpack. … lab1-nerdpack
✔ Name your nerdlet. … lab1-nerdlet
✔ Name your launcher. … lab1-launcher
Installing dependencies...
npm notice created a lockfile as package-lock.json. You should commit this file.
added 8 packages from 3 contributors and audited 8 packages in 1.79s
found 0 vulnerabilities
✔ nerdpack created successfully!
nerdpack lab1-nerdpack is available at "./"
✔ nerdlet created successfully!
nerdlet lab1-nerdlet is available at "./nerdlets/lab1-nerdlet"
✔ launcher created successfully!
launcher lab1-launcher is available at "./launchers/lab1-launcher"
#And if it's not already running, execute the following
nr1 nerdpack:serve
You'll notice that the CLI creates three files in the nerdlets/lab1-nerdlet
directory: index.js, styles.scss, and a nr1.json configuration.
-
Assuming the the developer server is still running (via
nr1 nerdpack:serve
), validate (in a web browser) that you can click on and see theLab 1 Launcher
launcher by navigating in Google Chrome to https://one.newrelic.com?nerdpacks=local and click on theLab 1 Launcher
. -
Change the
displayName
property of the Nerdlet innerdlets/lab1-nerdlet/nr1.json
toLab 1 Nerdlet
and save that file. Restart your local server withCTRL+C
andnr1 nerdpack:serve
. -
Check Google Chrome. You should see the following:
- Next, we're going to prep the Nerdlet to be able to generate some charts. Add the following code to your
Lab1Nerdlet
class just above therender
method innerdlets/lab1-nerdlet/index.js
.
constructor(props) {
super(props);
this.accountId = <REPLACE_WITH_YOUR_ACCOUNT_ID>;
this.state = {
appId: null,
appName: null
};
console.debug("Nerdlet constructor", this); //eslint-disable-line
}
Note: The value of the accountId just needs to be a New Relic account to which you have access. Information on locating your account ID can be found in these docs.
- Save
index.js
and watch theLab 1 Nerdlet
reload in your Browser. - Ctrl+click (or right click) on the web browser screen displaying our Nerdlet and choose the menu item
Inspect
. - In the DevTools window now open, click on the
Console
tab at the top. - In the
Console
tab, choose theverbose
option on the left hand side. (It's in the drop-down next to the 'Filter' bar.) - Go back to the browser window and reload the current page, and then go back to the DevTools window. You should be looking at a screen like the following:
You may get a notification at the top of your debug window indicating that you do not have the 'React DevTools' loaded. If you would like to load the React DevTools extension, you can click on this link and load the chrome extension (or firefox exetension). You should become familiar with using the developer tools as a way to explore the values of your objects on the client. Take a moment now to explore the objects returned to the console.
- Open the provided documentation in this documentation.
- Find the
TableChart
documentation and explore its Usage, Example, and Config content. - Find the
ScatterChart
documentation and explore its Usage, Example, and Config content. - Find the
LineChart
documentation and explore its Usage, Example, and Config content. - Find the
ChartGroup
documentation and explore its usage and example content.
In the next few sections, we're going to create an interface that explores the relationship between the volume of transactions over time and apdex metric using the components we just reviewed.
- Add the following import statement near the top of
lab1/nerdlets/lab1-nerdlet/index.js
. This will bring in the TableComponent from the datanerd npm lirary that you named in the setup and prerequisites step.
import { TableChart, Stack, StackItem } from 'nr1';
- Add the following code to the
lab1/nerdlets/lab1-nerdlet/styles.scss
file.
.top-chart {
padding: 10px;
width: 100vw;
height: 45vh;
}
.chart {
padding: 10px;
width: 48vw;
height: 45vh;
}
- Now, let's use the
TableChart
to display a simple table with apdex information. Replace therender
method oflab1/nerdlets/lab1-nerdlet/index.js
with the following, save the file, and validate the the component renders. The Nerdlet framework will recognize thequery
object passed as an argument into theTableChart
in therender
method, and will, under the covers, execute the NRQL statement as part of a GraphQL query.
render(){
const { appId, appName } = this.state;
const nrql = `SELECT count(*) as 'transactions', apdex(duration) as 'apdex', percentile(duration, 99, 90, 70) FROM Transaction facet appName, appId limit 25`;
//return the JSX we're rendering
return (
<Stack
verticalType={Stack.VERTICAL_TYPE.FILL}
directionType={Stack.DIRECTION_TYPE.VERTICAL}
gapType={Stack.GAP_TYPE.EXTRA_LOOSE}>
<StackItem>
<TableChart query={nrql} accountId={this.accountId} className="chart" />
</StackItem>
</Stack>
)
}
- Save the files and check your web browser to see a reloaded experience that looks something like the following.
In this step, we're going to add two detail charts and wrap them in a ChartGroup
that will ensure synchronized effects across the charts.
- Modify the
nr1
import statement at the top oflab1/nerdlets/lab1-nerdlet/index.js
to the following.
import { TableChart, Stack, StackItem, ChartGroup, LineChart, ScatterChart } from 'nr1';
- We're going to add several components (spelled out in the block of code further below) to the
render
method:- a
ChartGroup
- within the
ChartGroup
aLineChart
to chart the timeseries number of transactions using the NRQL querySELECT count(*) FROM Transaction WHERE appId = ${appId} TIMESERIES
as the source of its data. - within
ChartGroup
aScatterChart
to plot the duration of requests over time using the NRQL querySELECT apdex(duration) FROM Transaction WHERE appId = ${appId} TIMESERIES
as the source of its data. - We're also going to make use of additional
Stack
andStackItem
components. For now, just use the code. We'll explain more about their purpose in a future exercise.
- a
That all results in the following block of code. Copy/reproduce the code below as the replacement for the render
method of the index.js
file:
render(){
const { appId, appName } = this.state;
const nrql = `SELECT count(*) as 'transactions', apdex(duration) as 'apdex', percentile(duration, 99, 90, 70) FROM Transaction facet appName, appId limit 25`;
const tCountNrql = `SELECT count(*) FROM Transaction WHERE appId = ${appId} TIMESERIES`;
const apdexNrql = `SELECT apdex(duration) FROM Transaction WHERE appId = ${appId} TIMESERIES`
//return the JSX we're rendering
return (
<ChartGroup>
<Stack
verticalType={Stack.VERTICAL_TYPE.FILL}
directionType={Stack.DIRECTION_TYPE.VERTICAL}
gapType={Stack.GAP_TYPE.EXTRA_LOOSE}>
<StackItem>
<TableChart query={nrql} accountId={this.accountId} className="top-chart" />
</StackItem>
{appId && <StackItem>
<Stack
horizontalType={Stack.HORIZONTAL_TYPE.FILL}
directionType={Stack.DIRECTION_TYPE.HORIZONTAL}
gapType={Stack.GAP_TYPE.EXTRA_LOOSE}>
<StackItem>
<LineChart accountId={this.accountId} query={tCountNrql} className="chart"/>
</StackItem>
<StackItem>
<ScatterChart accountId={this.accountId} query={apdexNrql} className="chart"/>
</StackItem>
</Stack>
</StackItem>}
</Stack>
</ChartGroup>
)
}
Note that the line containing {appId && <Stack...
ensures that the lower section of content displays only when an appId
is present. We haven't set that value in the state
of the component yet, so we won't see the second row.
- Save the file and reload the page in the web browser.
Note that the second row of additional charts is never drawn because the state.appId
is always NULL. There's presently no way to set its value. Let's fix that.
- Add the following method to your Nerdlet React component, e.g. after the
contructor(props)
we added above:
setApplication(inAppId, inAppName) {
this.setState({ appId: inAppId, appName: inAppName })
}
- Add a new attribute named
onClickTable
to the existingTableChart
as a way to configure aclick
event on the table rows.
The onClickTable receives four parameters that each provide a different view of the overall data.
- dataEl: The inner contents of the specific Table element on which the user clicked.
- row: a JS object of the data that makes up the entire row of that table
- chart: the entire JS object used to generate the chart, both headings and data rows
<TableChart query={nrql} accountId={this.accountId} className="top-chart" onClickTable={(dataEl, row, chart) => {
//for learning purposes, we'll write to the console.
console.debug([dataEl, row, chart]); //eslint-disable-line
this.setApplication(row.appId, row.appName);
}}/>
- The resulting
index.js
should look like the following:
import React from 'react';
import { TableChart, Stack, StackItem, ChartGroup, LineChart, ScatterChart } from 'nr1';
export default class Lab1Nerdlet extends React.Component {
constructor(props) {
super(props);
this.accountId = <REPLACE_WITH_YOUR_ACCOUNT_ID>;
this.state = {
appId: null,
appName: null
};
console.debug("Nerdlet constructor", this); //eslint-disable-line
}
setApplication(inAppId, inAppName) {
this.setState({ appId: inAppId, appName: inAppName })
}
render(){
const { appId, appName } = this.state;
const nrql = `SELECT count(*) as 'transactions', apdex(duration) as 'apdex', percentile(duration, 99, 90, 70) FROM Transaction facet appName, appId limit 25`;
const tCountNrql = `SELECT count(*) FROM Transaction WHERE appId = ${appId} TIMESERIES`;
const apdexNrql = `SELECT apdex(duration) FROM Transaction WHERE appId = ${appId} TIMESERIES`
//return the JSX we're rendering
return (
<ChartGroup>
<Stack
verticalType={Stack.VERTICAL_TYPE.FILL}
directionType={Stack.DIRECTION_TYPE.VERTICAL}
gapType={Stack.GAP_TYPE.EXTRA_LOOSE}>
<StackItem>
<TableChart query={nrql} accountId={this.accountId} className="top-chart" onClickTable={(dataEl, row, chart) => {
//for learning purposes, we'll write to the console.
console.debug([dataEl, row, chart]) //eslint-disable-line
this.setApplication(row.appId, row.appName)
}}/>
</StackItem>
{appId && <StackItem>
<Stack
horizontalType={Stack.HORIZONTAL_TYPE.FILL}
directionType={Stack.DIRECTION_TYPE.HORIZONTAL}
gapType={Stack.GAP_TYPE.EXTRA_LOOSE}>
<StackItem>
<LineChart accountId={this.accountId} query={tCountNrql} className="chart"/>
</StackItem>
<StackItem>
<ScatterChart accountId={this.accountId} query={apdexNrql} className="chart"/>
</StackItem>
</Stack>
</StackItem>}
</Stack>
</ChartGroup>
)
}
}
- Save the file and reload the page. You should be able to click on an application and see the resulting second row of charts. ✨
Based on what you've executed above, apply that learning in the following:
- Replicate the
Stack
andStackItem
code from the lower part of the display (i.e. the portion containing theScatterChart
andLineChart
) into the upper half of the display that currently only contains aTableChart
.
Note: We're going to add a LineChart
next to our TableChart
, which will require a Stack
with in the very first StackItem
that itself contains another Stack
with two StackItem
elements.
-
Within the first, new
StackItem
element, place the existingTableChart
. -
Update the
className
on theTableChart
toclassName="chart"
. -
Next to the
TableChart
in the secondStackItem
, add aLineChart
using the following NRQL query:SELECT count(*) as 'transactions' FROM Transaction facet appName, appId limit 25 TIMESERIES
. -
Add a
onClickLine
attribute to yourLineChart
that processesonClick
events in the same way that theTableChart
onTableClick
operates (i.e. calling thethis.setApplication
method). See the following:
<LineChart
query={`SELECT count(*) as 'transactions' FROM Transaction facet appName, appId limit 25 TIMESERIES`}
className="chart"
accountId={this.accountId}
onClickLine={(line) => {
//more console logging for learning purposes
console.debug(line); //eslint-disable-line
const params = line.metadata.label.split(',');
this.setApplication(params[1], params[0]);
}}
/>
- The resulting
index.js
should look like the following:
render(){
const { appId, appName } = this.state;
const nrql = `SELECT count(*) as 'transactions', apdex(duration) as 'apdex', percentile(duration, 99, 90, 70) FROM Transaction facet appName, appId limit 25`;
const tCountNrql = `SELECT count(*) FROM Transaction WHERE appId = ${appId} TIMESERIES`;
const apdexNrql = `SELECT apdex(duration) FROM Transaction WHERE appId = ${appId} TIMESERIES`
//return the JSX we're rendering
return (
<ChartGroup>
<Stack
verticalType={Stack.VERTICAL_TYPE.FILL}
directionType={Stack.DIRECTION_TYPE.VERTICAL}
gapType={Stack.GAP_TYPE.EXTRA_LOOSE}>
<StackItem>
<Stack
horizontalType={Stack.HORIZONTAL_TYPE.FILL}
directionType={Stack.DIRECTION_TYPE.HORIZONTAL}
gapType={Stack.GAP_TYPE.EXTRA_LOOSE}>
<StackItem>
<TableChart query={nrql} accountId={this.accountId} className="chart" onClickTable={(dataEl, row, chart) => {
//for learning purposes, we'll write to the console.
console.debug([dataEl, row, chart]) //eslint-disable-line
this.setApplication(row.appId, row.appName)
}}/>
</StackItem>
<StackItem>
<LineChart
query={`SELECT count(*) as 'transactions' FROM Transaction facet appName, appId limit 25 TIMESERIES`}
className="chart"
accountId={this.accountId}
onClickLine={(line) => {
//more console logging for learning purposes
console.debug(line); //eslint-disable=line
const params = line.metadata.label.split(",");
this.setApplication(params[1], params[0]);
}}
/>
</StackItem>
</Stack>
</StackItem>
{appId && <StackItem>
<Stack
horizontalType={Stack.HORIZONTAL_TYPE.FILL}
directionType={Stack.DIRECTION_TYPE.HORIZONTAL}
gapType={Stack.GAP_TYPE.EXTRA_LOOSE}>
<StackItem>
<LineChart accountId={this.accountId} query={tCountNrql} className="chart"/>
</StackItem>
<StackItem>
<ScatterChart accountId={this.accountId} query={apdexNrql} className="chart"/>
</StackItem>
</Stack>
</StackItem>}
</Stack>
</ChartGroup>
)
}
- What was the purpose of the
ChartGroup
? What is it doing for us?