MetricTankMetaInspector.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import { css, cx } from '@emotion/css';
  2. import React, { PureComponent } from 'react';
  3. import { MetadataInspectorProps, rangeUtil } from '@grafana/data';
  4. import { stylesFactory } from '@grafana/ui';
  5. import { config } from 'app/core/config';
  6. import { GraphiteDatasource } from '../datasource';
  7. import { parseSchemaRetentions, getRollupNotice, getRuntimeConsolidationNotice } from '../meta';
  8. import { GraphiteQuery, GraphiteOptions, MetricTankSeriesMeta } from '../types';
  9. export type Props = MetadataInspectorProps<GraphiteDatasource, GraphiteQuery, GraphiteOptions>;
  10. export interface State {
  11. index: number;
  12. }
  13. export class MetricTankMetaInspector extends PureComponent<Props, State> {
  14. renderMeta(meta: MetricTankSeriesMeta, key: string) {
  15. const styles = getStyles();
  16. const buckets = parseSchemaRetentions(meta['schema-retentions']);
  17. const rollupNotice = getRollupNotice([meta]);
  18. const runtimeNotice = getRuntimeConsolidationNotice([meta]);
  19. const normFunc = (meta['consolidator-normfetch'] ?? '').replace('Consolidator', '');
  20. const totalSeconds = buckets.reduce(
  21. (acc, bucket) => acc + (bucket.retention ? rangeUtil.intervalToSeconds(bucket.retention) : 0),
  22. 0
  23. );
  24. return (
  25. <div className={styles.metaItem} key={key}>
  26. <div className={styles.metaItemHeader}>
  27. Schema: {meta['schema-name']}
  28. <div className="small muted">Series count: {meta.count}</div>
  29. </div>
  30. <div className={styles.metaItemBody}>
  31. <div className={styles.step}>
  32. <div className={styles.stepHeading}>Step 1: Fetch</div>
  33. <div className={styles.stepDescription}>
  34. First data is fetched, either from raw data archive or a rollup archive
  35. </div>
  36. {rollupNotice && <p>{rollupNotice.text}</p>}
  37. {!rollupNotice && <p>No rollup archive was used</p>}
  38. <div>
  39. {buckets.map((bucket, index) => {
  40. const bucketLength = bucket.retention ? rangeUtil.intervalToSeconds(bucket.retention) : 0;
  41. const lengthPercent = (bucketLength / totalSeconds) * 100;
  42. const isActive = index === meta['archive-read'];
  43. return (
  44. <div key={bucket.retention} className={styles.bucket}>
  45. <div className={styles.bucketInterval}>{bucket.interval}</div>
  46. <div
  47. className={cx(styles.bucketRetention, { [styles.bucketRetentionActive]: isActive })}
  48. style={{ flexGrow: lengthPercent }}
  49. />
  50. <div style={{ flexGrow: 100 - lengthPercent }}>{bucket.retention}</div>
  51. </div>
  52. );
  53. })}
  54. </div>
  55. </div>
  56. <div className={styles.step}>
  57. <div className={styles.stepHeading}>Step 2: Normalization</div>
  58. <div className={styles.stepDescription}>
  59. Normalization happens when series with different intervals between points are combined.
  60. </div>
  61. {meta['aggnum-norm'] > 1 && <p>Normalization did occur using {normFunc}</p>}
  62. {meta['aggnum-norm'] === 1 && <p>No normalization was needed</p>}
  63. </div>
  64. <div className={styles.step}>
  65. <div className={styles.stepHeading}>Step 3: Runtime consolidation</div>
  66. <div className={styles.stepDescription}>
  67. If there are too many data points at this point Metrictank will consolidate them down to below max data
  68. points (set in queries tab).
  69. </div>
  70. {runtimeNotice && <p>{runtimeNotice.text}</p>}
  71. {!runtimeNotice && <p>No runtime consolidation</p>}
  72. </div>
  73. </div>
  74. </div>
  75. );
  76. }
  77. render() {
  78. const { data } = this.props;
  79. // away to dedupe them
  80. const seriesMetas: Record<string, MetricTankSeriesMeta> = {};
  81. for (const series of data) {
  82. if (series.meta && series.meta.custom) {
  83. for (const metaItem of series.meta.custom.seriesMetaList as MetricTankSeriesMeta[]) {
  84. // key is to dedupe as many series will have identitical meta
  85. const key = `${JSON.stringify(metaItem)}`;
  86. if (seriesMetas[key]) {
  87. seriesMetas[key].count += metaItem.count;
  88. } else {
  89. seriesMetas[key] = metaItem;
  90. }
  91. }
  92. }
  93. }
  94. if (Object.keys(seriesMetas).length === 0) {
  95. return <div>No response meta data</div>;
  96. }
  97. return (
  98. <div>
  99. <h2 className="page-heading">Metrictank Lineage</h2>
  100. {Object.keys(seriesMetas).map((key) => this.renderMeta(seriesMetas[key], key))}
  101. </div>
  102. );
  103. }
  104. }
  105. const getStyles = stylesFactory(() => {
  106. const { theme } = config;
  107. const borderColor = theme.isDark ? theme.palette.gray25 : theme.palette.gray85;
  108. const background = theme.isDark ? theme.palette.dark1 : theme.palette.white;
  109. const headerBg = theme.isDark ? theme.palette.gray15 : theme.palette.gray85;
  110. return {
  111. metaItem: css`
  112. background: ${background};
  113. border: 1px solid ${borderColor};
  114. margin-bottom: ${theme.spacing.md};
  115. `,
  116. metaItemHeader: css`
  117. background: ${headerBg};
  118. padding: ${theme.spacing.xs} ${theme.spacing.md};
  119. font-size: ${theme.typography.size.md};
  120. display: flex;
  121. justify-content: space-between;
  122. `,
  123. metaItemBody: css`
  124. padding: ${theme.spacing.md};
  125. `,
  126. stepHeading: css`
  127. font-size: ${theme.typography.size.md};
  128. `,
  129. stepDescription: css`
  130. font-size: ${theme.typography.size.sm};
  131. color: ${theme.colors.textWeak};
  132. margin-bottom: ${theme.spacing.sm};
  133. `,
  134. step: css`
  135. margin-bottom: ${theme.spacing.lg};
  136. &:last-child {
  137. margin-bottom: 0;
  138. }
  139. `,
  140. bucket: css`
  141. display: flex;
  142. margin-bottom: ${theme.spacing.sm};
  143. border-radius: ${theme.border.radius.md};
  144. `,
  145. bucketInterval: css`
  146. flex-grow: 0;
  147. width: 60px;
  148. `,
  149. bucketRetention: css`
  150. background: linear-gradient(0deg, ${theme.palette.blue85}, ${theme.palette.blue95});
  151. text-align: center;
  152. color: ${theme.palette.white};
  153. margin-right: ${theme.spacing.md};
  154. border-radius: ${theme.border.radius.md};
  155. `,
  156. bucketRetentionActive: css`
  157. background: linear-gradient(0deg, ${theme.palette.greenBase}, ${theme.palette.greenShade});
  158. `,
  159. };
  160. });