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

'use strict';

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

15
const FATAL: number = 1;
Deshui Yu's avatar
Deshui Yu committed
16
17
18
19
const ERROR: number = 2;
const WARNING: number = 3;
const INFO: number = 4;
const DEBUG: number = 5;
20
21
22
23
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
24
25
26
27
28
29
30

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

    constructor(writable: Writable) {
Zejun Lin's avatar
Zejun Lin committed
31
        this.buffer = Buffer.alloc(0);
Deshui Yu's avatar
Deshui Yu committed
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
        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
52
        this.buffer = Buffer.alloc(0);
Deshui Yu's avatar
Deshui Yu committed
53
54
55
56
57
58
    }
}

@component.Singleton
class Logger {
    private DEFAULT_LOGFILE: string = path.join(getLogDir(), 'nnimanager.log');
chicm-ms's avatar
chicm-ms committed
59
    private level: number = INFO;
Deshui Yu's avatar
Deshui Yu committed
60
    private bufferSerialEmitter: BufferSerialEmitter;
Gems Guo's avatar
Gems Guo committed
61
    private writable: Writable;
SparkSnail's avatar
SparkSnail committed
62
    private readonly: boolean = false;
Deshui Yu's avatar
Deshui Yu committed
63
64
65
66
67
68

    constructor(fileName?: string) {
        let logFile: string | undefined = fileName;
        if (logFile === undefined) {
            logFile = this.DEFAULT_LOGFILE;
        }
Gems Guo's avatar
Gems Guo committed
69
        this.writable = fs.createWriteStream(logFile, {
Deshui Yu's avatar
Deshui Yu committed
70
71
72
            flags: 'a+',
            encoding: 'utf8',
            autoClose: true
73
        });
Gems Guo's avatar
Gems Guo committed
74
        this.bufferSerialEmitter = new BufferSerialEmitter(this.writable);
75
76
77
78
79
80
81

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

        this.readonly = isReadonly();
84
85
    }

chicm-ms's avatar
chicm-ms committed
86
    public close(): void {
Gems Guo's avatar
Gems Guo committed
87
        this.writable.destroy();
Deshui Yu's avatar
Deshui Yu committed
88
89
    }

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

Deshui Yu's avatar
Deshui Yu committed
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
    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);
        }
    }

120
121
    public fatal(...param: any[]): void {
        this.log('FATAL', param);
Deshui Yu's avatar
Deshui Yu committed
122
    }
SparkSnail's avatar
SparkSnail committed
123
124
125
126
127
128
    
    /**
     * 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
129
    private log(level: string, param: any[]): void {
SparkSnail's avatar
SparkSnail committed
130
131
132
133
134
135
136
137
        if (!this.readonly) {
            const buffer: WritableStreamBuffer = new WritableStreamBuffer();
            buffer.write(`[${(new Date()).toLocaleString()}] ${level} `);
            buffer.write(format(param));
            buffer.write('\n');
            buffer.end();
            this.bufferSerialEmitter.feed(buffer.getContents());
        }
Deshui Yu's avatar
Deshui Yu committed
138
139
140
    }
}

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

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