1 |
|
2 | moment = require 'moment'
|
3 | colors = require './colors'
|
4 | path = require 'path'
|
5 | levels = require './levels'
|
6 |
|
7 | cwd = process.cwd()
|
8 | reg = [
|
9 | /\b(file|lineno|stack|stackColored)\b/
|
10 | /\b(now|time|date|fulltime|numbertime|mstimestamp|timestamp|moment)\b/
|
11 | /\(([^\)\(]+?):(\d+):\d+\)$/]
|
12 | stackNames = ['file', 'lineno', 'stack', 'stackColored']
|
13 | timeNames = ['now', 'time', 'date', 'fulltime', 'numbertime', 'mstimestamp', 'timestamp']
|
14 |
|
15 | timeFormats =
|
16 | time : 'HH:mm:ss'
|
17 | date : 'YYYY-MM-DD'
|
18 | fulltime : 'YYYY-MM-DD HH:mm:ss'
|
19 | numbertime : 'YYYYMMDDHHmmss'
|
20 |
|
21 | justlogPath = __dirname + '/log' + path.extname __filename
|
22 |
|
23 | anonymous = '<anonymous>'
|
24 |
|
25 | module.exports = pattern =
|
26 | |
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | pre :
|
46 | 'simple-nocolor' : '{level} {msg}'
|
47 | 'simple-color' : '{color.level level} {msg}'
|
48 | 'nocolor' : '{time} [{levelTrim}] ({stack}) {msg}'
|
49 | 'color' : '{time} {color.level level} {stackColored} {msg}'
|
50 | 'file' : '{fulltime} [{levelTrim}] ({stack}) {msg}'
|
51 | 'event-color' : '{time} {color.event event} {args}'
|
52 | 'event-nocolor' : '{fulltime} {event} {args}'
|
53 | 'accesslog' : '''
|
54 | {remote-address} {ident} {user}
|
55 | [{now "DD/MMM/YYYY:HH:mm:ss ZZ"}]
|
56 | "{method} {url} HTTP/{version}"
|
57 | {status} {content-length}
|
58 | "{headers.referer}" "{headers.user-agent}"
|
59 | '''.replace /\n/g, ' '
|
60 | 'accesslog-rt' : '''
|
61 | {remote-address} {ident} {user}
|
62 | [{now 'DD/MMM/YYYY:HH:mm:ss ZZ'}]
|
63 | "{method} {url} HTTP/{version}"
|
64 | {status} {content-length}
|
65 | "{headers.referer}" "{headers.user-agent}" {rt}
|
66 | '''.replace /\n/g, ' '
|
67 | 'accesslog-color' : '''
|
68 | {remote-address@yellow} {ident} {user}
|
69 | [{now 'DD/MMM/YYYY:HH:mm:ss ZZ'}]
|
70 | "{color.method method} {url@underline,bold,blue} HTTP/{version}"
|
71 | {color.status status} {content-length}
|
72 | "{headers.referer@blue}" "{headers.user-agent@cyan}" {rt}
|
73 | '''.replace /\n/g, ' '
|
74 | 'accesslog-traceid' : '''
|
75 | {remote-address}:{remote-port} {ident} {user}
|
76 | [{now 'DD/MMM/YYYY:HH:mm:ss ZZ'}]
|
77 | "{method} {url} HTTP/{version}"
|
78 | {status} {content-length}
|
79 | "{headers.referer}" "{headers.user-agent}" {rt} {traceid}
|
80 | '''.replace /\n/g, ' '
|
81 |
|
82 | |
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 | compile : (pat, options = placeholder : '-')->
|
93 | {placeholder, traceid} = options
|
94 | code = pattern.pre[pat] ? pat
|
95 | code = code.replace /"/g, '\\"'
|
96 | useStack = false
|
97 | useTime = false
|
98 |
|
99 | funcs = []
|
100 | code = code.replace ///
|
101 | \{
|
102 | ([a-zA-Z][\-\w]+)
|
103 | (?:\.([\w\-]+))?
|
104 | (?:\s([^}@]+?))?
|
105 | (?:@((?:[a-z_]+,?)+))?
|
106 | \}
|
107 | ///g, (match, name, key, args, style) ->
|
108 | useStack = true if name in stackNames
|
109 | useTime = true if name in timeNames
|
110 | codes = []
|
111 |
|
112 |
|
113 | code = ''
|
114 | styles = style.split ',' if style
|
115 | if styles
|
116 | code += colors[style] for style in styles
|
117 | codes.push '"' + code + '"'
|
118 |
|
119 |
|
120 | code = ''
|
121 | if args
|
122 | num = funcs.length
|
123 | funcs.push [name, key, args.replace(/\\"/g, '"')]
|
124 | code = "__func[#{num}]"
|
125 | else if name of timeFormats
|
126 | code += "__vars.now('#{timeFormats[name]}')"
|
127 | else
|
128 | code += "__vars['#{name}']#{if key then "['#{key}']" else ''}"
|
129 | codes.push "(#{code}||\"#{placeholder}\")"
|
130 |
|
131 |
|
132 | codes.push '"' + colors.reset + '"' if styles
|
133 | '"+\n' + codes.join('+\n') + '+\n"'
|
134 |
|
135 |
|
136 | code = ('"' + code + '"').replace(/^""\+$/mg, '')
|
137 | code = "return #{code.trim()};"
|
138 |
|
139 | funcCode = []
|
140 | if funcs.length > 0
|
141 | funcCode.push 'var __func = [];'
|
142 | for [name, key, args] in funcs
|
143 | args = "__vars['#{args}']" if args[0].match /[a-z]/i
|
144 | funcCode.push "__func.push(__vars['#{name}']#{if key then "['#{key}']" else ''}(#{args}));"
|
145 |
|
146 | code = funcCode.join(";\n")+code
|
147 |
|
148 |
|
149 | func = new Function('__vars', code)
|
150 | func.stack = useStack
|
151 | func.time = useTime
|
152 | func.pattern = pat
|
153 | func
|
154 |
|
155 | |
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 | format : (render, msg, level) ->
|
164 | msg = '' if msg is null
|
165 | msg = msg: msg.toString() if typeof msg isnt 'object'
|
166 | msg.color = colors
|
167 | msg.level = levels.text[level]
|
168 | msg.levelTrim = msg.level.trim()
|
169 | if render.time
|
170 | msg.now = getFormatedTime
|
171 | msg.mstimestamp = moment().valueOf()
|
172 | msg.timestamp = Math.floor msg.mstimestamp / 1000
|
173 | msg = trackStack msg if render.stack
|
174 | render(msg) + "\n"
|
175 |
|
176 | FORMATED_TIME = {}
|
177 |
|
178 | getFormatedTime = ( format ) ->
|
179 | unless format of FORMATED_TIME
|
180 |
|
181 | interval = 1000
|
182 | timer = ->
|
183 | now = moment()
|
184 | setTimeout(timer, interval-now.milliseconds())
|
185 | FORMATED_TIME[format] = now.format format
|
186 | timer()
|
187 | FORMATED_TIME[format]
|
188 |
|
189 | trackStack = (msg) ->
|
190 | try
|
191 | throw new Error
|
192 | catch err
|
193 | return stackProcess err, msg
|
194 |
|
195 | stackProcess = (err, msg) ->
|
196 | stacks = err.stack.split "\n"
|
197 | flag = false
|
198 | for stack in stacks
|
199 | if res = stack.match reg[2]
|
200 | if res[1] isnt justlogPath and res[1] isnt __filename and res[1] isnt anonymous
|
201 | flag = true
|
202 | break
|
203 | if flag is false
|
204 | msg.file = 'NULL'
|
205 | msg.lineno = 0
|
206 | else
|
207 | file = res[1]
|
208 | msg.file = if file[0] is '/' then path.relative cwd, file else file
|
209 | msg.lineno = res[2]
|
210 | msg.stack = "#{msg.file}:#{msg.lineno}"
|
211 | msg.stackColored = "#{colors.underline}#{colors.cyan}#{msg.file}:#{colors.yellow}#{msg.lineno}#{colors.reset}"
|
212 | msg
|