microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
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 | |
| 11 | declare 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 | |
| 25 | export type LogLevel = "off" | "error" | "warn" | "info" | "debug" | "trace"; |
| 26 | export type TelemetryEvent = { id: string; data?: any }; |
| 27 | export type TelemetryCollector = (event: TelemetryEvent) => void; |
| 28 | |
| 29 | let telemetryCollector: TelemetryCollector | null = null; |
| 30 | const levels = ["off", "error", "warn", "info", "debug", "trace"]; |
| 31 | let logLevel = 0; |
| 32 | const levelChangedListeners: Set<(level: number) => void> = new Set(); |
| 33 | |
| 34 | export 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 |
| 122 | globalThis.qscLog = log; |
| 123 | |