log.ts 3.95 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

'use strict';

import * as fs from 'fs';
import { Writable } from 'stream';
import { WritableStreamBuffer } from 'stream-buffers';
import { format } from 'util';
import * as component from '../common/component';
SparkSnail's avatar
SparkSnail committed
11
import { getExperimentStartupInfo, isReadonly } from './experimentStartupInfo';
Deshui Yu's avatar
Deshui Yu committed
12

13
const FATAL: number = 1;
Deshui Yu's avatar
Deshui Yu committed
14
15
16
17
const ERROR: number = 2;
const WARNING: number = 3;
const INFO: number = 4;
const DEBUG: number = 5;
18
19
20
21
const TRACE: number = 6;

const logLevelNameMap: Map<string, number> = new Map([['fatal', FATAL],
    ['error', ERROR], ['warning', WARNING], ['info', INFO], ['debug', DEBUG], ['trace', TRACE]]);
Deshui Yu's avatar
Deshui Yu committed
22
23
24
25
26
27
28

class BufferSerialEmitter {
    private buffer: Buffer;
    private emitting: boolean;
    private writable: Writable;

    constructor(writable: Writable) {
Zejun Lin's avatar
Zejun Lin committed
29
        this.buffer = Buffer.alloc(0);
Deshui Yu's avatar
Deshui Yu committed
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
        this.emitting = false;
        this.writable = writable;
    }

    public feed(buffer: Buffer): void {
        this.buffer = Buffer.concat([this.buffer, buffer]);
        if (!this.emitting) {
            this.emit();
        }
    }

    private emit(): void {
        this.emitting = true;
        this.writable.write(this.buffer, () => {
            if (this.buffer.length === 0) {
                this.emitting = false;
            } else {
                this.emit();
            }
        });
Zejun Lin's avatar
Zejun Lin committed
50
        this.buffer = Buffer.alloc(0);
Deshui Yu's avatar
Deshui Yu committed
51
52
53
54
55
    }
}

@component.Singleton
class Logger {
chicm-ms's avatar
chicm-ms committed
56
    private level: number = INFO;
57
58
    private bufferSerialEmitter?: BufferSerialEmitter;
    private writable?: Writable;
SparkSnail's avatar
SparkSnail committed
59
    private readonly: boolean = false;
Deshui Yu's avatar
Deshui Yu committed
60
61

    constructor(fileName?: string) {
62
63
64
65
66
67
68
69
        const logFile: string | undefined = fileName;
        if (logFile) {
            this.writable = fs.createWriteStream(logFile, {
                flags: 'a+',
                encoding: 'utf8',
                autoClose: true
            });
            this.bufferSerialEmitter = new BufferSerialEmitter(this.writable);
Deshui Yu's avatar
Deshui Yu committed
70
        }
71
72
73
74
75
76
77

        const logLevelName: string = getExperimentStartupInfo()
                                    .getLogLevel();
        const logLevel: number | undefined = logLevelNameMap.get(logLevelName);
        if (logLevel !== undefined) {
            this.level = logLevel;
        }
SparkSnail's avatar
SparkSnail committed
78
79

        this.readonly = isReadonly();
80
81
    }

chicm-ms's avatar
chicm-ms committed
82
    public close(): void {
83
84
85
        if (this.writable) {
            this.writable.destroy();
        }
Deshui Yu's avatar
Deshui Yu committed
86
87
    }

88
89
90
91
92
93
    public trace(...param: any[]): void {
        if (this.level >= TRACE) {
            this.log('TRACE', param);
        }
    }

Deshui Yu's avatar
Deshui Yu committed
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    public debug(...param: any[]): void {
        if (this.level >= DEBUG) {
            this.log('DEBUG', param);
        }
    }

    public info(...param: any[]): void {
        if (this.level >= INFO) {
            this.log('INFO', param);
        }
    }

    public warning(...param: any[]): void {
        if (this.level >= WARNING) {
            this.log('WARNING', param);
        }
    }

    public error(...param: any[]): void {
        if (this.level >= ERROR) {
            this.log('ERROR', param);
        }
    }

118
119
    public fatal(...param: any[]): void {
        this.log('FATAL', param);
Deshui Yu's avatar
Deshui Yu committed
120
    }
SparkSnail's avatar
SparkSnail committed
121
122
123
124
125
126
    
    /**
     * if the experiment is not in readonly mode, write log content to stream
     * @param level log level
     * @param param the params to be written
     */
Deshui Yu's avatar
Deshui Yu committed
127
    private log(level: string, param: any[]): void {
SparkSnail's avatar
SparkSnail committed
128
        if (!this.readonly) {
129
130
131
132
133
134
135
136
137
            const logContent = `[${(new Date()).toLocaleString()}] ${level} ${format(param)}\n`;
            if (this.writable && this.bufferSerialEmitter) {
                const buffer: WritableStreamBuffer = new WritableStreamBuffer();
                buffer.write(logContent);
                buffer.end();
                this.bufferSerialEmitter.feed(buffer.getContents());
            } else {
                console.log(logContent);
            }
SparkSnail's avatar
SparkSnail committed
138
        }
Deshui Yu's avatar
Deshui Yu committed
139
140
141
    }
}

SparkSnail's avatar
SparkSnail committed
142
function getLogger(): Logger {
Deshui Yu's avatar
Deshui Yu committed
143
144
145
    return component.get(Logger);
}

chicm-ms's avatar
chicm-ms committed
146
export { Logger, getLogger, logLevelNameMap };