LiveLogs.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import React, { PureComponent } from 'react';
  2. import { css, cx } from 'emotion';
  3. import { Themeable, withTheme, GrafanaTheme, selectThemeVariant, LinkButton, getLogRowStyles } from '@grafana/ui';
  4. import { LogsModel, LogRowModel, TimeZone } from '@grafana/data';
  5. import ElapsedTime from './ElapsedTime';
  6. const getStyles = (theme: GrafanaTheme) => ({
  7. logsRowsLive: css`
  8. label: logs-rows-live;
  9. display: flex;
  10. flex-flow: column nowrap;
  11. height: 65vh;
  12. overflow-y: auto;
  13. :first-child {
  14. margin-top: auto !important;
  15. }
  16. `,
  17. logsRowFresh: css`
  18. label: logs-row-fresh;
  19. color: ${theme.colors.text};
  20. background-color: ${selectThemeVariant({ light: theme.colors.gray6, dark: theme.colors.gray1 }, theme.type)};
  21. `,
  22. logsRowOld: css`
  23. label: logs-row-old;
  24. opacity: 0.8;
  25. `,
  26. logsRowsIndicator: css`
  27. font-size: ${theme.typography.size.md};
  28. padding: ${theme.spacing.sm} 0;
  29. display: flex;
  30. align-items: center;
  31. `,
  32. });
  33. export interface Props extends Themeable {
  34. logsResult?: LogsModel;
  35. timeZone: TimeZone;
  36. stopLive: () => void;
  37. }
  38. class LiveLogs extends PureComponent<Props> {
  39. private liveEndDiv: HTMLDivElement = null;
  40. componentDidUpdate(prevProps: Props) {
  41. if (this.liveEndDiv) {
  42. this.liveEndDiv.scrollIntoView(false);
  43. }
  44. }
  45. render() {
  46. const { theme, timeZone } = this.props;
  47. const styles = getStyles(theme);
  48. const rowsToRender: LogRowModel[] = this.props.logsResult ? this.props.logsResult.rows : [];
  49. const showUtc = timeZone === 'utc';
  50. const { logsRow, logsRowLocalTime, logsRowMessage } = getLogRowStyles(theme);
  51. return (
  52. <>
  53. <div className={cx(['logs-rows', styles.logsRowsLive])}>
  54. {rowsToRender.map((row: any, index) => {
  55. return (
  56. <div
  57. className={row.fresh ? cx([logsRow, styles.logsRowFresh]) : cx([logsRow, styles.logsRowOld])}
  58. key={`${row.timeEpochMs}-${index}`}
  59. >
  60. {showUtc && (
  61. <div className={cx([logsRowLocalTime])} title={`Local: ${row.timeLocal} (${row.timeFromNow})`}>
  62. {row.timeUtc}
  63. </div>
  64. )}
  65. {!showUtc && (
  66. <div className={cx([logsRowLocalTime])} title={`${row.timeUtc} (${row.timeFromNow})`}>
  67. {row.timeLocal}
  68. </div>
  69. )}
  70. <div className={cx([logsRowMessage])}>{row.entry}</div>
  71. </div>
  72. );
  73. })}
  74. <div
  75. ref={element => {
  76. this.liveEndDiv = element;
  77. if (this.liveEndDiv) {
  78. this.liveEndDiv.scrollIntoView(false);
  79. }
  80. }}
  81. />
  82. </div>
  83. <div className={cx([styles.logsRowsIndicator])}>
  84. <span>
  85. Last line received: <ElapsedTime resetKey={this.props.logsResult} humanize={true} /> ago
  86. </span>
  87. <LinkButton
  88. onClick={this.props.stopLive}
  89. size="md"
  90. variant="transparent"
  91. style={{ color: theme.colors.orange }}
  92. >
  93. Stop Live
  94. </LinkButton>
  95. </div>
  96. </>
  97. );
  98. }
  99. }
  100. export const LiveLogsWithTheme = withTheme(LiveLogs);