1 |
|
2 | e = require 'expect.js'
|
3 | fs = require 'fs'
|
4 | path = require 'path'
|
5 | stream = require 'stream'
|
6 |
|
7 | class mockStream
|
8 | constructor : () -> @chunks = []
|
9 | write : (chunk) ->
|
10 | @chunks.push chunk
|
11 | clean : -> chunks = []
|
12 | toString : (nocolor = true) ->
|
13 | data = ''
|
14 | for chunk in @chunks
|
15 | data += chunk.toString()
|
16 | data
|
17 | toStringNoColor : (nocolor = true) ->
|
18 | data = ''
|
19 | for chunk in @chunks
|
20 | data += chunk.toString()
|
21 | data.replace /\x1b\[\d+m/g, ''
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | mock =
|
27 | send : ''
|
28 | headers : {}
|
29 | req :
|
30 | url : '/'
|
31 | headers :
|
32 | 'user-agent' : 'mock server'
|
33 | 'referer' : 'mock refer'
|
34 | socket :
|
35 | remoteAddress : '127.0.0.1'
|
36 | remotePort : 1234
|
37 | httpVersionMajor : 1
|
38 | httpVersionMinor : 1
|
39 | method : 'GET'
|
40 | resp :
|
41 | write : (data)-> mock.send += data.toString() if data?
|
42 | end : (data)-> mock.send += data.toString() if data?
|
43 | statusCode : 0
|
44 | setHeader : (name, value) -> mock.headers[name.toLowerCase()] = value
|
45 | getHeader : (name) -> mock.headers[name.toLowerCase()]
|
46 | clean : ->
|
47 | mock.send = ''
|
48 | mock.headers = {}
|
49 | mock.resp.statusCode = 0
|
50 | mock.url = '/'
|
51 | mock.resp.headers = mock.headers
|
52 |
|
53 | describe 'JustLog', ->
|
54 | jl = require '../lib/justlog'
|
55 | jl.config flushTime : 10
|
56 | {pre: predefined} = require '../lib/pattern'
|
57 | options = stdout = stderr = l = null
|
58 | dir = "#{__dirname}/log_file"
|
59 |
|
60 | beforeEach ->
|
61 | mock.clean()
|
62 | stdout = new mockStream
|
63 | stderr = new mockStream
|
64 | options =
|
65 | file:
|
66 | path : "[#{dir}/test.txt]"
|
67 | stdio :
|
68 | stdout : stdout
|
69 | stderr : stderr
|
70 |
|
71 |
|
72 | afterEach (done)->
|
73 | return done() if not l?
|
74 | l.close ()->
|
75 | setTimeout ->
|
76 | try
|
77 | for file in fs.readdirSync dir
|
78 | fs.unlinkSync "#{dir}/#{file}"
|
79 | fs.rmdirSync dir
|
80 | done()
|
81 | , 100
|
82 | l = null
|
83 |
|
84 | describe 'options init', ->
|
85 | it 'check default options', (done)->
|
86 | l = new jl
|
87 | e(jl.INFO).to.be l.INFO
|
88 | e(jl.DEBUG).to.be l.DEBUG
|
89 | e(jl.WARN).to.be l.WARN
|
90 | e(jl.ERROR).to.be l.ERROR
|
91 |
|
92 | e(l.options.file.level).to.be jl.EXCEPTION
|
93 | e(l.options.file.path).to.be "[#{process.cwd()}/logs/_mocha-]YYYY-MM-DD[.log]"
|
94 | e(l.options.stdio.level).to.be jl.ALL
|
95 | e(l.options.stdio.stdout).to.be process.stdout
|
96 | e(l.options.stdio.stderr).to.be process.stderr
|
97 | e(l.options.file.render.pattern).to.be 'file'
|
98 | e(l.options.stdio.render.pattern).to.be 'color'
|
99 | setTimeout done, 100
|
100 | describe 'stdio', ->
|
101 | it 'stdout with default output pattern', (done)->
|
102 | options.file = false
|
103 | l = new jl options
|
104 | l.info 'simple info'
|
105 | l.debug 'simple debug'
|
106 | l.warn 'simple warn'
|
107 | l.error 'simple error'
|
108 | e(stdout.toStringNoColor()).to.match ///
|
109 | ^
|
110 | \d{2}:\d{2}:\d{2}\s
|
111 | INFO\s+(out/test/)?tests/test-justlog\.(js|coffee):\d+\ssimple\sinfo\n
|
112 | \d{2}:\d{2}:\d{2}\s
|
113 | DEBUG\s+(out/test/)?tests/test-justlog\.(js|coffee):\d+\ssimple\sdebug\n
|
114 | $
|
115 | ///
|
116 | e(stderr.toStringNoColor()).to.match ///
|
117 | ^
|
118 | \d{2}:\d{2}:\d{2}\s
|
119 | WARN\s+(out/test/)?tests/test-justlog\.(js|coffee):\d+\ssimple\swarn\n
|
120 | \d{2}:\d{2}:\d{2}\s
|
121 | ERROR\s+(out/test/)?tests/test-justlog\.(js|coffee):\d+\ssimple\serror\n
|
122 | $
|
123 | ///
|
124 | done()
|
125 |
|
126 | it 'stdout with default output pattern and printf info', (done)->
|
127 | options.file.level = 0
|
128 | options.stdio.level = jl.INFO | jl.DEBUG
|
129 | l = new jl options
|
130 | l.info 'simple info', 'data2', 123
|
131 | l.debug 'simple debug %s %d %s %j', 'data3', 456, [1, 2, 3], a:1
|
132 | l.warn 'simple warn %s'
|
133 | l.error 'simple error'
|
134 | e(stdout.toStringNoColor()).to.match ///
|
135 | ^
|
136 | \d{2}:\d{2}:\d{2}\s
|
137 | INFO\s+(out/test/)?tests/test-justlog\.(js|coffee):\d+\ssimple\sinfo\sdata2\s123\n
|
138 | \d{2}:\d{2}:\d{2}\s
|
139 | DEBUG\s+(out/test/)?tests/test-justlog\.(js|coffee):\d+\ssimple\sdebug\sdata3\s456\s1,2,3\s\{"a":1\}\n
|
140 | $
|
141 | ///
|
142 | e(stderr.toStringNoColor()).to.be ''
|
143 | done()
|
144 | it 'stdout with default output pattern and change placeholder to "_"', (done)->
|
145 | options.file = false
|
146 | options.placeholder = "_"
|
147 | l = new jl options
|
148 | l.info {}
|
149 | e(stdout.toStringNoColor()).to.match ///
|
150 | ^
|
151 | \d{2}:\d{2}:\d{2}\s
|
152 | INFO\s+(out/test/)?tests/test-justlog\.(js|coffee):\d+\s_\n
|
153 | $
|
154 | ///
|
155 | done()
|
156 | describe 'file', ->
|
157 | it 'write warn & error log with default pattern', (done)->
|
158 | options.stdio = false
|
159 | l = new jl options
|
160 | l.info 'simple info'
|
161 | l.debug 'simple debug'
|
162 | l.warn 'simple warn'
|
163 | l.error 'simple error'
|
164 | l.close ->
|
165 | e(fs.readFileSync(l.file.path).toString()).to.match ///
|
166 | ^
|
167 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
168 | \[WARN\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\swarn\n
|
169 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
170 | \[ERROR\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\serror\n
|
171 | $
|
172 | ///
|
173 | done()
|
174 |
|
175 | it 'rename file when current stream is not closed', (done)->
|
176 | options.stdio.level = 0
|
177 | options.file._watcher_timeout = 10
|
178 |
|
179 | l = new jl options
|
180 | flag = 0
|
181 | l.on 'rename', (file) ->
|
182 | flag++
|
183 | e(file).to.be l.file.path
|
184 | l.warn 'simple warn'
|
185 | setTimeout ->
|
186 | fs.renameSync l.file.path, l.file.path+'.move'
|
187 | , 300
|
188 | setTimeout ->
|
189 | l.error 'simple error'
|
190 | , 600
|
191 | setTimeout ->
|
192 | fs.renameSync l.file.path, l.file.path+'.move1'
|
193 | , 900
|
194 | setTimeout ->
|
195 | l.error 'simple error2'
|
196 | , 1200
|
197 | setTimeout ->
|
198 | e(flag).to.be 2
|
199 | l.close ()->
|
200 | e(fs.readFileSync(l.file.path+'.move').toString()).to.match ///
|
201 | ^
|
202 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
203 | \[WARN\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\swarn\n
|
204 | $
|
205 | ///
|
206 | e(fs.readFileSync(l.file.path+'.move1').toString()).to.match ///
|
207 | ^
|
208 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
209 | \[ERROR\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\serror\n
|
210 | $
|
211 | ///
|
212 | e(fs.readFileSync(l.file.path).toString()).to.match ///
|
213 | ^
|
214 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
215 | \[ERROR\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\serror2\n
|
216 | $
|
217 | ///
|
218 | done()
|
219 | , 1500
|
220 | it 'file inode changed when current stream is not closed', (done)->
|
221 |
|
222 | options.stdio = false
|
223 | options.file._watcher_timeout = 10
|
224 | l = new jl options
|
225 | flag = 0
|
226 | l.on 'rename', (file) ->
|
227 | flag++
|
228 | e(file).to.be l.file.path
|
229 | l.warn 'simple warn'
|
230 | setTimeout ->
|
231 | fs.renameSync l.file.path, l.file.path+'.move'
|
232 | fs.writeFileSync l.file.path, 'somedata1\n'
|
233 | , 300
|
234 | setTimeout ->
|
235 | l.error 'simple error'
|
236 | , 600
|
237 | setTimeout ->
|
238 | fs.renameSync l.file.path, l.file.path+'.move1'
|
239 | fs.writeFileSync l.file.path, 'somedata2\n'
|
240 | , 900
|
241 | setTimeout ->
|
242 | l.error 'simple error2'
|
243 | , 1200
|
244 | setTimeout ->
|
245 | e(flag).to.be 2
|
246 | l.close ()->
|
247 | e(fs.readFileSync(l.file.path+'.move').toString()).to.match ///
|
248 | ^
|
249 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
250 | \[WARN\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\swarn\n
|
251 | $
|
252 | ///
|
253 | e(fs.readFileSync(l.file.path+'.move1').toString()).to.match ///
|
254 | ^
|
255 | somedata1\n
|
256 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
257 | \[ERROR\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\serror\n
|
258 | $
|
259 | ///
|
260 | e(fs.readFileSync(l.file.path).toString()).to.match ///
|
261 | ^
|
262 | somedata2\n
|
263 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
264 | \[ERROR\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\serror2\n
|
265 | $
|
266 | ///
|
267 | done()
|
268 | , 1500
|
269 |
|
270 | it 'logfile rotate by time', (done)->
|
271 | nowMs = new Date().getMilliseconds()
|
272 | this.timeout 10000
|
273 | setTimeout ->
|
274 | options.stdio = false
|
275 | options.file.watcher_timeout = 10
|
276 | options.file.path = "[#{dir}]/ss.txt"
|
277 | l = new jl options
|
278 | files = [l.file.path]
|
279 | l.on 'rotate', (prev, curr) ->
|
280 | files.push curr
|
281 | tflag = 0
|
282 | l.on 'timer', (ms)->
|
283 | e(ms).to.below 1001
|
284 | e(ms).to.above 99
|
285 | tflag++
|
286 |
|
287 | l.warn 'simple warn'
|
288 | setTimeout ->
|
289 | l.error 'simple error1'
|
290 | , 1000
|
291 | setTimeout ->
|
292 | l.error 'simple error2'
|
293 | , 2000
|
294 | setTimeout ->
|
295 | l.error 'simple error3'
|
296 | l.close ->
|
297 |
|
298 | e(tflag).to.above 3
|
299 |
|
300 | flag = 0
|
301 |
|
302 | for k in files
|
303 | txt = fs.readFileSync(k).toString().trim()
|
304 | fs.unlinkSync k
|
305 | continue unless txt
|
306 | switch ++flag
|
307 | when 1
|
308 | e(txt).to.match ///
|
309 | ^
|
310 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
311 | \[WARN\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\swarn\n?
|
312 | $
|
313 | ///
|
314 | when 2
|
315 | e(txt).to.match ///
|
316 | ^
|
317 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
318 | \[ERROR\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\serror1\n?
|
319 | $
|
320 | ///
|
321 | when 3
|
322 | e(txt).to.match ///
|
323 | ^
|
324 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
325 | \[ERROR\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\serror2\n?
|
326 | $
|
327 | ///
|
328 | when 4
|
329 | e(txt).to.match ///
|
330 | ^
|
331 | \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\s
|
332 | \[ERROR\]\s+\((out/test/)?tests/test-justlog\.(js|coffee):\d+\)\ssimple\serror3\n?
|
333 | $
|
334 | ///
|
335 | e(flag).to.be 4
|
336 | done()
|
337 | , 3000
|
338 | , nowMs + 10
|
339 | describe 'middleware', ->
|
340 | it 'simple 200 response', (done)->
|
341 | m = jl.middleware options
|
342 | l = m.justlog
|
343 | mock.resp.statusCode = 200
|
344 | mock.req.url = '/simple_200'
|
345 | m mock.req, mock.resp, -> setTimeout (->mock.resp.end 'some data1'), 20
|
346 | setTimeout ->
|
347 | mock.resp.end 'some data'
|
348 | std = stdout.toString()
|
349 | e(stdout.toString()).to.match ///
|
350 | ^
|
351 | \x1b\[33m127\.0\.0\.1\x1b\[0m\s
|
352 | -\s
|
353 | -\s
|
354 | \[\d{1,2}/\w{3}/\d{4}:\d\d:\d\d:\d\d\s[\+\-]?\d{4}\]\s
|
355 | "\x1b\[32mGET\x1b\[0m\s\x1b\[4m\x1b\[1m\x1b\[34m/simple_200\x1b\[0m\sHTTP/1.1"\s
|
356 | \x1b\[32m200\x1b\[0m\s-\s
|
357 | "\x1b\[34mmock\srefer\x1b\[0m"\s
|
358 | "\x1b\[36mmock\sserver\x1b\[0m"\s
|
359 | [12]\d\n
|
360 | $
|
361 | ///
|
362 |
|
363 | fbody = fs.readFileSync(l.file.path).toString()
|
364 | e(fbody).to.match
|
365 | ///
|
366 | ^
|
367 | 127\.0\.0\.1\s
|
368 | -\s
|
369 | -\s
|
370 | \[\d{1,2}/\w{3}/\d{4}:\d\d:\d\d:\d\d\s[\+\-]?\d{4}\]\s
|
371 | "GET\s /simple_200\sHTTP/1.1"\s
|
372 | 200\s-\s
|
373 | "mock\srefer"\s
|
374 | "mock\sserver"\s
|
375 | [12]\d\n
|
376 | $
|
377 | ///
|
378 | done()
|
379 | , 100
|
380 | it 'with traceid', (done)->
|
381 | options.traceid = true
|
382 | options.file.pattern = 'accesslog-traceid'
|
383 | m = jl.middleware options
|
384 | l = m.justlog
|
385 | mock.resp.statusCode = 200
|
386 | mock.req.url = '/traceid'
|
387 | m mock.req, mock.resp, -> setTimeout (->mock.resp.end 'some data1'), 20
|
388 | setTimeout ->
|
389 | mock.resp.end 'some data'
|
390 | fbody = fs.readFileSync(l.file.path).toString()
|
391 | e(fbody).to.match
|
392 | ///
|
393 | ^
|
394 | 127\.0\.0\.1\s
|
395 | -\s
|
396 | -\s
|
397 | \[\d{1,2}/\w{3}/\d{4}:\d\d:\d\d:\d\d\s[\+\-]?\d{4}\]\s
|
398 | "GET\s /traceid\sHTTP/1.1"\s
|
399 | 200\s-\s
|
400 | "mock\srefer"\s
|
401 | "mock\sserver"\s
|
402 | [12]\d\s
|
403 | [\w\/\=]{24}\n
|
404 | $
|
405 | ///
|
406 | done()
|
407 | , 100
|
408 |
|
409 |
|