UNPKG

2.98 kBJavaScriptView Raw
1// @flow
2
3// NOTE: don't use Module here, since Module uses this class
4
5import stringify from 'json-stringify-safe'
6import _ from 'lodash'
7
8import { httpReq } from './httpUtils'
9import { padRight } from './stringUtils'
10
11const LOG_FLUSH_INTERVAL_MS = 5 * 1000
12
13type LoggerOptions = {
14 noHTTPS?: boolean,
15 host?: ?string,
16 dataFieldsToPromote?: ?{ [id: string]: string },
17 dataFieldsToRemove?: ?Array<string>,
18 systemMeta?: ?{},
19}
20
21export default class S3Logger {
22
23 _logObjects: Array<Object> = []
24 _logRecordNum: number = 0
25 _options: LoggerOptions
26
27
28 constructor(options: LoggerOptions) {
29 this._options = options
30
31 if (this._options.host) {
32 setInterval(this.flushLogs, LOG_FLUSH_INTERVAL_MS)
33 }
34 }
35
36
37 queueLogRecord = (logObj: Object): void => {
38 try {
39 const nowMS = Date.now()
40
41 let { data, message, ...restLogObj } = logObj
42
43 const objToPush = {
44 event: message,
45
46 ...restLogObj,
47
48 record_num: ++this._logRecordNum,
49 time_ms: nowMS,
50 time_ts: new Date(nowMS).toISOString(),
51 }
52
53 if (data != null) {
54
55 // make sure data is a object
56 if (typeof data !== 'object') {
57 data = {
58 value: data.toString()
59 }
60 }
61
62 // promote data fields
63 if (this._options.dataFieldsToPromote) {
64 Object.entries(this._options.dataFieldsToPromote).forEach(([logProp, dataPath]) => {
65 // $FlowIgnore
66 const dataVal = _.get(data, dataPath)
67 if (dataVal !== undefined) {
68 objToPush[logProp] = dataVal
69 }
70 })
71 }
72
73 // remove data fields
74 if (this._options.dataFieldsToRemove) {
75 this._options.dataFieldsToRemove.forEach(dataProp => {
76 delete data[dataProp]
77 })
78 }
79
80 // serialize data
81 if (Object.keys(data).length > 0)
82 objToPush.data = stringify(data)
83
84 }
85
86 // log to console
87 console.log(`${objToPush.time_ts.substr(11, 12)}: ${padRight(objToPush.level, 5, ' ')} [${objToPush.module}] ${objToPush.event} ${objToPush.data || ''}`) // eslint-disable-line no-console
88
89 this._logObjects.push(objToPush)
90 } catch (err) {
91 console.error('Failed to queue a log record', logObj, err) // eslint-disable-line no-console
92 }
93 }
94
95
96 flushLogs = async (): Promise<void> => {
97 try {
98 if (!this._logObjects.length || !this._options.host)
99 return
100
101 const logsToFlush = this._logObjects
102 this._logObjects = []
103
104 await httpReq(`${this._options.noHTTPS ? 'http' : 'https'}://${this._options.host}/p1`, {
105 method: 'POST',
106 body: {
107 beacons: logsToFlush,
108 common: this._options.systemMeta,
109 client_sending_time_ms: Date.now(),
110 },
111 timeout: 10000,
112 })
113
114 } catch (err) {
115 console.error('Failed to flush logs', (new Date()).toUTCString(), err) // eslint-disable-line no-console
116 }
117 }
118}