Skip to content

Commit f0c6845

Browse files
authored
feat: improve logging (#254)
## Motivation <!-- List motivation and changes here --> Clean up logging for electron ## Issues closed <!-- List closed issues here -->
1 parent 8f3e68f commit f0c6845

24 files changed

+216
-56
lines changed

src/client/pages/[locale]/prompt.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,12 @@ export default function Page() {
117117
setValue(event.target.value);
118118
try {
119119
const result = evaluate(event.target.value);
120-
setEvaluationResult(result.toString());
120+
if (["string", "number"].includes(typeof result)) {
121+
setEvaluationResult(result.toString());
122+
} else {
123+
setEvaluationResult("");
124+
}
121125
} catch {
122-
// Console.error(error);
123126
setEvaluationResult("");
124127
}
125128
}}

src/electron/helpers/app-loaders.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import serve from "electron-serve";
77
import { globbySync } from "globby";
88
import matter from "gray-matter";
99

10+
import { jsonStringify } from "#/object";
1011
import { logger } from "@/services/logger";
1112
import { getCaptainApps } from "@/utils/path-helpers";
1213

@@ -80,7 +81,7 @@ export function registerApps() {
8081
const installedApps = globbySync(["*/captain.md"], {
8182
cwd: getCaptainApps(),
8283
});
83-
logger.info(JSON.stringify(installedApps, null, 2));
84+
logger.info(jsonStringify(installedApps));
8485

8586
// Register a custom protocol for each installed app, allowing them to be served statically.
8687
for (const installedApp of installedApps) {

src/electron/helpers/core-setup.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import console from "node:console";
12
import path from "node:path";
23
import process from "node:process";
34

@@ -38,6 +39,7 @@ if (gotTheLock || isTest) {
3839
}
3940
} else {
4041
// The app is locked, so we force quit the new instance
42+
// Use native log here since this is before the logger was initialized
4143
console.log("App is locked by another instance. Closing app");
4244
app.quit();
4345
}

src/electron/helpers/init-local-protocol.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from "path";
44
import { protocol } from "electron";
55

66
import { LOCAL_PROTOCOL } from "#/constants";
7+
import { logError } from "@/services/logger";
78

89
/**
910
* Handles requests to a custom protocol for serving local files in an Electron application.
@@ -55,7 +56,7 @@ export function initLocalProtocol() {
5556
return new Response(file, { headers: { "Content-Type": "application/octet-stream" } });
5657
} catch (error) {
5758
// Log and return an error response if the file cannot be read
58-
console.error(`Failed to read ${filePath}:`, error);
59+
logError(error, `Failed to read ${filePath}:`);
5960
return new Response("File not found", { status: 404 });
6061
}
6162
});

src/electron/helpers/ipc/downloads.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ipcMain } from "electron";
44

55
import { apps } from "@/apps";
66
import { DownloadManager } from "@/services/download-manager";
7+
import { logError } from "@/services/logger";
78

89
const downloadManager = new DownloadManager();
910

@@ -39,7 +40,7 @@ ipcMain.on(
3940
payload,
4041
});
4142
} catch (error) {
42-
console.log(error);
43+
logError(error);
4344
}
4445

4546
break;

src/electron/helpers/ipc/global.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { VECTOR_STORE_COLLECTION } from "#/constants";
2222
import { ID } from "#/enums";
2323
import { cleanPath, extractH1Headings, getFileType } from "#/string";
2424
import { apps } from "@/apps";
25+
import { logError } from "@/services/logger";
2526
import { VectorStore } from "@/services/vector-store";
2627
import { inventoryStore, downloadsStore, userStore, appSettingsStore } from "@/stores";
2728
import { pushToStore } from "@/stores/utils";
@@ -145,7 +146,7 @@ ipcMain.handle(
145146
try {
146147
await writePngMetaData(filePath, context);
147148
} catch (error) {
148-
console.log(error);
149+
logError(error);
149150
}
150151
}
151152

@@ -370,7 +371,7 @@ ipcMain.handle(
370371
apps[item.appId]?.webContents.send("downloadComplete", true);
371372
}
372373
} catch (error) {
373-
console.log(`Error on ${item.id}`, error);
374+
logError(error, `Error on ${item.id}`);
374375
downloadsStore.set(item.id, DownloadState.FAILED);
375376
} finally {
376377
downloadsStore.delete(item.id);

src/electron/helpers/ipc/marketplace.ts

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ ipcMain.handle(
1515

1616
try {
1717
const { data } = await axios.get(url);
18-
console.log("data", data);
1918
marketplaceStore.store = data;
2019
return data;
2120
} catch (error) {

src/electron/helpers/ipc/sdk.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import console from "node:console";
2+
3+
import type { SDKMessage } from "@captn/react/types";
14
import { APP_MESSAGE_KEY, DownloadState } from "@captn/utils/constants";
25
import type { VectorStoreDocument } from "@captn/utils/types";
36
import { getProperty } from "dot-prop";
@@ -10,16 +13,12 @@ import { ID } from "#/enums";
1013
import type { StoryRequest } from "#/types/story";
1114
import { apps } from "@/apps";
1215
import { captionImages, createStory, maxTokenMap } from "@/ipc/story";
16+
import { logError, logger } from "@/services/logger";
1317
import { downloadsStore, inventoryStore, userStore } from "@/stores";
1418
import { pushToStore } from "@/stores/utils";
1519
import { clone } from "@/utils/git";
1620
import { getCaptainDownloads, getUserData } from "@/utils/path-helpers";
1721

18-
export interface SDKMessage<T> {
19-
payload: T;
20-
action?: string;
21-
}
22-
2322
ipcMain.on(
2423
APP_MESSAGE_KEY,
2524
<T>(_event: IpcMainEvent, { message, appId }: { message: SDKMessage<T>; appId: string }) => {
@@ -79,7 +78,7 @@ ipcMain.on(
7978
await Promise.all(promises);
8079
} catch (error) {
8180
// TODO: add "onError" into "clone" to properly catch the error
82-
console.log(error);
81+
logError(error);
8382
}
8483
}
8584

@@ -110,12 +109,13 @@ ipcMain.on(
110109

111110
// Seems like no descriptions were generated. Handle as error
112111
if (!imageDescriptions) {
113-
console.log("missing descriptions");
112+
logger.error("missing descriptions");
114113

115114
return;
116115
}
117116

118117
// Debugging helper to check how the images were captioned
118+
// Use native log here as this is only useful while developing
119119
console.log(imageDescriptions);
120120

121121
const maxTokens = maxTokenMap[options.length] * images.length;
@@ -125,7 +125,7 @@ ipcMain.on(
125125
{ imageDescriptions, maxTokens, locale, options },
126126
{
127127
onError(error) {
128-
console.log(error);
128+
logError(error);
129129
},
130130
onChunk(story) {
131131
event.sender.send(channel, {
@@ -208,13 +208,13 @@ ipcMain.on(
208208
// Retaining this log facilitates a deeper understanding of the action's payload and helps identify
209209
// potential issues or anomalies in the data structure or content. It's particularly useful for tracking
210210
// the flow of data and ensuring that the expected actions are triggered correctly within the SDK.
211-
console.log(message);
211+
logger.info(message);
212212
switch (message.action) {
213213
case "function": {
214214
try {
215215
handleCaptainAction(message.payload as VectorStoreDocument["payload"]);
216216
} catch (error) {
217-
console.log(error);
217+
logError(error);
218218
}
219219

220220
if (apps.prompt) {

src/electron/helpers/ipc/sdk/image-to-image.ts

+10-14
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
import console from "node:console";
12
import fsp from "node:fs/promises";
23

4+
import type { SDKMessage } from "@captn/react/types";
35
import { APP_MESSAGE_KEY } from "@captn/utils/constants";
46
import type { IpcMainEvent } from "electron";
57
import { ipcMain } from "electron";
68
import type { ExecaChildProcess } from "execa";
79
import { execa } from "execa";
810

11+
import { jsonStringify } from "#/object";
912
import { cleanPath } from "#/string";
10-
import { logger } from "@/services/logger";
13+
import { logError, logger } from "@/services/logger";
1114
import { createDirectory } from "@/utils/fs";
1215
import {
1316
getCaptainData,
@@ -16,11 +19,6 @@ import {
1619
getDirectory,
1720
} from "@/utils/path-helpers";
1821

19-
export interface SDKMessage<T> {
20-
payload: T;
21-
action?: string;
22-
}
23-
2422
let process_: ExecaChildProcess<string> | undefined;
2523
let cache = "";
2624

@@ -94,7 +92,8 @@ ipcMain.on(
9492
try {
9593
const jsonData = JSON.parse(dataString);
9694

97-
console.log(`image-to-image: ${JSON.stringify(jsonData)}`);
95+
// Use native log here as this is only useful while developing
96+
console.log(`image-to-image: ${jsonStringify(jsonData)}`);
9897

9998
if (process_ && jsonData.status === "starting") {
10099
event.sender.send(channel, {
@@ -157,15 +156,12 @@ ipcMain.on(
157156
}
158157
} catch {
159158
logger.info(`image-to-image: Received non-JSON data: ${dataString}`);
160-
console.log("Received non-JSON data:", dataString);
161159
}
162160
});
163161

164162
logger.info(`image-to-image: processing stderr`);
165163
process_.stderr.on("image-to-image:data", data => {
166-
console.error(`error: ${data}`);
167-
168-
logger.info(`image-to-image: error: ${data}`);
164+
logError(data, "image-to-image error:");
169165

170166
event.sender.send(channel, { action: "image-to-image:error", payload: data });
171167
});
@@ -204,19 +200,19 @@ ipcMain.on(
204200
decodedImageData
205201
);
206202
} catch (error) {
207-
console.error(error);
203+
logError(error);
208204
}
209205

210206
break;
211207
}
212208

213209
case "image-to-image:imageBuffer": {
214210
try {
215-
const { buffer } = message.payload as any;
211+
const { buffer } = message.payload as { buffer: Buffer };
216212

217213
await fsp.writeFile(getCaptainTemporary("image-to-image/input.png"), buffer);
218214
} catch (error) {
219-
console.error(error);
215+
logError(error);
220216
}
221217

222218
break;

src/electron/helpers/ipc/sdk/text-to-image.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import { ipcMain } from "electron";
66
import type { ExecaChildProcess } from "execa";
77
import { execa } from "execa";
88

9+
import { jsonStringify } from "#/object";
910
import { cleanPath } from "#/string";
10-
import { logger } from "@/services/logger";
11+
import { logError, logger } from "@/services/logger";
1112
import { createDirectory } from "@/utils/fs";
1213
import {
1314
getCaptainData,
@@ -89,7 +90,8 @@ ipcMain.on(
8990
try {
9091
const jsonData = JSON.parse(dataString);
9192

92-
console.log(`text-to-image: ${JSON.stringify(jsonData)}`);
93+
// Use native log here as this is only useful while developing
94+
console.log(`text-to-image: ${jsonStringify(jsonData)}`);
9395

9496
if (process_ && jsonData.status === "starting") {
9597
event.sender.send(channel, {
@@ -152,15 +154,12 @@ ipcMain.on(
152154
}
153155
} catch {
154156
logger.info(`text-to-image: Received non-JSON data: ${dataString}`);
155-
console.log("Received non-JSON data:", dataString);
156157
}
157158
});
158159

159160
logger.info(`text-to-image: processing stderr`);
160161
process_.stderr.on("text-to-image:data", data => {
161-
console.error(`error: ${data}`);
162-
163-
logger.info(`text-to-image: error: ${data}`);
162+
logError(data);
164163

165164
event.sender.send(channel, { action: "text-to-image:error", payload: data });
166165
});

src/electron/helpers/ipc/vector-store.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { ScrollOptions, SearchOptions } from "@captn/utils/types";
99
import { ipcMain } from "electron";
1010

1111
import { VECTOR_STORE_COLLECTION } from "#/constants";
12+
import { logError } from "@/services/logger";
1213
import { VectorStore } from "@/services/vector-store";
1314

1415
ipcMain.on(
@@ -21,7 +22,7 @@ ipcMain.on(
2122

2223
event.sender.send(VECTOR_STORE_SEARCH_RESULT_KEY, result);
2324
} catch (error) {
24-
console.log(error);
25+
logError(error);
2526
event.sender.send(ERROR_KEY, error);
2627
}
2728
}

src/electron/helpers/services/__tests__/download-manager.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ describe("DownloadManager", () => {
123123
await fsp.rm(testDirectory, { recursive: true });
124124
}
125125
} catch (error) {
126+
// Use native log here as this is only useful while developing
126127
console.log("Error cleaning up test directory before tests:", error);
127128
}
128129
});
@@ -134,6 +135,7 @@ describe("DownloadManager", () => {
134135
await fsp.rm(testDirectory, { recursive: true });
135136
}
136137
} catch (error) {
138+
// Use native log here as this is only useful while developing
137139
console.log("Error cleaning up test directory after all tests:", error);
138140
}
139141
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { logError, logger } from "../logger";
2+
3+
import { jsonStringify } from "#/object";
4+
5+
// Mock the logger to prevent actual logging during tests
6+
jest.mock("electron-log/node", () => ({
7+
transports: {
8+
file: {
9+
level: jest.fn(),
10+
},
11+
console: {
12+
format: jest.fn(),
13+
},
14+
},
15+
error: jest.fn(),
16+
}));
17+
18+
jest.mock("#/object", () => ({
19+
jsonStringify: jest.fn(() => "mocked json"),
20+
}));
21+
22+
describe("logError", () => {
23+
it("logs Error instances correctly", () => {
24+
const error = new Error("Test error");
25+
logError(error, "Error:");
26+
expect(logger.error).toHaveBeenCalledWith("Error:", error.message, error.stack);
27+
});
28+
29+
it("logs plain objects correctly", () => {
30+
const error = { key: "value" };
31+
logError(error, "Object Error:");
32+
expect(jsonStringify).toHaveBeenCalledWith(error);
33+
expect(logger.error).toHaveBeenCalledWith("Object Error:", "mocked json");
34+
});
35+
36+
it("logs non-object, non-Error values correctly", () => {
37+
const error = "A string error";
38+
logError(error, "String Error:");
39+
expect(logger.error).toHaveBeenCalledWith("String Error:", error);
40+
});
41+
});

src/electron/helpers/services/__tests__/vector-store.test.e2e.ts

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ describe("VectorStore Integration Tests", () => {
140140
await vectorStore.search(collectionName2, document1.content);
141141
} catch (error) {
142142
expect(error).toBeDefined();
143+
// Use native log here as this is only useful while developing
143144
console.log(error);
144145
expect((error as Error).message).toContain(
145146
`Collection ${collectionName2} doesn't exist`

0 commit comments

Comments
 (0)