microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/fix-wasm-logging-issue

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/npm/qsharp/src/log.ts

122lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4// Logging infrastructure for JavaScript environments (e.g. browser and node.js)
5//
6// Ideally this should be the only module to have global side effects and run code
7// on module load (i.e. other modules should consist almost entirely of declarations
8// and exports at the top level), which means it is configurable and usable from
9// the host environment after import resolution and before other logic runs.
10
11declare global {
12 // Align with VS Code names (but not level numbers)
13 // 0 = off
14 // 1 = error
15 // 2 = warn
16 // 3 = info
17 // 4 = debug (called 'verbose' in VS Code)
18 // 5 = trace
19 // Note this also aligns with the Rust log crate macros/levels
20 // See https://docs.rs/log/latest/log/
21 var qscLog: typeof log;
22 var qscGitHash: string;
23}
24
25export type LogLevel = "off" | "error" | "warn" | "info" | "debug" | "trace";
26export type TelemetryEvent = { id: string; data?: any };
27export type TelemetryCollector = (event: TelemetryEvent) => void;
28
29let telemetryCollector: TelemetryCollector | null = null;
30const levels = ["off", "error", "warn", "info", "debug", "trace"];
31let logLevel = 0;
32const levelChangedListeners: Set<(level: number) => void> = new Set();
33
34export const log = {
35 setLogLevel(level: LogLevel | number) {
36 if (typeof level === "string") {
37 // Convert to number
38 const lowerLevel = level.toLowerCase();
39 let newLevel = 0;
40 levels.forEach((name, idx) => {
41 if (name === lowerLevel) newLevel = idx;
42 });
43 logLevel = newLevel;
44 } else {
45 logLevel = level;
46 }
47 this.onLevelChanged?.(logLevel);
48 levelChangedListeners.forEach((fn) => fn(logLevel));
49 },
50 onLevelChanged: null as ((level: number) => void) | null,
51 /**
52 * Registers a listener that is called whenever the log level changes.
53 * @returns An unsubscribe function that removes the listener when called.
54 */
55 addLevelChangedListener(listener: (level: number) => void): () => void {
56 levelChangedListeners.add(listener);
57 return () => levelChangedListeners.delete(listener);
58 },
59 getLogLevel(): number {
60 return logLevel;
61 },
62 error(...args: any) {
63 if (logLevel >= 1) console.error(...args);
64 },
65 warn(...args: any) {
66 if (logLevel >= 2) console.warn(...args);
67 },
68 info(...args: any) {
69 if (logLevel >= 3) console.info(...args);
70 },
71 debug(...args: any) {
72 if (logLevel >= 4) console.debug(...args);
73 },
74 trace(...args: any) {
75 // console.trace in JavaScript just writes a stack trace at info level, so use 'debug'
76 if (logLevel >= 5) console.debug(...args);
77 },
78 never(val: never) {
79 // Utility function to ensure exhaustive type checking. See https://stackoverflow.com/a/39419171
80 log.error("Exhaustive type checking didn't account for: %o", val);
81 },
82 /**
83 * @param level - A number indicating severity: 1 = Error, 2 = Warn, 3 = Info, 4 = Debug, 5 = Trace
84 * @param target - The area or component sending the message, e.g. "parser" (useful for filtering)
85 * @param args - The format string and args to log, e.g. ["Index of %s is %i", str, index]
86 */
87 logWithLevel(level: number, target: string, ...args: any) {
88 const [firstArg, ...trailingArgs] = args;
89 const outArgs = [`[${target || ""}] ${firstArg}`, ...trailingArgs];
90 switch (level) {
91 case 1:
92 log.error(...outArgs);
93 break;
94 case 2:
95 log.warn(...outArgs);
96 break;
97 case 3:
98 log.info(...outArgs);
99 break;
100 case 4:
101 log.debug(...outArgs);
102 break;
103 case 5:
104 log.trace(...outArgs);
105 break;
106 default:
107 log.error("Invalid logLevel: ", level);
108 }
109 },
110 setTelemetryCollector(handler: TelemetryCollector) {
111 telemetryCollector = handler;
112 },
113 logTelemetry(event: { id: string; data?: any }) {
114 telemetryCollector?.(event);
115 },
116 isTelemetryEnabled() {
117 return !!telemetryCollector;
118 },
119};
120
121// Enable globally for easy interaction and debugging in live environments
122globalThis.qscLog = log;
123