log.ts 3.58 KB
Newer Older
liuzhe-lz's avatar
liuzhe-lz committed
1
2
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
Deshui Yu's avatar
Deshui Yu committed
3

4
5
6
7
8
9
10
11
12
13
/**
 *  Python-like logging interface.
 *
 *      const logger = getLogger('moduleName');
 *      logger.info('hello', { to: 'world' });
 *
 *  Outputs:
 *
 *      [1970-01-01 00:00:00] INFO (moduleName) hello { to: 'world' }
 **/
14

15
import util from 'util';
16

17
import globals from 'common/globals';
18

19
const levelNameToValue = { trace: 0, debug: 10, info: 20, warning: 30, error: 40, critical: 50 } as const;
Deshui Yu's avatar
Deshui Yu committed
20

21
const loggers: Record<string, Logger> = {};
Deshui Yu's avatar
Deshui Yu committed
22

23
24
25
26
27
28
export function getLogger(name: string): Logger {
    if (loggers[name] === undefined) {
        loggers[name] = new Logger(name);
    }
    return loggers[name];
}
Deshui Yu's avatar
Deshui Yu committed
29

30
31
32
33
34
35
36
37
38
39
/**
 *  A special logger prints to stderr when the logging system has problems.
 *  For modules that are responsible for handling logger errors.
 **/
export function getRobustLogger(name: string): Logger {
    if (loggers[name] === undefined || !(loggers[name] as RobustLogger).robust) {
        loggers[name] = new RobustLogger(name);
    }
    return loggers[name];
}
Deshui Yu's avatar
Deshui Yu committed
40

41
export class Logger {
42
    protected name: string;
43

44
    constructor(name: string) {
45
        this.name = name;
Deshui Yu's avatar
Deshui Yu committed
46
47
    }

48
    public trace(...args: any[]): void {
49
        this.log(levelNameToValue.trace, 'TRACE', args);
50
    }
51

52
    public debug(...args: any[]): void {
53
        this.log(levelNameToValue.debug, 'DEBUG', args);
54
    }
SparkSnail's avatar
SparkSnail committed
55

56
    public info(...args: any[]): void {
57
        this.log(levelNameToValue.info, 'INFO', args);
58
59
    }

60
    public warning(...args: any[]): void {
61
        this.log(levelNameToValue.warning, 'WARNING', args);
Deshui Yu's avatar
Deshui Yu committed
62
63
    }

64
    public error(...args: any[]): void {
65
        this.log(levelNameToValue.error, 'ERROR', args);
66
67
    }

68
    public critical(...args: any[]): void {
69
        this.log(levelNameToValue.critical, 'CRITICAL', args);
Deshui Yu's avatar
Deshui Yu committed
70
71
    }

72
73
74
75
76
    protected log(levelValue: number, levelName: string, args: any[]): void {
        if (levelValue >= levelNameToValue[globals.args.logLevel]) {
            const msg = `[${timestamp()}] ${levelName} (${this.name}) ${formatArgs(args)}`;
            globals.logStream.writeLine(msg);
        }
Deshui Yu's avatar
Deshui Yu committed
77
    }
78
}
Deshui Yu's avatar
Deshui Yu committed
79

80
81
82
83
84
85
86
class RobustLogger extends Logger {
    public readonly robust: boolean = true;
    private errorOccurred: boolean = false;

    protected log(levelValue: number, levelName: string, args: any[]): void {
        if (this.errorOccurred) {
            this.logAfterError(levelName, args);
87
            return;
Deshui Yu's avatar
Deshui Yu committed
88
        }
89
90
91
92
        try {
            if (levelValue >= levelNameToValue[globals.args.logLevel]) {
                const msg = `[${timestamp()}] ${levelName} (${this.name}) ${formatArgs(args)}`;
                globals.logStream.writeLineSync(msg);
liuzhe-lz's avatar
liuzhe-lz committed
93
            }
94
95
96
97
        } catch (error) {
            this.errorOccurred = true;
            console.error('[ERROR] Logger has stopped working:', error);
            this.logAfterError(levelName, args);
J-shang's avatar
J-shang committed
98
        }
Deshui Yu's avatar
Deshui Yu committed
99
100
    }

101
102
103
104
105
    private logAfterError(levelName: string, args: any[]): void {
        try {
            args = args.map(arg => util.inspect(arg));
        } catch { /* fallback */ }
        console.error(`[${levelName}] (${this.name})`, ...args);
Deshui Yu's avatar
Deshui Yu committed
106
    }
107
108
}

109
110
111
112
113
function timestamp(): string {
    const now = new Date();
    const date = now.getFullYear() + '-' + zeroPad(now.getMonth() + 1) + '-' + zeroPad(now.getDate());
    const time = zeroPad(now.getHours()) + ':' + zeroPad(now.getMinutes()) + ':' + zeroPad(now.getSeconds());
    return date + ' ' + time;
Deshui Yu's avatar
Deshui Yu committed
114
115
}

116
117
function zeroPad(num: number): string {
    return num.toString().padStart(2, '0');
Deshui Yu's avatar
Deshui Yu committed
118
119
}

120
121
function formatArgs(args: any[]): string {
    return args.map(arg => (typeof arg === 'string' ? arg : util.inspect(arg))).join(' ');
liuzhe-lz's avatar
liuzhe-lz committed
122
}