Skip to content

Commit d628a98

Browse files
Neet-Nestorjingyi-zhao-01
authored andcommitted
[Log] Set log level using 'loglevel' package (mlc-ai#427)
This PR introduces a new package, `logLevel`, which is a lightweight logging javascript library for managing logging levels and filtering debug logs in production. https://github.com/pimterry/loglevel We exposed `logLevel` as part of the engine config to allow users to modify it. The default log level is `WARN`. In `TRACE` level: <img width="766" alt="Screenshot 2024-05-26 at 6 45 43 PM" src="https://github.com/mlc-ai/web-llm/assets/23090573/1245d868-4848-4310-a4ed-b3097b0972cd"> In `WARN` level: <img width="779" alt="Screenshot 2024-05-26 at 6 53 27 PM" src="https://github.com/mlc-ai/web-llm/assets/23090573/0a391d55-5d40-4199-bb2b-7c02600b2460">
1 parent 6a14782 commit d628a98

11 files changed

+117
-46
lines changed

package-lock.json

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,8 @@
5151
"tslib": "^2.3.1",
5252
"tvmjs": "file:./tvm_home/web",
5353
"typescript": "^4.9.5"
54+
},
55+
"dependencies": {
56+
"loglevel": "^1.9.1"
5457
}
5558
}

src/config.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-non-null-assertion */
2-
2+
import log from "loglevel";
33
import { ResponseFormat } from "./openai_api_protocols";
4-
import { LogitProcessor, InitProgressCallback } from "./types";
4+
import { LogitProcessor, InitProgressCallback, LogLevel } from "./types";
55

66
/**
77
* Conversation template config
@@ -26,6 +26,8 @@ export enum Role {
2626
assistant = "assistant",
2727
}
2828

29+
export const DefaultLogLevel: LogLevel = "WARN";
30+
2931
/**
3032
* Place holders that can be used in role templates.
3133
* For example, a role template of
@@ -91,6 +93,7 @@ export interface MLCEngineConfig {
9193
appConfig?: AppConfig;
9294
initProgressCallback?: InitProgressCallback;
9395
logitProcessorRegistry?: Map<string, LogitProcessor>;
96+
logLevel: LogLevel;
9497
}
9598

9699
/**
@@ -167,16 +170,14 @@ export function postInitAndCheckGenerationConfigValues(
167170
!_hasValue(config.presence_penalty)
168171
) {
169172
config.presence_penalty = 0.0;
170-
console.log(
171-
"Only frequency_penalty is set; we default presence_penaty to 0.",
172-
);
173+
log.warn("Only frequency_penalty is set; we default presence_penaty to 0.");
173174
}
174175
if (
175176
_hasValue(config.presence_penalty) &&
176177
!_hasValue(config.frequency_penalty)
177178
) {
178179
config.frequency_penalty = 0.0;
179-
console.log(
180+
log.warn(
180181
"Only presence_penalty is set; we default frequency_penalty to 0.",
181182
);
182183
}

src/engine.ts

+18-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as tvmjs from "tvmjs";
2+
import log from "loglevel";
23
import { Tokenizer } from "@mlc-ai/web-tokenizers";
34
import * as API from "./openai_api_protocols/apis";
45
import {
@@ -10,6 +11,7 @@ import {
1011
postInitAndCheckGenerationConfigValues,
1112
Role,
1213
MLCEngineConfig,
14+
DefaultLogLevel,
1315
} from "./config";
1416
import { LLMChatPipeline } from "./llm_chat";
1517
import {
@@ -30,6 +32,7 @@ import {
3032
MLCEngineInterface,
3133
GenerateProgressCallback,
3234
LogitProcessor,
35+
LogLevel,
3336
} from "./types";
3437
import {
3538
Conversation,
@@ -61,6 +64,7 @@ export async function CreateMLCEngine(
6164
engineConfig?: MLCEngineConfig,
6265
): Promise<MLCEngine> {
6366
const engine = new MLCEngine();
67+
engine.setLogLevel(engineConfig?.logLevel || DefaultLogLevel);
6468
engine.setInitProgressCallback(engineConfig?.initProgressCallback);
6569
engine.setLogitProcessorRegistry(engineConfig?.logitProcessorRegistry);
6670
await engine.reload(modelId, engineConfig?.chatOpts, engineConfig?.appConfig);
@@ -76,7 +80,7 @@ export class MLCEngine implements MLCEngineInterface {
7680
public chat: API.Chat;
7781

7882
private currentModelId?: string = undefined; // Model current loaded, undefined if nothing is loaded
79-
private logger: (msg: string) => void = console.log;
83+
private logger: (msg: string) => void = log.info;
8084
private logitProcessorRegistry?: Map<string, LogitProcessor>;
8185
private logitProcessor?: LogitProcessor;
8286
private pipeline?: LLMChatPipeline;
@@ -238,7 +242,7 @@ export class MLCEngine implements MLCEngineInterface {
238242
let deviceLostInReload = false;
239243
gpuDetectOutput.device.lost.then((info: any) => {
240244
if (this.deviceLostIsError) {
241-
console.error(
245+
log.error(
242246
`Device was lost during reload. This can happen due to insufficient memory or other GPU constraints. Detailed error: ${info}. Please try to reload WebLLM with a less resource-intensive model.`,
243247
);
244248
this.unload();
@@ -291,7 +295,7 @@ export class MLCEngine implements MLCEngineInterface {
291295
streamInterval = 1,
292296
genConfig?: GenerationConfig,
293297
): Promise<string> {
294-
console.log(
298+
log.warn(
295299
"WARNING: `generate()` will soon be deprecated. " +
296300
"Please use `engine.chat.completions.create()` instead. " +
297301
"For multi-round chatting, see `examples/multi-round-chat` on how to use " +
@@ -579,7 +583,7 @@ export class MLCEngine implements MLCEngineInterface {
579583
gpuDetectOutput.device.limits.maxStorageBufferBindingSize;
580584
const defaultMaxStorageBufferBindingSize = 1 << 30; // 1GB
581585
if (maxStorageBufferBindingSize < defaultMaxStorageBufferBindingSize) {
582-
console.log(
586+
log.warn(
583587
`WARNING: the current maxStorageBufferBindingSize ` +
584588
`(${computeMB(maxStorageBufferBindingSize)}) ` +
585589
`may only work for a limited number of models, e.g.: \n` +
@@ -636,6 +640,15 @@ export class MLCEngine implements MLCEngineInterface {
636640
return this.getPipeline().getMessage();
637641
}
638642

643+
/**
644+
* Set MLCEngine logging output level
645+
*
646+
* @param logLevel The new log level
647+
*/
648+
setLogLevel(logLevel: LogLevel) {
649+
log.setLevel(logLevel);
650+
}
651+
639652
/**
640653
* Get a new Conversation object based on the chat completion request.
641654
*
@@ -792,7 +805,7 @@ export class MLCEngine implements MLCEngineInterface {
792805
this.resetChat();
793806
this.getPipeline().setConversation(newConv);
794807
} else {
795-
console.log("Multiround chatting, reuse KVCache.");
808+
log.info("Multiround chatting, reuse KVCache.");
796809
}
797810

798811
// 2. Treat the last message as the usual input

src/extension_service_worker.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as tvmjs from "tvmjs";
2+
import log from "loglevel";
23
import { AppConfig, ChatOptions, MLCEngineConfig } from "./config";
34
import { ReloadParams, WorkerRequest } from "./message";
4-
import { MLCEngineInterface } from "./types";
5+
import { LogLevel, MLCEngineInterface } from "./types";
56
import {
67
ChatWorker,
78
MLCEngineWorkerHandler,
@@ -88,7 +89,7 @@ export class ServiceWorkerMLCEngineHandler extends MLCEngineWorkerHandler {
8889
areChatOptionsEqual(this.chatOpts, params.chatOpts) &&
8990
areAppConfigsEqual(this.appConfig, params.appConfig)
9091
) {
91-
console.log("Already loaded the model. Skip loading");
92+
log.info("Already loaded the model. Skip loading");
9293
const gpuDetectOutput = await tvmjs.detectGPUDevice();
9394
if (gpuDetectOutput == undefined) {
9495
throw Error("Cannot find WebGPU in the environment");
@@ -140,6 +141,9 @@ export async function CreateServiceWorkerMLCEngine(
140141
keepAliveMs = 10000,
141142
): Promise<ServiceWorkerMLCEngine> {
142143
const serviceWorkerMLCEngine = new ServiceWorkerMLCEngine(keepAliveMs);
144+
if (engineConfig?.logLevel) {
145+
serviceWorkerMLCEngine.setLogLevel(engineConfig.logLevel);
146+
}
143147
serviceWorkerMLCEngine.setInitProgressCallback(
144148
engineConfig?.initProgressCallback,
145149
);

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export {
1414
InitProgressReport,
1515
MLCEngineInterface,
1616
LogitProcessor,
17+
LogLevel,
1718
} from "./types";
1819

1920
export { MLCEngine, CreateMLCEngine } from "./engine";

src/llm_chat.ts

+9-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-non-null-assertion */
22
/* eslint-disable no-prototype-builtins */
33
import * as tvmjs from "tvmjs";
4+
import log from "loglevel";
45
import { Tokenizer } from "@mlc-ai/web-tokenizers";
56
import { ChatConfig, GenerationConfig, Role } from "./config";
67
import { getConversation, Conversation } from "./conversation";
@@ -72,9 +73,6 @@ export class LLMChatPipeline {
7273
private curRoundDecodingTotalTokens = 0;
7374
private curRoundPrefillTotalTokens = 0;
7475

75-
// logger
76-
private logger = console.log;
77-
7876
// LogitProcessor
7977
private logitProcessor?: LogitProcessor = undefined;
8078

@@ -154,7 +152,7 @@ export class LLMChatPipeline {
154152

155153
// 4. Read in compilation configurations from metadata
156154
this.prefillChunkSize = metadata.prefill_chunk_size;
157-
this.logger("Using prefillChunkSize: ", this.prefillChunkSize);
155+
log.info("Using prefillChunkSize: ", this.prefillChunkSize);
158156
if (this.prefillChunkSize <= 0) {
159157
throw Error("Prefill chunk size needs to be positive.");
160158
}
@@ -164,14 +162,14 @@ export class LLMChatPipeline {
164162
metadata.sliding_window_size != -1
165163
) {
166164
this.slidingWindowSize = metadata.sliding_window_size;
167-
this.logger("Using slidingWindowSize: ", this.slidingWindowSize);
165+
log.info("Using slidingWindowSize: ", this.slidingWindowSize);
168166
// Parse attention sink size
169167
if (
170168
metadata.hasOwnProperty("attention_sink_size") &&
171169
metadata.attention_sink_size >= 0
172170
) {
173171
this.attentionSinkSize = metadata.attention_sink_size;
174-
this.logger("Using attentionSinkSize: ", this.attentionSinkSize);
172+
log.info("Using attentionSinkSize: ", this.attentionSinkSize);
175173
} else {
176174
throw Error(
177175
"Need to specify non-negative attention_sink_size if using sliding window. " +
@@ -184,7 +182,7 @@ export class LLMChatPipeline {
184182
metadata.context_window_size != -1
185183
) {
186184
this.maxWindowLength = metadata.context_window_size;
187-
this.logger("Using maxWindowLength: ", this.maxWindowLength);
185+
log.info("Using maxWindowLength: ", this.maxWindowLength);
188186
} else {
189187
throw Error(
190188
"Need to specify either sliding window size or max window size.",
@@ -905,7 +903,7 @@ export class LLMChatPipeline {
905903
}
906904

907905
// need shift window and re-encode
908-
this.logger("need shift window");
906+
log.info("need shift window");
909907
this.filledKVCacheLength = 0;
910908
this.resetKVCache();
911909

@@ -1056,8 +1054,8 @@ export class LLMChatPipeline {
10561054
`decoding-time=${((decodingEnd - decodingStart) / 1000).toFixed(4)} sec`;
10571055

10581056
// simply log tokens for eyeballing.
1059-
console.log("Logits:");
1060-
console.log(logitsOnCPU.toArray());
1061-
console.log(msg);
1057+
log.info("Logits:");
1058+
log.info(logitsOnCPU.toArray());
1059+
log.info(msg);
10621060
}
10631061
}

src/service_worker.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as tvmjs from "tvmjs";
2+
import log from "loglevel";
23
import { AppConfig, ChatOptions, MLCEngineConfig } from "./config";
34
import { ReloadParams, WorkerRequest, WorkerResponse } from "./message";
4-
import { MLCEngineInterface, InitProgressReport } from "./types";
5+
import { MLCEngineInterface, InitProgressReport, LogLevel } from "./types";
56
import {
67
MLCEngineWorkerHandler,
78
WebWorkerMLCEngine,
@@ -90,7 +91,7 @@ export class ServiceWorkerMLCEngineHandler extends MLCEngineWorkerHandler {
9091
onError?: () => void,
9192
): void {
9293
const msg = event.data as WorkerRequest;
93-
console.debug(
94+
log.trace(
9495
`ServiceWorker message: [${msg.kind}] ${JSON.stringify(msg.content)}`,
9596
);
9697

@@ -114,7 +115,7 @@ export class ServiceWorkerMLCEngineHandler extends MLCEngineWorkerHandler {
114115
areChatOptionsEqual(this.chatOpts, params.chatOpts) &&
115116
areAppConfigsEqual(this.appConfig, params.appConfig)
116117
) {
117-
console.log("Already loaded the model. Skip loading");
118+
log.info("Already loaded the model. Skip loading");
118119
const gpuDetectOutput = await tvmjs.detectGPUDevice();
119120
if (gpuDetectOutput == undefined) {
120121
throw Error("Cannot find WebGPU in the environment");
@@ -206,6 +207,9 @@ export async function CreateServiceWorkerMLCEngine(
206207
);
207208
}
208209
const serviceWorkerMLCEngine = new ServiceWorkerMLCEngine(serviceWorker);
210+
if (engineConfig?.logLevel) {
211+
serviceWorkerMLCEngine.setLogLevel(engineConfig.logLevel);
212+
}
209213
serviceWorkerMLCEngine.setInitProgressCallback(
210214
engineConfig?.initProgressCallback,
211215
);
@@ -234,7 +238,7 @@ export class ServiceWorkerMLCEngine extends WebWorkerMLCEngine {
234238
"message",
235239
(event: MessageEvent) => {
236240
const msg = event.data;
237-
console.debug(
241+
log.trace(
238242
`MLC client message: [${msg.kind}] ${JSON.stringify(msg.content)}`,
239243
);
240244
try {
@@ -246,7 +250,7 @@ export class ServiceWorkerMLCEngine extends WebWorkerMLCEngine {
246250
} catch (err: any) {
247251
// This is expected to throw if user has multiple windows open
248252
if (!err.message.startsWith("return from a unknown uuid")) {
249-
console.error("CreateWebServiceWorkerMLCEngine.onmessage", err);
253+
log.error("CreateWebServiceWorkerMLCEngine.onmessage", err);
250254
}
251255
}
252256
},
@@ -255,7 +259,7 @@ export class ServiceWorkerMLCEngine extends WebWorkerMLCEngine {
255259
setInterval(() => {
256260
this.worker.postMessage({ kind: "keepAlive", uuid: crypto.randomUUID() });
257261
this.missedHeatbeat += 1;
258-
console.debug("missedHeatbeat", this.missedHeatbeat);
262+
log.trace("missedHeatbeat", this.missedHeatbeat);
259263
}, keepAliveMs);
260264
}
261265

src/types.ts

+17
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,21 @@ export interface MLCEngineInterface {
194194
inputIds: Array<number>,
195195
isPrefill: boolean,
196196
): Promise<number>;
197+
198+
/**
199+
* Set MLCEngine logging output level
200+
*
201+
* @param logLevel The new log level
202+
*/
203+
setLogLevel(logLevel: LogLevel): void;
197204
}
205+
206+
export const LOG_LEVELS = {
207+
TRACE: 0,
208+
DEBUG: 1,
209+
INFO: 2,
210+
WARN: 3,
211+
ERROR: 4,
212+
SILENT: 5,
213+
};
214+
export type LogLevel = keyof typeof LOG_LEVELS;

src/web_worker.ts

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
GenerateProgressCallback,
1010
InitProgressCallback,
1111
InitProgressReport,
12+
LogLevel,
1213
} from "./types";
1314
import {
1415
ChatCompletionRequest,
@@ -31,6 +32,7 @@ import {
3132
WorkerResponse,
3233
WorkerRequest,
3334
} from "./message";
35+
import log from "loglevel";
3436

3537
export interface PostMessageHandler {
3638
postMessage: (message: any) => void;
@@ -624,4 +626,8 @@ export class WebWorkerMLCEngine implements MLCEngineInterface {
624626
}
625627
}
626628
}
629+
630+
setLogLevel(logLevel: LogLevel) {
631+
log.setLevel(logLevel);
632+
}
627633
}

0 commit comments

Comments
 (0)