ヒント
このレッスンは、New Relic プラットフォームでカスタム視覚化を構築する方法を学習するコースの一部です。
コースの各レッスンは前回のレッスンに基づいて構築されるため、このレッスンを開始する前に、前回のレッスン「カスタム視覚化と New Relic SDK 」を完了していることを確認してください。
ここから始めましょう
前のレッスンでは、クエリされたデータを次の 2 つのグラフ タイプのいずれかで表示するカスタム視覚化を構築しました。
視覚化 UI で 2 つのグラフ タイプを切り替えるためにSegmentedControl
を使用しました。 この実装は視覚化のスペースを占有しますが、チャートのインスタンスを作成した後でも、ユーザーは 2 つのチャート タイプを切り替えることができます。 しかし、視覚化を初期化するときにオプションを 1 回だけ選択できるようにする必要がある場合はどうでしょうか?
このレッスンでは、 SegmentedControl
を置き換える設定オプションを視覚化に追加する方法を学びます。
ヒント
コード プロジェクトで迷ってしまったり、各レッスンを終えたときにファイルがどのようになっているかを確認したい場合は、Github のコース プロジェクトを確認してください。
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Treemap,} from 'recharts';import { AutoSizer, Card, CardBody, HeadingText, NrqlQuery, SegmentedControl, SegmentedControlItem, Spinner,} from 'nr1';
const CHART_TYPES = { Radar: 'radar', Treemap: 'treemap',};
export default class RadarOrTreemapVisualization extends React.Component { // Custom props you wish to be configurable in the UI must also be defined in // the nr1.json file for the visualization. See docs for more details. static propTypes = { /** * A fill color to override the default fill color. This is an example of * a custom chart configuration. */ fill: PropTypes.string,
/** * A stroke color to override the default stroke color. This is an example of * a custom chart configuration. */ stroke: PropTypes.string, /** * An array of objects consisting of a nrql `query` and `accountId`. * This should be a standard prop for any NRQL based visualizations. */ nrqlQueries: PropTypes.arrayOf( PropTypes.shape({ accountId: PropTypes.number, query: PropTypes.string, }) ), };
state = { selectedChart: CHART_TYPES.Radar, };
/** * Restructure the data for a non-time-series, facet-based NRQL query into a * form accepted by the Recharts library's RadarChart. * (https://recharts.org/api/RadarChart). */ transformData = (rawData) => { return rawData.map((entry) => ({ name: entry.metadata.name, // Only grabbing the first data value because this is not time-series data. value: entry.data[0].y, })); };
/** * Format the given axis tick's numeric value into a string for display. */ formatTick = (value) => { return value.toLocaleString(); };
updateSelectedChart = (evt, value) => { this.setState({ selectedChart: value }); };
render() { const { nrqlQueries, stroke, fill } = this.props; const { selectedChart } = this.state;
const nrqlQueryPropsAvailable = nrqlQueries && nrqlQueries[0] && nrqlQueries[0].accountId && nrqlQueries[0].query;
if (!nrqlQueryPropsAvailable) { return <EmptyState />; }
return ( <AutoSizer> {({ width, height }) => ( <NrqlQuery query={nrqlQueries[0].query} accountId={parseInt(nrqlQueries[0].accountId)} pollInterval={NrqlQuery.AUTO_POLL_INTERVAL} > {({ data, loading, error }) => { if (loading) { return <Spinner />; }
if (error) { return <ErrorState />; }
const transformedData = this.transformData(data);
return ( <React.Fragment> <SegmentedControl onChange={this.updateSelectedChart}> <SegmentedControlItem value={CHART_TYPES.Radar} label="Radar chart" /> <SegmentedControlItem value={CHART_TYPES.Treemap} label="Treemap chart" /> </SegmentedControl> {selectedChart === CHART_TYPES.Radar ? ( <RadarChart width={width} height={height} data={transformedData} > <PolarGrid /> <PolarAngleAxis dataKey="name" /> <PolarRadiusAxis tickFormatter={this.formatTick} /> <Radar dataKey="value" stroke={stroke || '#51C9B7'} fill={fill || '#51C9B7'} fillOpacity={0.6} /> </RadarChart> ) : ( <Treemap width={width} height={height} data={transformedData} dataKey="value" ratio={4 / 3} stroke={stroke || '#000000'} fill={fill || '#51C9B7'} /> )} </React.Fragment> ); }} </NrqlQuery> )} </AutoSizer> ); }}
const EmptyState = () => ( <Card className="EmptyState"> <CardBody className="EmptyState-cardBody"> <HeadingText spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Please provide at least one NRQL query & account ID pair </HeadingText> <HeadingText spacingType={[HeadingText.SPACING_TYPE.MEDIUM]} type={HeadingText.TYPE.HEADING_4} > An example NRQL query you can try is: </HeadingText> <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code> </CardBody> </Card>);
const ErrorState = () => ( <Card className="ErrorState"> <CardBody className="ErrorState-cardBody"> <HeadingText className="ErrorState-headingText" spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Oops! Something went wrong. </HeadingText> </CardBody> </Card>);
{ "schemaType": "VISUALIZATION", "id": "radar-or-treemap", "displayName": "RadarOrTreemap", "description": "", "configuration": [ { "name": "nrqlQueries", "title": "NRQL Queries", "type": "collection", "items": [ { "name": "accountId", "title": "Account ID", "description": "Account ID to be associated with the query", "type": "account-id" }, { "name": "query", "title": "Query", "description": "NRQL query for visualization", "type": "nrql" } ] }, { "name": "fill", "title": "Fill color", "description": "A fill color to override the default fill color", "type": "string" }, { "name": "stroke", "title": "Stroke color", "description": "A stroke color to override the default stroke color", "type": "string" } ]}
新しい設定オプションを追加する
視覚化のnr1.jsonファイルに、 selectedChart
のenum
設定 オブジェクトを追加します。
{ "schemaType": "VISUALIZATION", "id": "radar-or-treemap", "displayName": "RadarOrTreemap", "description": "", "configuration": [ { "name": "selectedChart", "title": "Select chart", "description": "Select which chart to display", "type": "enum", "items": [ { "title": "Radar", "value": "radar" }, { "title": "Treemap", "value": "treemap" } ] }, { "name": "nrqlQueries", "title": "NRQL Queries", "type": "collection", "items": [ { "name": "accountId", "title": "Account ID", "description": "Account ID to be associated with the query", "type": "account-id" }, { "name": "query", "title": "Query", "description": "NRQL query for visualization", "type": "nrql" } ] }, { "name": "fill", "title": "Fill color", "description": "A fill color to override the default fill color", "type": "string" }, { "name": "stroke", "title": "Stroke color", "description": "A stroke color to override the default stroke color", "type": "string" } ]}
alternate-vizにある Nerdpack のルートに移動します。
$nr1 nerdpack:serve
前回のレッスンで作成した Nerdpack をまだ提供している場合は、 CTRL-X
で停止し、再度提供してnr1.jsonへの変更を反映する必要があります。
https://one.newrelic.com/?nerdpacks=localにアクセスしてください。 nerdpacks=local
クエリ文字列は、UI にローカル サーバーから視覚化を読み込むように指示します。
Apps [アプリ]ページを開きます。
Custom Visualizations [カスタム視覚化]に移動します。
Custom Visualizations [カスタム視覚化]で、視覚化を見つけてクリックします。
新しいSelect chart [チャート設定の選択]オプションに注目してください。
グラフの種類を選択しても視覚化には影響しません。 これは、まず視覚化コンポーネントにselectedChart
プロパティを導入する必要があるためです。 次に、 selectedChart
を使用してレンダリングするグラフの種類を決定します。
SegmentedControl
を構成可能なプロパティに置き換えます
視覚化のindex.jsファイルを開きます。 ガイドの残りの部分はここで作業します。
render()
では、 props
を分解して取得した定数としてselectedChart
を含め、コンポーネントのstate
を削除します。
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Treemap,} from 'recharts';import { AutoSizer, Card, CardBody, HeadingText, NrqlQuery, SegmentedControl, SegmentedControlItem, Spinner,} from 'nr1';
const CHART_TYPES = { Radar: 'radar', Treemap: 'treemap',};
export default class RadarOrTreemapVisualization extends React.Component { // Custom props you wish to be configurable in the UI must also be defined in // the nr1.json file for the visualization. See docs for more details. static propTypes = { /** * A fill color to override the default fill color. This is an example of * a custom chart configuration. */ fill: PropTypes.string,
/** * A stroke color to override the default stroke color. This is an example of * a custom chart configuration. */ stroke: PropTypes.string, /** * An array of objects consisting of a nrql `query` and `accountId`. * This should be a standard prop for any NRQL based visualizations. */ nrqlQueries: PropTypes.arrayOf( PropTypes.shape({ accountId: PropTypes.number, query: PropTypes.string, }) ), };
/** * Restructure the data for a non-time-series, facet-based NRQL query into a * form accepted by the Recharts library's RadarChart. * (https://recharts.org/api/RadarChart). */ transformData = (rawData) => { return rawData.map((entry) => ({ name: entry.metadata.name, // Only grabbing the first data value because this is not time-series data. value: entry.data[0].y, })); };
/** * Format the given axis tick's numeric value into a string for display. */ formatTick = (value) => { return value.toLocaleString(); };
render() { const { nrqlQueries, stroke, fill, selectedChart } = this.props;
const nrqlQueryPropsAvailable = nrqlQueries && nrqlQueries[0] && nrqlQueries[0].accountId && nrqlQueries[0].query;
if (!nrqlQueryPropsAvailable) { return <EmptyState />; }
return ( <AutoSizer> {({ width, height }) => ( <NrqlQuery query={nrqlQueries[0].query} accountId={parseInt(nrqlQueries[0].accountId)} pollInterval={NrqlQuery.AUTO_POLL_INTERVAL} > {({ data, loading, error }) => { if (loading) { return <Spinner />; }
if (error) { return <ErrorState />; }
const transformedData = this.transformData(data);
return ( <React.Fragment> <SegmentedControl> <SegmentedControlItem value={CHART_TYPES.Radar} label="Radar chart" /> <SegmentedControlItem value={CHART_TYPES.Treemap} label="Treemap chart" /> </SegmentedControl> {selectedChart === CHART_TYPES.Radar ? ( <RadarChart width={width} height={height} data={transformedData} > <PolarGrid /> <PolarAngleAxis dataKey="name" /> <PolarRadiusAxis tickFormatter={this.formatTick} /> <Radar dataKey="value" stroke={stroke || '#51C9B7'} fill={fill || '#51C9B7'} fillOpacity={0.6} /> </RadarChart> ) : ( <Treemap width={width} height={height} data={transformedData} dataKey="value" ratio={4 / 3} stroke={stroke || '#000000'} fill={fill || '#51C9B7'} /> )} </React.Fragment> ); }} </NrqlQuery> )} </AutoSizer> ); }}
const EmptyState = () => ( <Card className="EmptyState"> <CardBody className="EmptyState-cardBody"> <HeadingText spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Please provide at least one NRQL query & account ID pair </HeadingText> <HeadingText spacingType={[HeadingText.SPACING_TYPE.MEDIUM]} type={HeadingText.TYPE.HEADING_4} > An example NRQL query you can try is: </HeadingText> <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code> </CardBody> </Card>);
const ErrorState = () => ( <Card className="ErrorState"> <CardBody className="ErrorState-cardBody"> <HeadingText className="ErrorState-headingText" spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Oops! Something went wrong. </HeadingText> </CardBody> </Card>);
コンポーネントstate
の代わりに設定オプションのselectedChart
を使用しているので、設定パネルでグラフを選択し、視覚化の変化を確認できます。 残念ながらバグがあります。 デフォルトのチャート オプションはRadar [レーダー]ですが、初期レンダリングではツリーマップが表示されます。
selectedChart
がない場合を考慮して三項式を更新します。
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Treemap,} from 'recharts';import { AutoSizer, Card, CardBody, HeadingText, NrqlQuery, SegmentedControl, SegmentedControlItem, Spinner,} from 'nr1';
const CHART_TYPES = { Radar: 'radar', Treemap: 'treemap',};
export default class RadarOrTreemapVisualization extends React.Component { // Custom props you wish to be configurable in the UI must also be defined in // the nr1.json file for the visualization. See docs for more details. static propTypes = { /** * A fill color to override the default fill color. This is an example of * a custom chart configuration. */ fill: PropTypes.string,
/** * A stroke color to override the default stroke color. This is an example of * a custom chart configuration. */ stroke: PropTypes.string, /** * An array of objects consisting of a nrql `query` and `accountId`. * This should be a standard prop for any NRQL based visualizations. */ nrqlQueries: PropTypes.arrayOf( PropTypes.shape({ accountId: PropTypes.number, query: PropTypes.string, }) ), };
/** * Restructure the data for a non-time-series, facet-based NRQL query into a * form accepted by the Recharts library's RadarChart. * (https://recharts.org/api/RadarChart). */ transformData = (rawData) => { return rawData.map((entry) => ({ name: entry.metadata.name, // Only grabbing the first data value because this is not time-series data. value: entry.data[0].y, })); };
/** * Format the given axis tick's numeric value into a string for display. */ formatTick = (value) => { return value.toLocaleString(); };
render() { const { nrqlQueries, stroke, fill, selectedChart } = this.props;
const nrqlQueryPropsAvailable = nrqlQueries && nrqlQueries[0] && nrqlQueries[0].accountId && nrqlQueries[0].query;
if (!nrqlQueryPropsAvailable) { return <EmptyState />; }
return ( <AutoSizer> {({ width, height }) => ( <NrqlQuery query={nrqlQueries[0].query} accountId={parseInt(nrqlQueries[0].accountId)} pollInterval={NrqlQuery.AUTO_POLL_INTERVAL} > {({ data, loading, error }) => { if (loading) { return <Spinner />; }
if (error) { return <ErrorState />; }
const transformedData = this.transformData(data);
return ( <React.Fragment> <SegmentedControl> <SegmentedControlItem value={CHART_TYPES.Radar} label="Radar chart" /> <SegmentedControlItem value={CHART_TYPES.Treemap} label="Treemap chart" /> </SegmentedControl> {!selectedChart || selectedChart === CHART_TYPES.Radar ? ( <RadarChart width={width} height={height} data={transformedData} > <PolarGrid /> <PolarAngleAxis dataKey="name" /> <PolarRadiusAxis tickFormatter={this.formatTick} /> <Radar dataKey="value" stroke={stroke || '#51C9B7'} fill={fill || '#51C9B7'} fillOpacity={0.6} /> </RadarChart> ) : ( <Treemap width={width} height={height} data={transformedData} dataKey="value" ratio={4 / 3} stroke={stroke || '#000000'} fill={fill || '#51C9B7'} /> )} </React.Fragment> ); }} </NrqlQuery> )} </AutoSizer> ); }}
const EmptyState = () => ( <Card className="EmptyState"> <CardBody className="EmptyState-cardBody"> <HeadingText spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Please provide at least one NRQL query & account ID pair </HeadingText> <HeadingText spacingType={[HeadingText.SPACING_TYPE.MEDIUM]} type={HeadingText.TYPE.HEADING_4} > An example NRQL query you can try is: </HeadingText> <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code> </CardBody> </Card>);
const ErrorState = () => ( <Card className="ErrorState"> <CardBody className="ErrorState-cardBody"> <HeadingText className="ErrorState-headingText" spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Oops! Something went wrong. </HeadingText> </CardBody> </Card>);
オプションをまだ設定していない場合、データはRadarChart
でレンダリングされます。
render()
からSegmentedControl
を削除します:
import React from 'react';import PropTypes from 'prop-types';import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Treemap,} from 'recharts';import { AutoSizer, Card, CardBody, HeadingText, NrqlQuery, Spinner,} from 'nr1';
const CHART_TYPES = { Radar: 'radar', Treemap: 'treemap',};
export default class RadarOrTreemapVisualization extends React.Component { // Custom props you wish to be configurable in the UI must also be defined in // the nr1.json file for the visualization. See docs for more details. static propTypes = { /** * A fill color to override the default fill color. This is an example of * a custom chart configuration. */ fill: PropTypes.string,
/** * A stroke color to override the default stroke color. This is an example of * a custom chart configuration. */ stroke: PropTypes.string, /** * An array of objects consisting of a nrql `query` and `accountId`. * This should be a standard prop for any NRQL based visualizations. */ nrqlQueries: PropTypes.arrayOf( PropTypes.shape({ accountId: PropTypes.number, query: PropTypes.string, }) ), };
/** * Restructure the data for a non-time-series, facet-based NRQL query into a * form accepted by the Recharts library's RadarChart. * (https://recharts.org/api/RadarChart). */ transformData = (rawData) => { return rawData.map((entry) => ({ name: entry.metadata.name, // Only grabbing the first data value because this is not time-series data. value: entry.data[0].y, })); };
/** * Format the given axis tick's numeric value into a string for display. */ formatTick = (value) => { return value.toLocaleString(); };
render() { const { nrqlQueries, stroke, fill, selectedChart } = this.props;
const nrqlQueryPropsAvailable = nrqlQueries && nrqlQueries[0] && nrqlQueries[0].accountId && nrqlQueries[0].query;
if (!nrqlQueryPropsAvailable) { return <EmptyState />; }
return ( <AutoSizer> {({ width, height }) => ( <NrqlQuery query={nrqlQueries[0].query} accountId={parseInt(nrqlQueries[0].accountId)} pollInterval={NrqlQuery.AUTO_POLL_INTERVAL} > {({ data, loading, error }) => { if (loading) { return <Spinner />; }
if (error) { return <ErrorState />; }
const transformedData = this.transformData(data);
return ( <React.Fragment> {!selectedChart || selectedChart === CHART_TYPES.Radar ? ( <RadarChart width={width} height={height} data={transformedData} > <PolarGrid /> <PolarAngleAxis dataKey="name" /> <PolarRadiusAxis tickFormatter={this.formatTick} /> <Radar dataKey="value" stroke={stroke || '#51C9B7'} fill={fill || '#51C9B7'} fillOpacity={0.6} /> </RadarChart> ) : ( <Treemap width={width} height={height} data={transformedData} dataKey="value" ratio={4 / 3} stroke={stroke || '#000000'} fill={fill || '#51C9B7'} /> )} </React.Fragment> ); }} </NrqlQuery> )} </AutoSizer> ); }}
const EmptyState = () => ( <Card className="EmptyState"> <CardBody className="EmptyState-cardBody"> <HeadingText spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Please provide at least one NRQL query & account ID pair </HeadingText> <HeadingText spacingType={[HeadingText.SPACING_TYPE.MEDIUM]} type={HeadingText.TYPE.HEADING_4} > An example NRQL query you can try is: </HeadingText> <code>FROM NrUsage SELECT sum(usage) FACET metric SINCE 1 week ago</code> </CardBody> </Card>);
const ErrorState = () => ( <Card className="ErrorState"> <CardBody className="ErrorState-cardBody"> <HeadingText className="ErrorState-headingText" spacingType={[HeadingText.SPACING_TYPE.LARGE]} type={HeadingText.TYPE.HEADING_3} > Oops! Something went wrong. </HeadingText> </CardBody> </Card>);
Nerdpack をローカルで提供し、New Relic のCustom Visualizations [カスタム視覚化]アプリで表示します。 設定サイドバーのドロップダウンからグラフの種類を選択すると、視覚化が更新され、一致するグラフの種類が表示されます。
概要
このレッスンを完了していただき、ありがとうございます! nr1.json設定を使用して視覚化をカスタマイズする方法を学びました。
コース
このレッスンは、New Relic プラットフォームでカスタム視覚化を構築する方法を学習するコースの一部です。 準備ができたら、次のレッスン「ダッシュボードにカスタム視覚化を追加する」に進んでください。