Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(ui): introduce a logger proxy - WF-85 #704

Merged
merged 1 commit into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/ui/src/components/core/content/CoreDataframe.vue
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ import {
} from "./CoreDataframe/constants";
import WdsButton from "@/wds/WdsButton.vue";
import { WdsColor } from "@/wds/tokens";
import { useLogger } from "@/composables/useLogger";

const description = "A component to display Pandas DataFrames.";

Expand Down Expand Up @@ -523,8 +524,7 @@ async function loadData() {
[ARQUERO_INTERNAL_ID]: () => aq.op.row_number(),
});
} catch (e) {
// eslint-disable-next-line no-console
console.error("Couldn't load dataframe from Arrow URL.", e);
useLogger().error("Couldn't load dataframe from Arrow URL.", e);
} finally {
isLoadingData.value = false;
}
Expand Down
5 changes: 3 additions & 2 deletions src/ui/src/components/core/content/CoreDataframe/useJobs.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useLogger } from "@/composables/useLogger";
import { shallowRef, ref, readonly } from "vue";

/**
Expand All @@ -6,6 +7,7 @@ import { shallowRef, ref, readonly } from "vue";
export function useJobs<T>(handler: (value: T) => Promise<void>) {
const jobs = shallowRef<T[]>([]);
const isRunning = ref(false);
const logger = useLogger();

async function run() {
if (isRunning.value) return;
Expand All @@ -18,8 +20,7 @@ export function useJobs<T>(handler: (value: T) => Promise<void>) {
try {
await handler(job);
} catch (error) {
// eslint-disable-next-line no-console
console.error("Error during handling job", job, error);
logger.error("Error during handling job", job, error);
} finally {
jobs.value = rest;
}
Expand Down
4 changes: 2 additions & 2 deletions src/ui/src/components/core/content/CoreDataframeLegacy.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ import WdsTextInput from "@/wds/WdsTextInput.vue";
import WdsControl from "@/wds/WdsControl.vue";
import BaseMarkdown from "../base/BaseMarkdown.vue";
import { WdsColor } from "@/wds/tokens";
import { useLogger } from "@/composables/useLogger";

const description = "A component to display Pandas DataFrames.";
const defaultDataframe = `data:application/vnd.apache.arrow.file;base64,QVJST1cxAAD/////iAMAABAAAAAAAAoADgAGAAUACAAKAAAAAAEEABAAAAAAAAoADAAAAAQACAAKAAAAlAIAAAQAAAABAAAADAAAAAgADAAEAAgACAAAAGwCAAAEAAAAXwIAAHsiaW5kZXhfY29sdW1ucyI6IFsiX19pbmRleF9sZXZlbF8wX18iXSwgImNvbHVtbl9pbmRleGVzIjogW3sibmFtZSI6IG51bGwsICJmaWVsZF9uYW1lIjogbnVsbCwgInBhbmRhc190eXBlIjogInVuaWNvZGUiLCAibnVtcHlfdHlwZSI6ICJvYmplY3QiLCAibWV0YWRhdGEiOiB7ImVuY29kaW5nIjogIlVURi04In19XSwgImNvbHVtbnMiOiBbeyJuYW1lIjogImNvbF9hIiwgImZpZWxkX25hbWUiOiAiY29sX2EiLCAicGFuZGFzX3R5cGUiOiAiaW50NjQiLCAibnVtcHlfdHlwZSI6ICJpbnQ2NCIsICJtZXRhZGF0YSI6IG51bGx9LCB7Im5hbWUiOiAiY29sX2IiLCAiZmllbGRfbmFtZSI6ICJjb2xfYiIsICJwYW5kYXNfdHlwZSI6ICJpbnQ2NCIsICJudW1weV90eXBlIjogImludDY0IiwgIm1ldGFkYXRhIjogbnVsbH0sIHsibmFtZSI6IG51bGwsICJmaWVsZF9uYW1lIjogIl9faW5kZXhfbGV2ZWxfMF9fIiwgInBhbmRhc190eXBlIjogImludDY0IiwgIm51bXB5X3R5cGUiOiAiaW50NjQiLCAibWV0YWRhdGEiOiBudWxsfV0sICJjcmVhdG9yIjogeyJsaWJyYXJ5IjogInB5YXJyb3ciLCAidmVyc2lvbiI6ICIxMi4wLjAifSwgInBhbmRhc192ZXJzaW9uIjogIjEuNS4zIn0ABgAAAHBhbmRhcwAAAwAAAIgAAABEAAAABAAAAJT///8AAAECEAAAACQAAAAEAAAAAAAAABEAAABfX2luZGV4X2xldmVsXzBfXwAAAJD///8AAAABQAAAAND///8AAAECEAAAABgAAAAEAAAAAAAAAAUAAABjb2xfYgAAAMD///8AAAABQAAAABAAFAAIAAYABwAMAAAAEAAQAAAAAAABAhAAAAAgAAAABAAAAAAAAAAFAAAAY29sX2EAAAAIAAwACAAHAAgAAAAAAAABQAAAAAAAAAD/////6AAAABQAAAAAAAAADAAWAAYABQAIAAwADAAAAAADBAAYAAAAMAAAAAAAAAAAAAoAGAAMAAQACAAKAAAAfAAAABAAAAACAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAABAAAAAAAAAAAAAAAAMAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAD/////AAAAABAAAAAMABQABgAIAAwAEAAMAAAAAAAEADwAAAAoAAAABAAAAAEAAACYAwAAAAAAAPAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAACgAMAAAABAAIAAoAAACUAgAABAAAAAEAAAAMAAAACAAMAAQACAAIAAAAbAIAAAQAAABfAgAAeyJpbmRleF9jb2x1bW5zIjogWyJfX2luZGV4X2xldmVsXzBfXyJdLCAiY29sdW1uX2luZGV4ZXMiOiBbeyJuYW1lIjogbnVsbCwgImZpZWxkX25hbWUiOiBudWxsLCAicGFuZGFzX3R5cGUiOiAidW5pY29kZSIsICJudW1weV90eXBlIjogIm9iamVjdCIsICJtZXRhZGF0YSI6IHsiZW5jb2RpbmciOiAiVVRGLTgifX1dLCAiY29sdW1ucyI6IFt7Im5hbWUiOiAiY29sX2EiLCAiZmllbGRfbmFtZSI6ICJjb2xfYSIsICJwYW5kYXNfdHlwZSI6ICJpbnQ2NCIsICJudW1weV90eXBlIjogImludDY0IiwgIm1ldGFkYXRhIjogbnVsbH0sIHsibmFtZSI6ICJjb2xfYiIsICJmaWVsZF9uYW1lIjogImNvbF9iIiwgInBhbmRhc190eXBlIjogImludDY0IiwgIm51bXB5X3R5cGUiOiAiaW50NjQiLCAibWV0YWRhdGEiOiBudWxsfSwgeyJuYW1lIjogbnVsbCwgImZpZWxkX25hbWUiOiAiX19pbmRleF9sZXZlbF8wX18iLCAicGFuZGFzX3R5cGUiOiAiaW50NjQiLCAibnVtcHlfdHlwZSI6ICJpbnQ2NCIsICJtZXRhZGF0YSI6IG51bGx9XSwgImNyZWF0b3IiOiB7ImxpYnJhcnkiOiAicHlhcnJvdyIsICJ2ZXJzaW9uIjogIjEyLjAuMCJ9LCAicGFuZGFzX3ZlcnNpb24iOiAiMS41LjMifQAGAAAAcGFuZGFzAAADAAAAiAAAAEQAAAAEAAAAlP///wAAAQIQAAAAJAAAAAQAAAAAAAAAEQAAAF9faW5kZXhfbGV2ZWxfMF9fAAAAkP///wAAAAFAAAAA0P///wAAAQIQAAAAGAAAAAQAAAAAAAAABQAAAGNvbF9iAAAAwP///wAAAAFAAAAAEAAUAAgABgAHAAwAAAAQABAAAAAAAAECEAAAACAAAAAEAAAAAAAAAAUAAABjb2xfYQAAAAgADAAIAAcACAAAAAAAAAFAAAAAsAMAAEFSUk9XMQ==`;
Expand Down Expand Up @@ -419,8 +420,7 @@ async function loadData() {
baseTable = aqTable;
table.value = baseTable;
} catch (e) {
// eslint-disable-next-line no-console
console.error("Couldn't load dataframe from Arrow URL.", e);
useLogger().error("Couldn't load dataframe from Arrow URL.", e);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/ui/src/components/core/embed/CoreMapbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<script lang="ts">
import { FieldType } from "@/writerTypes";
import { cssClasses } from "@/renderer/sharedStyleFields";
import { useLogger } from "@/composables/useLogger";

const markersDefaultData = [
{ lat: 37.79322359164316, lng: -122.39999318828129, name: "Marker" },
Expand Down Expand Up @@ -137,8 +138,7 @@ const initMap = async () => {
map.value.on("load", renderMarkers);
}
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
useLogger().error(error);
}
};

Expand Down
9 changes: 5 additions & 4 deletions src/ui/src/components/core/other/CoreWebcamCapture.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
} from "@/renderer/sharedStyleFields";
import WdsButton from "@/wds/WdsButton.vue";
import WdsDropdownInput from "@/wds/WdsDropdownInput.vue";
import { useLogger } from "@/composables/useLogger";

const description =
"A user input component that allows users to capture images using their webcam.";
Expand Down Expand Up @@ -106,6 +107,8 @@ const fields = inject(injectionKeys.evaluatedFields);
const videoDevices: Ref<MediaDeviceInfo[]> = ref(null);
const preferredDeviceId: Ref<MediaDeviceInfo["deviceId"]> = ref(null);

const logger = useLogger();

onMounted(async () => {
videoDevices.value = await getVideoDevices();
preferredDeviceId.value = videoDevices.value?.[0]?.deviceId;
Expand Down Expand Up @@ -174,8 +177,7 @@ const startCapture = async (): Promise<void> => {
videoEl.value.style.display = "";
return new Promise((resolve, reject) => {
if (!navigator.mediaDevices.getUserMedia) {
// eslint-disable-next-line no-console
console.error("This browser doesn't support webcam connection.");
logger.error("This browser doesn't support webcam connection.");
reject();
}

Expand All @@ -201,8 +203,7 @@ const startCapture = async (): Promise<void> => {
resolve();
})
.catch((error) => {
// eslint-disable-next-line no-console
console.error(
logger.error(
"An error occurred when trying to use the webcam.",
error,
);
Expand Down
5 changes: 3 additions & 2 deletions src/ui/src/components/shared/SharedControlBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
</template>

<script setup lang="ts">
import { useLogger } from "@/composables/useLogger";

const props = defineProps<{
copyRawContent?: string;
copyStructuredContent?: string;
Expand All @@ -27,8 +29,7 @@ function copyToClipboard({ text = "" }: { text?: string }) {
try {
navigator.clipboard.writeText(text);
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
useLogger().error(error);
}
}
</script>
Expand Down
16 changes: 16 additions & 0 deletions src/ui/src/composables/useLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint-disable no-console */

/**
* A simple abstraction to use logger in the application. For the moment, it's just a proxy to `console`, but it can be plugged to any library later.
*/
export function useLogger(): Pick<
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What the interest of using a hook syntax here and not use a function as ?

logger.log("my logger")

instead of

useLogger().log()

typeof console,
"log" | "warn" | "info" | "error"
> {
return {
log: console.log,
warn: console.warn,
info: console.info,
error: console.error,
};
}
21 changes: 14 additions & 7 deletions src/ui/src/core/auditAndFix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
WriterComponentDefinition,
} from "@/writerTypes";
import { getComponentDefinition } from "./templateMap";
import { useLogger } from "@/composables/useLogger";

/**
* Audits integrity of ComponentMap. Applies automatic fixes if necessary.
Expand All @@ -23,9 +24,11 @@ export function auditAndFixComponents(components: ComponentMap): boolean {
}

export function auditAndFixComponent(component: Component): boolean {
const logger = useLogger();

const def = getComponentDefinition(component.type);
if (!def || def.category == "Fallback") {
console.error(
logger.error(
`Component ${component.id} (${component.type}). Invalid component type.`,
);
return false;
Expand Down Expand Up @@ -53,14 +56,15 @@ function traverseComponentTree(
}

function auditOrphanComponents(components: ComponentMap) {
const logger = useLogger();
const visited = Object.fromEntries(
Object.entries(components).map(([componentId]) => [componentId, false]),
);
traverseComponentTree("root", components, visited);
traverseComponentTree("workflows_root", components, visited);
Object.entries(visited).forEach(([componentId, isVisited]) => {
if (!isVisited) {
console.warn(
logger.warn(
`Component ${componentId} (${components[componentId].type}). Orphan component.`,
);
}
Expand All @@ -71,11 +75,12 @@ function auditComponentFieldKeys(
component: Component,
def: WriterComponentDefinition,
) {
const logger = useLogger();
const fieldKeys = Object.keys(def.fields ?? {});
if (!component.content) return;
Object.keys(component.content).forEach((contentFieldKey) => {
if (fieldKeys.includes(contentFieldKey)) return;
console.warn(
logger.warn(
`Component ${component.id} (${component.type}). Field key "${contentFieldKey}" is defined in the component but not in the template.`,
);
});
Expand All @@ -93,7 +98,8 @@ function auditComponentBinding(
def.events[boundEventType].bindable
)
return;
console.warn(
const logger = useLogger();
logger.warn(
`Component ${component.id} (${component.type}). The component is bound to event "${component.binding.eventType}" but the template doesn't define that event or it's not bindable.`,
);
}
Expand All @@ -110,14 +116,15 @@ function auditAndFixPositions(
component: Component,
components: ComponentMap,
): boolean {
const logger = useLogger();
let isFixApplied = false;
if (component.id == "root") {
if (component.position !== 0) {
console.error("Root must be at position 0.");
logger.error("Root must be at position 0.");
}
}
if (component.position == -1) {
console.error(
logger.error(
`Component ${component.id} (${component.type}). Invalid position.`,
);
}
Expand All @@ -136,7 +143,7 @@ function auditAndFixPositions(
const arithmeticProgression =
((positionfulChildren.length - 1) * positionfulChildren.length) / 2;
if (arithmeticProgression !== positionSum) {
console.error(
logger.error(
`Component ${component.id} (${component.type}). Invalid children positions. Automated fix will be applied.`,
);
fixPositions(positionfulChildren);
Expand Down
26 changes: 12 additions & 14 deletions src/ui/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { auditAndFixComponents } from "./auditAndFix";
import { parseAccessor } from "./parsing";
import { loadExtensions } from "./loadExtensions";
import { bigIntReplacer } from "./serializer";
import { useLogger } from "@/composables/useLogger";

const RECONNECT_DELAY_MS = 1000;
const KEEP_ALIVE_DELAY_MS = 60000;
Expand Down Expand Up @@ -191,6 +192,8 @@ export function generateCore() {
async function startSync(): Promise<void> {
if (webSocket) return; // Open WebSocket exists

const logger = useLogger();

const url = new URL("./api/stream", window.location.href);
url.protocol = url.protocol.replace("https", "wss");
url.protocol = url.protocol.replace("http", "ws");
Expand All @@ -199,7 +202,7 @@ export function generateCore() {
webSocket.onopen = () => {
clearFrontendMap();
syncHealth.value = "connected";
console.log("WebSocket connected. Initialising stream...");
logger.log("WebSocket connected. Initialising stream...");
sendFrontendMessage("streamInit", { sessionId });
};

Expand Down Expand Up @@ -236,7 +239,7 @@ export function generateCore() {
// Connection established correctly but closed due to invalid session.
// Do not attempt to reconnect, the session will remain invalid. Initialise a new session.

console.error("Invalid session. Reinitialising...");
logger.error("Invalid session. Reinitialising...");

// Take care of pending event resolutions and fail them.
await initSession();
Expand All @@ -245,12 +248,12 @@ export function generateCore() {

// Connection lost due to some other reason. Try to reconnect.

console.error("WebSocket closed. Attempting to reconnect...");
logger.error("WebSocket closed. Attempting to reconnect...");
setTimeout(async () => {
try {
await startSync();
} catch {
console.error("Couldn't reconnect.");
logger.error("Couldn't reconnect.");
}
}, RECONNECT_DELAY_MS);
};
Expand Down Expand Up @@ -336,11 +339,11 @@ export function generateCore() {
/**
* Sends a message to be hashed in the backend using the relevant keys.
* Due to security reasons, it works only in edit mode.
*
*
* @param message Messaged to be hashed
* @returns The hashed message
*/
async function hashMessage(message: string):Promise<string> {
async function hashMessage(message: string): Promise<string> {
return new Promise((resolve, reject) => {
const messageCallback = (r: {
ok: boolean;
Expand All @@ -353,13 +356,8 @@ export function generateCore() {
resolve(r.payload?.message);
};

sendFrontendMessage(
"hashRequest",
{ message },
messageCallback,
);
sendFrontendMessage("hashRequest", { message }, messageCallback);
});

}

async function sendCodeSaveRequest(newCode: string): Promise<void> {
Expand Down Expand Up @@ -440,6 +438,7 @@ export function generateCore() {
callback?.({ ok: false });
return;
}
const logger = useLogger();
const trackingId = frontendMessageCounter++;
try {
if (callback || track) {
Expand Down Expand Up @@ -468,8 +467,7 @@ export function generateCore() {
}
webSocket.send(JSON.stringify(wsData, bigIntReplacer));
} catch (error) {
// eslint-disable-next-line no-console
console.error("sendFrontendMessage error", error);
logger.error("sendFrontendMessage error", error);
callback?.({ ok: false });
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/ui/src/core/loadExtensions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useLogger } from "@/composables/useLogger";
import { registerComponentTemplate } from "./templateMap";

const CUSTOM_COMPONENTS_GLOBAL_VAR = "WriterCustomComponentTemplates";
Expand All @@ -16,15 +17,16 @@ export async function loadExtensions(extensionPaths: string[]) {
}

async function importCustomComponentTemplate(path: string) {
console.log(`Importing custom component templates at "${path}"...`);
const logger = useLogger();
logger.log(`Importing custom component templates at "${path}"...`);
await import(/* @vite-ignore */ getRelativeExtensionsPath() + path);
Object.entries(window[CUSTOM_COMPONENTS_GLOBAL_VAR])?.forEach(
([key, template]) => {
if (checkComponentKey(key)) {
registerComponentTemplate(`custom_${key}`, template);
console.log(`Registering template for "${key}".`);
logger.log(`Registering template for "${key}".`);
} else {
console.warn(
logger.warn(
`custom component '${key}' is ignored. A custom component should be declared using only alphanumeric lowercase and _.`,
);
}
Expand Down Expand Up @@ -62,5 +64,3 @@ function getRelativeExtensionsPath() {

return `${pathname}extensions/`;
}


Loading
Loading