1import React from 'react';
2import { LogsDedupStrategy, LogsMetaItem, LogsMetaKind, LogRowModel } from '@grafana/data';
3import { Button, Tooltip, Icon, LogLabels } from '@grafana/ui';
4import { MAX_CHARACTERS } from '@grafana/ui/src/components/Logs/LogRowMessage';
5import { MetaInfoText, MetaItemProps } from './MetaInfoText';
6
7export type Props = {
8  meta: LogsMetaItem[];
9  dedupStrategy: LogsDedupStrategy;
10  dedupCount: number;
11  showDetectedFields: string[];
12  hasUnescapedContent: boolean;
13  forceEscape: boolean;
14  logRows: LogRowModel[];
15  onEscapeNewlines: () => void;
16  clearDetectedFields: () => void;
17};
18
19export const LogsMetaRow: React.FC<Props> = React.memo(
20  ({
21    meta,
22    dedupStrategy,
23    dedupCount,
24    showDetectedFields,
25    clearDetectedFields,
26    hasUnescapedContent,
27    forceEscape,
28    onEscapeNewlines,
29    logRows,
30  }) => {
31    const logsMetaItem: Array<LogsMetaItem | MetaItemProps> = [...meta];
32
33    // Add deduplication info
34    if (dedupStrategy !== LogsDedupStrategy.none) {
35      logsMetaItem.push({
36        label: 'Dedup count',
37        value: dedupCount,
38        kind: LogsMetaKind.Number,
39      });
40    }
41    // Add info about limit for highlighting
42    if (logRows.some((r) => r.entry.length > MAX_CHARACTERS)) {
43      logsMetaItem.push({
44        label: 'Info',
45        value: 'Logs with more than 100,000 characters could not be parsed and highlighted',
46        kind: LogsMetaKind.String,
47      });
48    }
49
50    // Add detected fields info
51    if (showDetectedFields?.length > 0) {
52      logsMetaItem.push(
53        {
54          label: 'Showing only detected fields',
55          value: renderMetaItem(showDetectedFields, LogsMetaKind.LabelsMap),
56        },
57        {
58          label: '',
59          value: (
60            <Button variant="secondary" size="sm" onClick={clearDetectedFields}>
61              Show all detected fields
62            </Button>
63          ),
64        }
65      );
66    }
67
68    // Add unescaped content info
69    if (hasUnescapedContent) {
70      logsMetaItem.push({
71        label: 'Your logs might have incorrectly escaped content',
72        value: (
73          <Tooltip
74            content="We suggest to try to fix the escaping of your log lines first. This is an experimental feature, your logs might not be correctly escaped."
75            placement="right"
76          >
77            <Button variant="secondary" size="sm" onClick={onEscapeNewlines}>
78              <span>{forceEscape ? 'Remove escaping' : 'Escape newlines'}&nbsp;</span>
79              <Icon name="exclamation-triangle" className="muted" size="sm" />
80            </Button>
81          </Tooltip>
82        ),
83      });
84    }
85
86    return (
87      <>
88        {logsMetaItem && (
89          <MetaInfoText
90            metaItems={logsMetaItem.map((item) => {
91              return {
92                label: item.label,
93                value: 'kind' in item ? renderMetaItem(item.value, item.kind) : item.value,
94              };
95            })}
96          />
97        )}
98      </>
99    );
100  }
101);
102
103LogsMetaRow.displayName = 'LogsMetaRow';
104
105function renderMetaItem(value: any, kind: LogsMetaKind) {
106  if (kind === LogsMetaKind.LabelsMap) {
107    return (
108      <span className="logs-meta-item__labels">
109        <LogLabels labels={value} />
110      </span>
111    );
112  } else if (kind === LogsMetaKind.Error) {
113    return <span className="logs-meta-item__error">{value}</span>;
114  }
115  return value;
116}
117