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