This repository has been archived by the owner on Jul 31, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* FLOW-972 POC with nikhils logview code * FLOW-972 percentage status added * FLOW-972 search keyword feature * FLOW-972 debounce added in search input * FLOW-972 close search bar icon added * FLOW-972 log-level filter added * FLOW-972 code segregation * FLOW-972 goto line feature * FLOW-972 loglevels made configurable and stories updated * FLOW-972 f-log playground updated * FLOW-972 highlight custom keywords feature added with its respective story * FLOW-972 unit tests added * FLOW-972 sonar bug * FLOW-972 lint checks fixed * FLOW-972 changelogs updated * FLOW-972 rebased * FLOW-972 changelog updated
- Loading branch information
1 parent
19fee15
commit ee89b32
Showing
18 changed files
with
913 additions
and
545 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,21 @@ | ||
@import "xterm/css/xterm.css"; | ||
@import "../../../../flow-core/src/mixins/scss/mixins"; | ||
|
||
f-log { | ||
@include base(); | ||
display: flex; | ||
flex: 1 0 auto; | ||
width: 100%; | ||
max-width: 100%; | ||
max-height: 100%; | ||
flex-direction: column; | ||
} | ||
f-div[direction="column"] { | ||
> f-log { | ||
width: 100%; | ||
} | ||
} | ||
|
||
f-div[direction="row"] { | ||
> f-log { | ||
height: 100%; | ||
} | ||
} |
126 changes: 126 additions & 0 deletions
126
packages/flow-log/src/components/f-log/f-log-search-and-filters.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import { FLog } from "./f-log"; | ||
import * as Mark from "mark.js"; | ||
|
||
export function closeSearchBar(this: FLog) { | ||
this.showToolbar = false; | ||
this.highlightText(""); | ||
} | ||
|
||
/** | ||
* Filter lines based on selected log level | ||
* @param filterText | ||
*/ | ||
export function filterLines(this: FLog, filterText: string) { | ||
if (filterText.trim() !== "") { | ||
const lines = this.shadowRoot?.querySelectorAll(".log-line"); | ||
if (lines) { | ||
lines.forEach(element => { | ||
if (!element.textContent?.toLowerCase()?.includes(filterText)) { | ||
element.classList.add("hidden"); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
/** | ||
* clear filtered lines | ||
*/ | ||
export function clearFilter(this: FLog) { | ||
const lines = this.shadowRoot?.querySelectorAll(".log-line.hidden"); | ||
if (lines) { | ||
lines.forEach(element => { | ||
element.classList.remove("hidden"); | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* It will highlight text present in logs with mark tag | ||
* @param searchText | ||
*/ | ||
export function highlightText(this: FLog, searchText: string): void { | ||
if (this.searchDebounceTimeout) { | ||
clearTimeout(this.searchDebounceTimeout); | ||
} | ||
this.searchDebounceTimeout = setTimeout(() => { | ||
const markInstance = new Mark( | ||
this.shadowRoot?.querySelectorAll(".log-line:not(.hidden)") as NodeListOf<HTMLSpanElement> | ||
); | ||
if (this.lastSearchValue && this.lastSearchValue !== "") { | ||
markInstance.unmark(this.lastSearchValue as unknown as HTMLElement); | ||
} | ||
this.lastSearchValue = searchText; | ||
if (searchText && searchText !== "") { | ||
markInstance.mark(searchText, { | ||
done: (occurrences: number) => { | ||
this.searchOccurrences = occurrences; | ||
const firstMark = this.shadowRoot?.querySelector("mark[data-markjs='true']"); | ||
firstMark?.scrollIntoView({ block: "start", behavior: "smooth" }); | ||
firstMark?.classList.add("active"); | ||
this.currentMarkIndex = 0; | ||
if (occurrences > 0 && this.searchInput) { | ||
this.searchInput.suggestElement.suffix = `1 of ${occurrences}`; | ||
} else if (this.searchInput) { | ||
this.searchInput.suggestElement.suffix = `No results`; | ||
} | ||
} | ||
}); | ||
} else { | ||
if (this.searchInput) this.searchInput.suggestElement.suffix = undefined; | ||
this.searchOccurrences = 0; | ||
} | ||
}, 500); | ||
} | ||
|
||
/** | ||
* when next arrow is clicked, it will go to next highlighted mark | ||
*/ | ||
export function nextMark(this: FLog) { | ||
if (this.searchOccurrences > 0 && this.allMarks) { | ||
this.allMarks[this.currentMarkIndex].classList.remove("active"); | ||
this.currentMarkIndex += 1; | ||
if (this.currentMarkIndex === this.allMarks.length) { | ||
this.currentMarkIndex = 0; | ||
} | ||
this.allMarks[this.currentMarkIndex].scrollIntoView({ block: "start", behavior: "smooth" }); | ||
this.allMarks[this.currentMarkIndex].classList.add("active"); | ||
if (this.searchInput) { | ||
this.searchInput.suggestElement.suffix = `${this.currentMarkIndex + 1} of ${ | ||
this.searchOccurrences | ||
}`; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* when previous arrow is clicked, it will go to previous highlighted mark | ||
*/ | ||
export function prevMark(this: FLog) { | ||
if (this.searchOccurrences > 0 && this.allMarks) { | ||
this.allMarks[this.currentMarkIndex].classList.remove("active"); | ||
this.currentMarkIndex -= 1; | ||
if (this.currentMarkIndex === -1) { | ||
this.currentMarkIndex = this.allMarks.length - 1; | ||
} | ||
this.allMarks[this.currentMarkIndex].scrollIntoView({ block: "start", behavior: "smooth" }); | ||
this.allMarks[this.currentMarkIndex].classList.add("active"); | ||
if (this.searchInput) { | ||
this.searchInput.suggestElement.suffix = `${this.currentMarkIndex + 1} of ${ | ||
this.searchOccurrences | ||
}`; | ||
} | ||
} | ||
} | ||
|
||
export function handleLogLevelFilter(this: FLog, event: CustomEvent<{ scope: string }>) { | ||
this.clearFilter(); | ||
const logLevel = event.detail.scope; | ||
if (logLevel && logLevel !== "ALL") { | ||
this.filterLines(logLevel.toLowerCase()); | ||
} | ||
} | ||
|
||
export function handleSearch(this: FLog, event: CustomEvent<{ value: string; scope: string }>) { | ||
this.highlightText(event.detail.value); | ||
this.handleLogLevelFilter(event); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
export type HexColor = `#${string}`; | ||
export type HighlightKeywords = Record<string, HexColor>; | ||
// Adds some general formatting/highlighting to logs | ||
export function formatLogLine(logLine: string, highlightKeywords?: HighlightKeywords): string { | ||
// Highlight [xxx] with a greyed out version | ||
let newLine = logLine | ||
// Highlight quoted strings | ||
.replace(/("[^"]*?"|'[^']*?')/g, '<span style="color: var(--color-warning-subtle)">$1</span>') | ||
|
||
// Highlight bracket contents | ||
// 100 is an arbitrary limit to prevent catastrophic backtracking in Regex | ||
.replace( | ||
/(\[[^\]]{0,100}\])/g, | ||
'<span style="color:var(--color-text-subtle); font-weight: bold;">$1</span>' | ||
) | ||
|
||
// Highlight potential dates (YYYY/MM/DD HH:MM:SS) | ||
.replace( | ||
/(\d{1,4}\/\d{1,2}\/\d{1,4}(?: \d{1,2}:\d{1,2}:\d{1,2})?)/g, | ||
'<span style="color: #68c9f2">$1</span>' | ||
) | ||
|
||
// Highlight potential dates (YYYY-MM-DD with timezone) | ||
.replace( | ||
/(\d{1,4}-\d{1,2}-\d{1,4}(?:\s?T?\d{1,2}:\d{1,2}:[\d.]{1,10})?Z?)/g, | ||
'<span style="color: #68c9f2">$1</span>' | ||
) | ||
|
||
// Highlight YAML keys | ||
// 100 is an arbitrary limit to prevent catastrophic backtracking in Regex | ||
.replace( | ||
/(^\s*[-\sa-z0-9_]{1,100}:\s)/gi, | ||
'<span style="color:var(--color-primary-default)">$1</span>' | ||
) | ||
|
||
// Highlight urls | ||
.replace( | ||
/((https?:\/\/)([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}(:[0-9]+)?(\/[^" ]*)?)/g, | ||
'<a style="color: var(--color-primary-default); text-decoration: underline" href="$1" rel="noopener" target="_blank">$1</a>' | ||
); | ||
|
||
if (highlightKeywords) { | ||
Object.entries(highlightKeywords).forEach(([keyword, color]) => { | ||
if (isValidHexColor(color)) { | ||
newLine = newLine.replace( | ||
new RegExp(`(${keyword})`), | ||
`<span style="color:${color}">$1</span>` | ||
); | ||
} else { | ||
console.warn(`${color as string} is not valid hex`); | ||
} | ||
}); | ||
} | ||
|
||
if (/(ERROR|FAILED)/gi.test(newLine)) { | ||
newLine = `<span style="background: var(--color-danger-subtle)">${newLine}</span>`; | ||
} | ||
|
||
return newLine; | ||
} | ||
|
||
export function isValidHexColor(value: string): value is HexColor { | ||
const hexColorRegex = /^#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/; | ||
return hexColorRegex.test(value); | ||
} | ||
|
||
export function getUniqueStringId() { | ||
return `${new Date().valueOf()}`; | ||
} |
Oops, something went wrong.