/usr/share/grafana/public/app/plugins/datasource/graphite/components
import { css, cx } from '@emotion/css'; import { PureComponent } from 'react'; import { MetadataInspectorProps, rangeUtil } from '@grafana/data'; import { config } from '@grafana/runtime'; import { stylesFactory } from '@grafana/ui'; import { GraphiteDatasource } from '../datasource'; import { getRollupNotice, getRuntimeConsolidationNotice, parseSchemaRetentions } from '../meta'; import { GraphiteOptions, GraphiteQuery, MetricTankSeriesMeta } from '../types'; export type Props = MetadataInspectorProps<GraphiteDatasource, GraphiteQuery, GraphiteOptions>; export interface State { index: number; } export class MetricTankMetaInspector extends PureComponent<Props, State> { renderMeta(meta: MetricTankSeriesMeta, key: string) { const styles = getStyles(); const buckets = parseSchemaRetentions(meta['schema-retentions']); const rollupNotice = getRollupNotice([meta]); const runtimeNotice = getRuntimeConsolidationNotice([meta]); const normFunc = (meta['consolidator-normfetch'] ?? '').replace('Consolidator', ''); const totalSeconds = buckets.reduce( (acc, bucket) => acc + (bucket.retention ? rangeUtil.intervalToSeconds(bucket.retention) : 0), 0 ); return ( <div className={styles.metaItem} key={key}> <div className={styles.metaItemHeader}> Schema: {meta['schema-name']} <div className="small muted">Series count: {meta.count}</div> </div> <div className={styles.metaItemBody}> <div className={styles.step}> <div className={styles.stepHeading}>Step 1: Fetch</div> <div className={styles.stepDescription}> First data is fetched, either from raw data archive or a rollup archive </div> {rollupNotice && <p>{rollupNotice.text}</p>} {!rollupNotice && <p>No rollup archive was used</p>} <div> {buckets.map((bucket, index) => { const bucketLength = bucket.retention ? rangeUtil.intervalToSeconds(bucket.retention) : 0; const lengthPercent = (bucketLength / totalSeconds) * 100; const isActive = index === meta['archive-read']; return ( <div key={bucket.retention} className={styles.bucket}> <div className={styles.bucketInterval}>{bucket.interval}</div> <div className={cx(styles.bucketRetention, { [styles.bucketRetentionActive]: isActive })} style={{ flexGrow: lengthPercent }} /> <div style={{ flexGrow: 100 - lengthPercent }}>{bucket.retention}</div> </div> ); })} </div> </div> <div className={styles.step}> <div className={styles.stepHeading}>Step 2: Normalization</div> <div className={styles.stepDescription}> Normalization happens when series with different intervals between points are combined. </div> {meta['aggnum-norm'] > 1 && <p>Normalization did occur using {normFunc}</p>} {meta['aggnum-norm'] === 1 && <p>No normalization was needed</p>} </div> <div className={styles.step}> <div className={styles.stepHeading}>Step 3: Runtime consolidation</div> <div className={styles.stepDescription}> If there are too many data points at this point Metrictank will consolidate them down to below max data points (set in queries tab). </div> {runtimeNotice && <p>{runtimeNotice.text}</p>} {!runtimeNotice && <p>No runtime consolidation</p>} </div> </div> </div> ); } render() { const { data } = this.props; // away to dedupe them const seriesMetas: Record<string, MetricTankSeriesMeta> = {}; for (const series of data) { const seriesMetaList: MetricTankSeriesMeta[] | undefined = series?.meta?.custom?.seriesMetaList; if (seriesMetaList) { for (const metaItem of seriesMetaList) { // key is to dedupe as many series will have identitical meta const key = `${JSON.stringify(metaItem)}`; if (seriesMetas[key]) { seriesMetas[key].count += metaItem.count; } else { seriesMetas[key] = metaItem; } } } } if (Object.keys(seriesMetas).length === 0) { return <div>No response meta data</div>; } return ( <div> <h2 className="page-heading">Metrictank Lineage</h2> {Object.keys(seriesMetas).map((key) => this.renderMeta(seriesMetas[key], key))} </div> ); } } const getStyles = stylesFactory(() => { const { theme } = config; const borderColor = theme.isDark ? theme.palette.gray25 : theme.palette.gray85; const background = theme.isDark ? theme.palette.dark1 : theme.palette.white; const headerBg = theme.isDark ? theme.palette.gray15 : theme.palette.gray85; return { metaItem: css({ background: background, border: `1px solid ${borderColor}`, marginBottom: theme.spacing.md, }), metaItemHeader: css({ background: headerBg, padding: `${theme.spacing.xs} ${theme.spacing.md}`, fontSize: theme.typography.size.md, display: 'flex', justifyContent: 'space-between', }), metaItemBody: css({ padding: theme.spacing.md, }), stepHeading: css({ fontSize: theme.typography.size.md, }), stepDescription: css({ fontSize: theme.typography.size.sm, color: theme.colors.textWeak, marginBottom: theme.spacing.sm, }), step: css({ marginBottom: theme.spacing.lg, '&:last-child': { marginBottom: 0, }, }), bucket: css({ display: 'flex', marginBottom: theme.spacing.sm, borderRadius: theme.border.radius.sm, }), bucketInterval: css({ flexGrow: 0, width: '60px', }), bucketRetention: css({ background: `linear-gradient(0deg, ${theme.palette.blue85}, ${theme.palette.blue95})`, textAlign: 'center', color: theme.palette.white, marginRight: theme.spacing.md, borderRadius: theme.border.radius.sm, }), bucketRetentionActive: css({ background: `linear-gradient(0deg, ${theme.palette.greenBase}, ${theme.palette.greenShade})`, }), }; });
.
Edit
..
Edit
AddGraphiteFunction.tsx
Edit
AnnotationsEditor.tsx
Edit
FunctionEditor.test.tsx
Edit
FunctionEditor.tsx
Edit
FunctionEditorControls.tsx
Edit
FunctionParamEditor.tsx
Edit
FunctionsSection.tsx
Edit
GraphiteFunctionEditor.tsx
Edit
GraphiteQueryEditor.tsx
Edit
GraphiteTextEditor.tsx
Edit
GraphiteVariableEditor.tsx
Edit
MetricSegment.tsx
Edit
MetricTankMetaInspector.tsx
Edit
MetricsSection.tsx
Edit
PlayButton.tsx
Edit
SeriesSection.tsx
Edit
TagEditor.tsx
Edit
TagsSection.tsx
Edit
helpers.test.ts
Edit
helpers.ts
Edit