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

'use strict';
/* tslint:disable:no-any */

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
13
import { getExperimentStartupInfo, isReadonly } from './experimentStartupInfo';
Deshui Yu's avatar
Deshui Yu committed
14
15
import { getLogDir } from './utils';

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

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

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

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

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

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

        this.readonly = isReadonly();
85
86
    }

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

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

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

121
122
    public fatal(...param: any[]): void {
        this.log('FATAL', param);
Deshui Yu's avatar
Deshui Yu committed
123
    }
SparkSnail's avatar
SparkSnail committed
124
125
126
127
128
129
    
    /**
     * 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
130
    private log(level: string, param: any[]): void {
SparkSnail's avatar
SparkSnail committed
131
132
133
134
135
136
137
138
        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
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 };