RuleStats.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import pluralize from 'pluralize';
  2. import React, { FC, Fragment, useMemo } from 'react';
  3. import { CombinedRule, CombinedRuleGroup, CombinedRuleNamespace } from 'app/types/unified-alerting';
  4. import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
  5. import { isAlertingRule, isRecordingRule, isRecordingRulerRule } from '../../utils/rules';
  6. import { StateColoredText } from '../StateColoredText';
  7. interface Props {
  8. showInactive?: boolean;
  9. showRecording?: boolean;
  10. group?: CombinedRuleGroup;
  11. namespaces?: CombinedRuleNamespace[];
  12. }
  13. const emptyStats = {
  14. total: 0,
  15. recording: 0,
  16. [PromAlertingRuleState.Firing]: 0,
  17. [PromAlertingRuleState.Pending]: 0,
  18. [PromAlertingRuleState.Inactive]: 0,
  19. error: 0,
  20. } as const;
  21. export const RuleStats: FC<Props> = ({ showInactive, showRecording, group, namespaces }) => {
  22. const calculated = useMemo(() => {
  23. const stats = { ...emptyStats };
  24. const calcRule = (rule: CombinedRule) => {
  25. if (rule.promRule && isAlertingRule(rule.promRule)) {
  26. stats[rule.promRule.state] += 1;
  27. }
  28. if (rule.promRule?.health === 'err' || rule.promRule?.health === 'error') {
  29. stats.error += 1;
  30. }
  31. if (
  32. (rule.promRule && isRecordingRule(rule.promRule)) ||
  33. (rule.rulerRule && isRecordingRulerRule(rule.rulerRule))
  34. ) {
  35. stats.recording += 1;
  36. }
  37. stats.total += 1;
  38. };
  39. if (group) {
  40. group.rules.forEach(calcRule);
  41. }
  42. if (namespaces) {
  43. namespaces.forEach((namespace) => namespace.groups.forEach((group) => group.rules.forEach(calcRule)));
  44. }
  45. return stats;
  46. }, [group, namespaces]);
  47. const statsComponents: React.ReactNode[] = [];
  48. if (calculated[PromAlertingRuleState.Firing]) {
  49. statsComponents.push(
  50. <StateColoredText key="firing" status={PromAlertingRuleState.Firing}>
  51. {calculated[PromAlertingRuleState.Firing]} firing
  52. </StateColoredText>
  53. );
  54. }
  55. if (calculated.error) {
  56. statsComponents.push(
  57. <StateColoredText key="errors" status={PromAlertingRuleState.Firing}>
  58. {calculated.error} errors
  59. </StateColoredText>
  60. );
  61. }
  62. if (calculated[PromAlertingRuleState.Pending]) {
  63. statsComponents.push(
  64. <StateColoredText key="pending" status={PromAlertingRuleState.Pending}>
  65. {calculated[PromAlertingRuleState.Pending]} pending
  66. </StateColoredText>
  67. );
  68. }
  69. if (showInactive && calculated[PromAlertingRuleState.Inactive]) {
  70. statsComponents.push(
  71. <StateColoredText key="inactive" status="neutral">
  72. {calculated[PromAlertingRuleState.Inactive]} normal
  73. </StateColoredText>
  74. );
  75. }
  76. if (showRecording && calculated.recording) {
  77. statsComponents.push(
  78. <StateColoredText key="recording" status="neutral">
  79. {calculated.recording} recording
  80. </StateColoredText>
  81. );
  82. }
  83. return (
  84. <div>
  85. <span>
  86. {calculated.total} {pluralize('rule', calculated.total)}
  87. </span>
  88. {!!statsComponents.length && (
  89. <>
  90. <span>: </span>
  91. {statsComponents.reduce<React.ReactNode[]>(
  92. (prev, curr, idx) =>
  93. prev.length
  94. ? [
  95. prev,
  96. <Fragment key={idx}>
  97. <span>, </span>
  98. </Fragment>,
  99. curr,
  100. ]
  101. : [curr],
  102. []
  103. )}
  104. </>
  105. )}
  106. </div>
  107. );
  108. };