Skip to content

Commit

Permalink
HPCC-32532 ECL Watch v9 support for Grafana/Loki logging
Browse files Browse the repository at this point in the history
adds support to ECL Watch v9 for displaying logs from Grafana/Loki

Signed-off-by: Jeremy Clements <[email protected]>
  • Loading branch information
jeclrsg committed Oct 8, 2024
1 parent a01fea2 commit f8e933a
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 26 deletions.
8 changes: 4 additions & 4 deletions esp/src/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion esp/src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"@hpcc-js/chart": "2.84.1",
"@hpcc-js/codemirror": "2.63.0",
"@hpcc-js/common": "2.72.0",
"@hpcc-js/comms": "2.96.1",
"@hpcc-js/comms": "2.97.0",
"@hpcc-js/dataflow": "8.1.7",
"@hpcc-js/eclwatch": "2.75.3",
"@hpcc-js/graph": "2.86.0",
Expand Down
60 changes: 41 additions & 19 deletions esp/src/src-react/components/Logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { CommandBar, ContextualMenuItemType, ICommandBarItemProps } from "@fluen
import { GetLogsExRequest, LogaccessService, TargetAudience, LogType } from "@hpcc-js/comms";
import { Level, scopedLogger } from "@hpcc-js/util";
import nlsHPCC from "src/nlsHPCC";
import { logColor, wuidToDate, wuidToTime } from "src/Utility";
import { logColor, timestampToDate, wuidToDate, wuidToTime } from "src/Utility";
import { useLoggingEngine } from "../hooks/platform";
import { HolyGrail } from "../layouts/HolyGrail";
import { pushParams } from "../util/history";
import { FluentGrid, useCopyButtons, useFluentStoreState, FluentColumns } from "./controls/Grid";
Expand Down Expand Up @@ -110,28 +111,49 @@ export const Logs: React.FunctionComponent<LogsProps> = ({

const now = React.useMemo(() => new Date(), []);

const loggingEngine = useLoggingEngine();

// Grid ---
const columns = React.useMemo((): FluentColumns => {
return {
timestamp: { label: nlsHPCC.TimeStamp, width: 140, sortable: false, },
message: { label: nlsHPCC.Message, width: 600, sortable: false, },
components: { label: nlsHPCC.ContainerName, width: 150, sortable: false },
instance: { label: nlsHPCC.PodName, width: 150, sortable: false },
audience: { label: nlsHPCC.Audience, width: 60, sortable: false, },
class: {
label: nlsHPCC.Class, width: 40, sortable: false,
formatter: level => {
const colors = logColor(levelMap(level));
const styles = { backgroundColor: colors.background, padding: "2px 6px", color: colors.foreground };
return <span style={styles}>{level}</span>;
}
let retVal = {
timestamp: {
label: nlsHPCC.TimeStamp, width: 140, sortable: false,
formatter: ts => {
if (ts) {
if (ts.indexOf(":") < 0) {
return timestampToDate(ts).toISOString();
}
return new Date(ts).toISOString();
}
},
},
workunits: { label: nlsHPCC.JobID, width: 50, sortable: false, hidden: wuid !== undefined, },
processid: { label: nlsHPCC.ProcessID, width: 75, sortable: false, },
logid: { label: nlsHPCC.Sequence, width: 70, sortable: false, },
threadid: { label: nlsHPCC.ThreadID, width: 60, sortable: false, },
message: { label: nlsHPCC.Message, width: 600, sortable: false, },
};
}, [wuid]);
if (loggingEngine === "grafanacurl") {
retVal = Object.assign(retVal, {
pod: { label: nlsHPCC.PodName, width: 150, sortable: false },
});
} else {
retVal = Object.assign(retVal, {
instance: { label: nlsHPCC.PodName, width: 150, sortable: false },
components: { label: nlsHPCC.ContainerName, width: 150, sortable: false },
audience: { label: nlsHPCC.Audience, width: 60, sortable: false, },
class: {
label: nlsHPCC.Class, width: 40, sortable: false,
formatter: level => {
const colors = logColor(levelMap(level));
const styles = { backgroundColor: colors.background, padding: "2px 6px", color: colors.foreground };
return <span style={styles}>{level}</span>;
}
},
workunits: { label: nlsHPCC.JobID, width: 50, sortable: false, hidden: wuid !== undefined, },
processid: { label: nlsHPCC.ProcessID, width: 75, sortable: false, },
logid: { label: nlsHPCC.Sequence, width: 70, sortable: false, },
threadid: { label: nlsHPCC.ThreadID, width: 60, sortable: false, },
});
}
return retVal;
}, [loggingEngine, wuid]);

const copyButtons = useCopyButtons(columns, selection, "logaccess");

Expand Down
16 changes: 14 additions & 2 deletions esp/src/src-react/hooks/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import * as React from "react";
import { Octokit } from "octokit";
import { useConst } from "@fluentui/react-hooks";
import { scopedLogger } from "@hpcc-js/util";
import { Topology, WsTopology, WorkunitsServiceEx } from "@hpcc-js/comms";
import { LogaccessService, Topology, WsTopology, WorkunitsServiceEx } from "@hpcc-js/comms";
import { getBuildInfo, BuildInfo, fetchModernMode } from "src/Session";
import { cmake_build_type, containerized, ModernMode } from "src/BuildInfo";
import { sessionKeyValStore, userKeyValStore } from "src/KeyValStore";
import { Palette } from "@hpcc-js/common";

const logger = scopedLogger("src-react/hooks/platform.ts");

export const service = new LogaccessService({ baseUrl: "" });

declare const dojoConfig;

export function useBuildInfo(): [BuildInfo, { isContainer: boolean, currencyCode: string, opsCategory: string }] {
Expand Down Expand Up @@ -205,4 +207,14 @@ export function useModernMode(): {
}, [modernMode, sessionStore, userStore]);

return { modernMode, setModernMode };
}
}

export function useLoggingEngine(): string {
const [loggingEngine, setLoggingEngine] = React.useState("");

React.useEffect(() => {
service.GetLogAccessInfo({}).then(response => setLoggingEngine(response.RemoteLogManagerType ?? ""));
}, []);

return loggingEngine;
}
15 changes: 15 additions & 0 deletions esp/src/src/Utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,21 @@ export function format(labelTpl, obj) {
.join("\n")
;
}

const TEN_TRILLION = 10000000000000;
export function nanosToMillis(timestamp: number): number {
if (timestamp > TEN_TRILLION) {
return Math.round(timestamp / 1000000);
} else {
return timestamp;
}
}

export function timestampToDate(timestamp: number): Date {
const millis = nanosToMillis(timestamp);
return new Date(millis);
}

const theme = getTheme();
const { semanticColors } = theme;

Expand Down

0 comments on commit f8e933a

Please sign in to comment.