├── .project
├── LICENSE
├── README.md
├── lib
├── cronTrigger.js
├── cronTriggerDecoder.js
├── job.js
├── priorityQueue.js
├── schedule.js
└── simpleTrigger.js
├── node_modules
└── log4js
│ ├── .npmignore
│ ├── .project
│ ├── .travis.yml
│ ├── README.md
│ ├── example-connect-logger.js
│ ├── example-socket.js
│ ├── example.js
│ ├── lib
│ ├── appenders
│ │ ├── console.js
│ │ ├── file.js
│ │ ├── gelf.js
│ │ ├── hookio.js
│ │ ├── logLevelFilter.js
│ │ ├── multiprocess.js
│ │ └── smtp.js
│ ├── connect-logger.js
│ ├── date_format.js
│ ├── layouts.js
│ ├── levels.js
│ ├── log4js.js
│ ├── log4js.json
│ └── streams.js
│ ├── log-rolling.js
│ ├── memory-test.js
│ ├── node_modules
│ ├── async
│ │ ├── .gitmodules
│ │ ├── LICENSE
│ │ ├── Makefile
│ │ ├── README.md
│ │ ├── deps
│ │ │ ├── nodeunit.css
│ │ │ └── nodeunit.js
│ │ ├── dist
│ │ │ └── async.min.js
│ │ ├── index.js
│ │ ├── lib
│ │ │ └── async.js
│ │ ├── nodelint.cfg
│ │ ├── package.json
│ │ └── test
│ │ │ ├── test-async.js
│ │ │ └── test.html
│ └── compress-buffer
│ │ ├── .lock-wscript
│ │ ├── .npmignore
│ │ ├── CHANGELOG
│ │ ├── LICENSE
│ │ ├── Makefile
│ │ ├── README.md
│ │ ├── build
│ │ ├── .wafpickle-7
│ │ ├── Release
│ │ │ ├── compress-buffer-bindings.node
│ │ │ └── src
│ │ │ │ └── compress-buffer_1.o
│ │ ├── c4che
│ │ │ ├── Release.cache.py
│ │ │ └── build.config.py
│ │ └── config.log
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── src
│ │ ├── compress-buffer.cc
│ │ └── compress-buffer.cc-
│ │ ├── t
│ │ ├── t.c
│ │ ├── t.plist
│ │ ├── test.csv
│ │ ├── test.gz
│ │ ├── test
│ │ └── node-compress-buffer-test.js
│ │ └── wscript
│ ├── package.json
│ └── test
│ ├── bufferedStream.js
│ ├── date_format.js
│ ├── fileAppender.js
│ ├── gelfAppender.js
│ ├── hookioAppender.js
│ ├── layouts.js
│ ├── levels.js
│ ├── log4js.json
│ ├── logLevelFilter.js
│ ├── logging.js
│ ├── multiprocessAppender.js
│ ├── rollingFileStream.js
│ ├── smtpAppender.js
│ ├── test-connect-logger.js
│ ├── test-global-log-level.js
│ ├── test-log-abspath.js
│ ├── test-nolog.js
│ ├── test-rolling-file-stream
│ ├── test-rolling-file-stream-write
│ ├── test-rolling-file-stream-write-less
│ ├── test-rolling-file-stream-write-more
│ ├── test-rolling-file-stream-write-more.1
│ ├── with-log-rolling.json
│ └── with-logLevelFilter.json
├── package.json
└── test
├── cronTriggerTest.js
├── priorityQueueTest.js
├── scheduleTest.js
└── test.js
/.project:
--------------------------------------------------------------------------------
1 |
2 |
73 | * * * * * * command to be executed 74 | - - - - - - 75 | | | | | | | 76 | | | | | | +----- day of week (0 - 6) (Sunday=0) 77 | | | | | +------- month (1 - 12) 78 | | | | +--------- day of month (1 - 31) 79 | | | +----------- hour (0 - 23) 80 | | +------------- min (0 - 59) 81 | +------------- second (0 - 59) 82 |83 | ###Exampe of cron tirggers 84 | 85 | "0/2 0 8 * * 6" Fire at every Satuaday at every even seconds of 08:00 86 | "0 30 10 1 4 *" Fire at 10:30 on 1st of March 87 | "15 15 15 10 10 *" Fire at Octorber 10th, at 15:15:15. 88 | 89 | ###Special characters 90 | Pomelo-schedule allow three kinds of spechial characters, they are '-', '/' and '.'. 91 | 92 | -: '-' means range. For example, 1-3 in the second field means the seconds 1, 2 and 3 93 | 94 | /: means increasement. For exapmle, 1/20 in the second field means 1, 21 and 41 second, and 1/2 means for every odd seconds as 1, 3, 5 ... ... 95 | 96 | ,: means additional values. For example, 1, 10, 15 in the second field means 1, 10 and 15 second. You can use '-', and '/' with ',', for example, 11,20-22,0/2 in the second filed means 11, 21 and all the even seconds. 97 | 98 | ##Cancel Job 99 | ``` javascript 100 | var schedule = require('../lib/schedule'); 101 | 102 | var simpleJob = function(){ 103 | console.log("run simple Job "); 104 | } 105 | 106 | //Add a simple job and save the id 107 | var id = schedule.scheduleJob({period: 1000}, simpleJob, {}); 108 | 109 | /** 110 | * Do some thing else 111 | */ 112 | 113 | //CancelJob 114 | schedule.cancelJob(id); 115 | ``` 116 | When you cancel a job, it will stop schedule immidiatelly, and delete the job. 117 | -------------------------------------------------------------------------------- /lib/cronTrigger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the trigger used to decode the cronTimer and calculate the next excution time of the cron Trigger. 3 | */ 4 | var logger = require('log4js').getLogger(__filename); 5 | 6 | var SECOND = 0; 7 | var MIN = 1; 8 | var HOUR = 2; 9 | var DOM = 3; 10 | var MONTH = 4; 11 | var DOW = 5; 12 | 13 | var Limit = [ 14 | [0, 59], 15 | [0, 59], 16 | [0, 24], 17 | [1, 31], 18 | [0, 11], 19 | [0, 6] 20 | ]; 21 | 22 | /** 23 | * The constructor of the CronTrigger 24 | * @param trigger The trigger str used to build the cronTrigger instance 25 | */ 26 | var CronTrigger = function(trigger, job) { 27 | this.trigger = this.decodeTrigger(trigger); 28 | 29 | this.nextTime = this.nextExcuteTime(Date.now()); 30 | 31 | this.job = job; 32 | }; 33 | 34 | var pro = CronTrigger.prototype; 35 | 36 | /** 37 | * Get the current excuteTime of trigger 38 | */ 39 | pro.excuteTime = function() { 40 | return this.nextTime; 41 | }; 42 | 43 | /** 44 | * Caculate the next valid cronTime after the given time 45 | * @param The given time point 46 | * @return The nearest valid time after the given time point 47 | */ 48 | pro.nextExcuteTime = function(time) { 49 | //add 1s to the time so it must be the next time 50 | time = !!time ? time : this.nextTime; 51 | time += 1000; 52 | 53 | var cronTrigger = this.trigger; 54 | var date = new Date(time); 55 | date.setMilliseconds(0); 56 | 57 | outmost: while (true) { 58 | if (date.getFullYear() > 2999) { 59 | logger.error("Can't compute the next time, exceed the limit"); 60 | return null; 61 | } 62 | if (!timeMatch(date.getMonth(), cronTrigger[MONTH])) { 63 | var nextMonth = nextCronTime(date.getMonth(), cronTrigger[MONTH]); 64 | 65 | if (nextMonth == null) 66 | return null; 67 | 68 | if (nextMonth <= date.getMonth()) { 69 | date.setYear(date.getFullYear() + 1); 70 | date.setMonth(0); 71 | date.setDate(1); 72 | date.setHours(0); 73 | date.setMinutes(0); 74 | date.setSeconds(0); 75 | continue; 76 | } 77 | 78 | date.setDate(1); 79 | date.setMonth(nextMonth); 80 | date.setHours(0); 81 | date.setMinutes(0); 82 | date.setSeconds(0); 83 | } 84 | 85 | if (!timeMatch(date.getDate(), cronTrigger[DOM]) || !timeMatch(date.getDay(), cronTrigger[DOW])) { 86 | var domLimit = getDomLimit(date.getFullYear(), date.getMonth()); 87 | 88 | do { 89 | var nextDom = nextCronTime(date.getDate(), cronTrigger[DOM]); 90 | if (nextDom == null) 91 | return null; 92 | 93 | //If the date is in the next month, add month 94 | if (nextDom <= date.getDate() || nextDom > domLimit) { 95 | date.setDate(1); 96 | date.setMonth(date.getMonth() + 1); 97 | date.setHours(0); 98 | date.setMinutes(0); 99 | date.setSeconds(0); 100 | continue outmost; 101 | } 102 | 103 | date.setDate(nextDom); 104 | } while (!timeMatch(date.getDay(), cronTrigger[DOW])); 105 | 106 | date.setHours(0); 107 | date.setMinutes(0); 108 | date.setSeconds(0); 109 | } 110 | 111 | if (!timeMatch(date.getHours(), cronTrigger[HOUR])) { 112 | var nextHour = nextCronTime(date.getHours(), cronTrigger[HOUR]); 113 | 114 | if (nextHour <= date.getHours()) { 115 | date.setDate(date.getDate() + 1); 116 | date.setHours(nextHour); 117 | date.setMinutes(0); 118 | date.setSeconds(0); 119 | continue; 120 | } 121 | 122 | date.setHours(nextHour); 123 | date.setMinutes(0); 124 | date.setSeconds(0); 125 | } 126 | 127 | if (!timeMatch(date.getMinutes(), cronTrigger[MIN])) { 128 | var nextMinute = nextCronTime(date.getMinutes(), cronTrigger[MIN]); 129 | 130 | if (nextMinute <= date.getMinutes()) { 131 | date.setHours(date.getHours() + 1); 132 | date.setMinutes(nextMinute); 133 | date.setSeconds(0); 134 | continue; 135 | } 136 | 137 | date.setMinutes(nextMinute); 138 | date.setSeconds(0); 139 | } 140 | 141 | if (!timeMatch(date.getSeconds(), cronTrigger[SECOND])) { 142 | var nextSecond = nextCronTime(date.getSeconds(), cronTrigger[SECOND]); 143 | 144 | if (nextSecond <= date.getSeconds()) { 145 | date.setMinutes(date.getMinutes() + 1); 146 | date.setSeconds(nextSecond); 147 | continue; 148 | } 149 | 150 | date.setSeconds(nextSecond); 151 | } 152 | break; 153 | } 154 | 155 | this.nextTime = date.getTime(); 156 | return this.nextTime; 157 | }; 158 | 159 | /** 160 | * return the next match time of the given value 161 | * @param value The time value 162 | * @param cronTime The cronTime need to match 163 | * @return The match value or null if unmatch(it offten means an error occur). 164 | */ 165 | function nextCronTime(value, cronTime) { 166 | value += 1; 167 | 168 | if (typeof(cronTime) == 'number') { 169 | if (cronTime == -1) 170 | return value; 171 | else 172 | return cronTime; 173 | } else if (typeof(cronTime) == 'object' && cronTime instanceof Array) { 174 | if (value <= cronTime[0] || value > cronTime[cronTime.length - 1]) 175 | return cronTime[0]; 176 | 177 | for (var i = 0; i < cronTime.length; i++) 178 | if (value <= cronTime[i]) 179 | return cronTime[i]; 180 | } 181 | 182 | logger.warn('Compute next Time error! value :' + value + ' cronTime : ' + cronTime); 183 | return null; 184 | } 185 | 186 | /** 187 | * Match the given value to the cronTime 188 | * @param value The given value 189 | * @param cronTime The cronTime 190 | * @return The match result 191 | */ 192 | function timeMatch(value, cronTime) { 193 | if (typeof(cronTime) == 'number') { 194 | if (cronTime == -1) 195 | return true; 196 | if (value == cronTime) 197 | return true; 198 | return false; 199 | } else if (typeof(cronTime) == 'object' && cronTime instanceof Array) { 200 | if (value < cronTime[0] || value > cronTime[cronTime.length - 1]) 201 | return false; 202 | 203 | for (var i = 0; i < cronTime.length; i++) 204 | if (value == cronTime[i]) 205 | return true; 206 | 207 | return false; 208 | } 209 | 210 | return null; 211 | } 212 | 213 | /** 214 | * Decude the cronTrigger string to arrays 215 | * @param cronTimeStr The cronTimeStr need to decode, like "0 12 * * * 3" 216 | * @return The array to represent the cronTimer 217 | */ 218 | pro.decodeTrigger = function(cronTimeStr) { 219 | cronTimeStr = cronTimeStr.trim(); 220 | var cronTimes = cronTimeStr.split(/\s+/); 221 | 222 | if (cronTimes.length != 6) { 223 | console.log('error'); 224 | return null; 225 | } 226 | 227 | for (var i = 0; i < cronTimes.length; i++) { 228 | cronTimes[i] = (this.decodeTimeStr(cronTimes[i], i)); 229 | 230 | if (!checkNum(cronTimes[i], Limit[i][0], Limit[i][1])) { 231 | logger.error('Decode crontime error, value exceed limit!' + 232 | JSON.stringify({ 233 | cronTime: cronTimes[i], 234 | limit: Limit[i] 235 | })); 236 | return null; 237 | } 238 | } 239 | 240 | return cronTimes; 241 | } 242 | 243 | /** 244 | * Decode the cron Time string 245 | * @param timeStr The cron time string, like: 1,2 or 1-3 246 | * @return A sorted array, like [1,2,3] 247 | */ 248 | pro.decodeTimeStr = function(timeStr, type) { 249 | var result = {}; 250 | var arr = []; 251 | 252 | if (timeStr == '*') { 253 | return -1; 254 | } else if (timeStr.search(',') > 0) { 255 | var timeArr = timeStr.split(','); 256 | for (var i = 0; i < timeArr.length; i++) { 257 | var time = timeArr[i]; 258 | if (time.match(/^\d+-\d+$/)) { 259 | decodeRangeTime(result, time); 260 | } else if (time.match(/^\d+\/\d+/)) { 261 | decodePeriodTime(result, time, type); 262 | } else if (!isNaN(time)) { 263 | var num = Number(time); 264 | result[num] = num; 265 | } else 266 | return null; 267 | } 268 | } else if (timeStr.match(/^\d+-\d+$/)) { 269 | decodeRangeTime(result, timeStr); 270 | } else if (timeStr.match(/^\d+\/\d+/)) { 271 | decodePeriodTime(result, timeStr, type); 272 | } else if (!isNaN(timeStr)) { 273 | var num = Number(timeStr); 274 | result[num] = num; 275 | } else { 276 | return null; 277 | } 278 | 279 | for (var key in result) { 280 | arr.push(result[key]); 281 | } 282 | 283 | arr.sort(function(a, b) { 284 | return a - b; 285 | }); 286 | 287 | return arr; 288 | } 289 | 290 | /** 291 | * Decode time range 292 | * @param map The decode map 293 | * @param timeStr The range string, like 2-5 294 | */ 295 | function decodeRangeTime(map, timeStr) { 296 | var times = timeStr.split('-'); 297 | 298 | times[0] = Number(times[0]); 299 | times[1] = Number(times[1]); 300 | if (times[0] > times[1]) { 301 | console.log("Error time range"); 302 | return null; 303 | } 304 | 305 | for (var i = times[0]; i <= times[1]; i++) { 306 | map[i] = i; 307 | } 308 | } 309 | 310 | /** 311 | * Compute the period timer 312 | */ 313 | function decodePeriodTime(map, timeStr, type) { 314 | var times = timeStr.split('/'); 315 | var min = Limit[type][0]; 316 | var max = Limit[type][1]; 317 | 318 | var remind = Number(times[0]); 319 | var period = Number(times[1]); 320 | 321 | if (period == 0) 322 | return; 323 | 324 | for (var i = remind; i <= max; i += period) { 325 | // if (i % period == remind) 326 | map[i] = i; 327 | // } 328 | } 329 | } 330 | 331 | /** 332 | * Check if the numbers are valid 333 | * @param nums The numbers array need to check 334 | * @param min Minimus value 335 | * @param max Maximam value 336 | * @return If all the numbers are in the data range 337 | */ 338 | function checkNum(nums, min, max) { 339 | if (nums == null) 340 | return false; 341 | 342 | if (nums == -1) 343 | return true; 344 | 345 | for (var i = 0; i < nums.length; i++) { 346 | if (nums[i] < min || nums[i] > max) 347 | return false; 348 | } 349 | 350 | return true; 351 | } 352 | 353 | /** 354 | * Get the date limit of given month 355 | * @param The given year 356 | * @month The given month 357 | * @return The date count of given month 358 | */ 359 | function getDomLimit(year, month) { 360 | var date = new Date(year, month + 1, 0); 361 | 362 | return date.getDate(); 363 | } 364 | 365 | /** 366 | * Create cronTrigger 367 | * @param trigger The Cron Trigger string 368 | * @return The Cron trigger 369 | */ 370 | function createTrigger(trigger, job) { 371 | return new CronTrigger(trigger, job); 372 | } 373 | 374 | module.exports.createTrigger = createTrigger; -------------------------------------------------------------------------------- /lib/cronTriggerDecoder.js: -------------------------------------------------------------------------------- 1 | var decoder = module.exports; 2 | decoder.decodeCronTime = decodeCronTime; 3 | 4 | var timer = { 5 | second: -1, 6 | min: -1, 7 | hour: -1, 8 | dom: -1, 9 | month: -1, 10 | dow: -1, 11 | executeTime: -1 12 | } 13 | 14 | var limit = [[0,59],[0,59],[0,24],[1,31],[1,12],[0,6]]; 15 | 16 | function nexExcuteTime(time, timer){ 17 | //add 1s to the time so it must be the next time 18 | time += 1000; 19 | var date = new Date(time); 20 | var nextTime = new Date(time); 21 | 22 | outmost: 23 | while(true){ 24 | if(!timeMatch(date.getMonth(), timer.month)){ 25 | var nextMonth = nextTime(date.getMonth(), timer.month); 26 | if(nextMonth < date.getMonth()){ 27 | date.setYear(date.getYear()+1); 28 | } 29 | date.setMonth(nextMonth); 30 | 31 | date.setDate(1); 32 | date.setHours(0); 33 | date.setMinutes(0); 34 | date.setSeconds(0); 35 | } 36 | 37 | if(!timeMatch(date.getDate(), timer.dom)){ 38 | do{ 39 | var nextDom = nextTime(date.getDate(), timer.dom); 40 | 41 | //If the date is in the next month, add month 42 | if(nextDom <= date.getDate()){ 43 | date.setMonth(date.getMonth() + 1); 44 | continue outmost; 45 | } 46 | 47 | //If the date exceed the limit, add month 48 | var domLimit = getDomLimit(); 49 | if(nexDom > domLimit){ 50 | date.setMonth(date.getMonth() + 1); 51 | continue outmost; 52 | } 53 | 54 | date.setDate(nextDom); 55 | }while(!timeMatch(date.getDay(), timer.dow)); 56 | 57 | date.setHours(0); 58 | date.setMinutes(0); 59 | date.setSeconds(0); 60 | } 61 | 62 | if(!timeMatch(date.getHours(), timer.hour)){ 63 | var nextHour = nextTime(date.getHours(), timer.hour); 64 | 65 | if(nextHour <= date.getHours()){ 66 | date.setDate(date.getDate() + 1); 67 | continue; 68 | } 69 | 70 | date.setHours(nextHour); 71 | date.setMinutes(0); 72 | date.setSeconds(0); 73 | } 74 | 75 | if(!timeMatch(date.getMinutes(), timer.minute)){ 76 | var nextMinute = nextTime(date.getMinutes(), timer.minute); 77 | 78 | if(nextMinute <= date.getMinutes()){ 79 | date.setHours(date.getHours() + 1); 80 | continue; 81 | } 82 | 83 | date.setMinutes(nextMinute); 84 | date.setSeconds(0); 85 | } 86 | 87 | if(!timeMatch(date.getSeconds(), timer.seconde)){ 88 | var nextSecond = nextTime(date.getSeconds(), timer.seconde); 89 | 90 | if(nextSecond <= date.getSeconds()){ 91 | date.setMinutes()(date.getMinutes() + 1); 92 | continue; 93 | } 94 | 95 | date.setSeconds(nextSecond); 96 | } 97 | 98 | break; 99 | } 100 | 101 | return date.getTime(); 102 | } 103 | 104 | /** 105 | * return the next match time of the given value 106 | */ 107 | function nextTime(value, cronTime){ 108 | if(typeof(cronTime) == 'number'){ 109 | if(cronTime == -1) 110 | return value + 1; 111 | else 112 | return cronTime; 113 | }else if(typeof(cronTime) == 'array'){ 114 | if(value < arr[0] || value > arr[arr.length -1]) 115 | return arr[0]; 116 | 117 | for(var i = 0; i < arr.length; i++) 118 | if(value < arr[i]) 119 | return arr[i]; 120 | } 121 | 122 | return null; 123 | } 124 | 125 | function timeMatch(value, cronTime){ 126 | if(tyeof(cronTime) == 'number'){ 127 | if(cronTime == -1) 128 | return true; 129 | if(value == cronTime) 130 | return true; 131 | return false; 132 | }else if(typeof(cronTime) == 'array'){ 133 | if(value < arr[0] || value > arr[arr.length -1]) 134 | return false; 135 | 136 | for(var i = 0; i < arr.length; i++) 137 | if(value == arr[i]) 138 | return true; 139 | 140 | return false; 141 | } 142 | 143 | return null; 144 | } 145 | 146 | function getDomLimit(year, month){ 147 | var date = new Date(year, month, 0); 148 | 149 | return date.getDate(); 150 | } 151 | 152 | function decodeCronTime(cronTime){ 153 | var timers = cronTime.split(/\s+/); 154 | 155 | if(timers.length != 6){ 156 | return null; 157 | } 158 | 159 | for(var i = 0; i < timers.length; i++){ 160 | timers[i] = (decodeTimeStr(timers[i])); 161 | 162 | if(!checkNum(timers[i], limit[i][0], limit[i][1])){ 163 | return null; 164 | } 165 | } 166 | 167 | return timers; 168 | } 169 | 170 | function decodeTimeStr(timeStr){ 171 | var result = {}; 172 | var arr = []; 173 | 174 | if(timeStr=='*'){ 175 | return -1; 176 | }else if(timeStr.search(',')>0){ 177 | var timeStrArray = timeStr.split(','); 178 | for(var i = 0; i < timeStrArray.length; i++){ 179 | var time = timeStrArray[i]; 180 | if(time.match(/^\d+-\d+$/)){ 181 | decodeRangeTime(result, time); 182 | }else if(!isNaN(timeStrArray[i])) 183 | result[i] = time; 184 | else 185 | return null; 186 | } 187 | }else if(timeStr.match(/^\d+-\d+$/)){ 188 | decodeRangeTime(result, time); 189 | }else if(!isNaN(timeStr)){ 190 | result[timeStr] = timeStr; 191 | }else{ 192 | return null; 193 | } 194 | 195 | for(var key in result) 196 | arr.push(result[key]); 197 | 198 | arr.sort(); 199 | 200 | return arr; 201 | } 202 | 203 | function decodeRangeTime(map, timeStr){ 204 | var times = timeStr.split('-'); 205 | 206 | if(times[0] > times[1]) 207 | return null; 208 | for(var i = times[0]; i <= times[1]; i++) 209 | map[i] = i; 210 | } 211 | 212 | function checkNum(nums, min, max){ 213 | if(nums == null) 214 | return false; 215 | 216 | if(nums == -1) 217 | return true; 218 | 219 | for(var i = 0; i < nums.length; i++){ 220 | if(nums[i]
103 | * [startTime] [logLevel] categoryName - message\n 104 | *105 | * 106 | * @author Stephan Strittmatter 107 | */ 108 | function basicLayout (loggingEvent) { 109 | return timestampLevelAndCategory(loggingEvent) + formatLogData(loggingEvent.data); 110 | } 111 | 112 | /** 113 | * colouredLayout - taken from masylum's fork. 114 | * same as basicLayout, but with colours. 115 | */ 116 | function colouredLayout (loggingEvent) { 117 | return timestampLevelAndCategory(loggingEvent, colours[loggingEvent.level.toString()]) + formatLogData(loggingEvent.data); 118 | } 119 | 120 | function messagePassThroughLayout (loggingEvent) { 121 | return formatLogData(loggingEvent.data); 122 | } 123 | 124 | /** 125 | * PatternLayout 126 | * Format for specifiers is %[padding].[truncation][field]{[format]} 127 | * e.g. %5.10p - left pad the log level by 5 characters, up to a max of 10 128 | * Fields can be any of: 129 | * - %r time in toLocaleTimeString format 130 | * - %p log level 131 | * - %c log category 132 | * - %m log data 133 | * - %d date in various formats 134 | * - %% % 135 | * - %n newline 136 | * Takes a pattern string and returns a layout function. 137 | * @author Stephan Strittmatter 138 | */ 139 | function patternLayout (pattern) { 140 | var TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n"; 141 | var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([cdmnpr%])(\{([^\}]+)\})?|([^%]+)/; 142 | 143 | pattern = pattern || TTCC_CONVERSION_PATTERN; 144 | 145 | return function(loggingEvent) { 146 | var formattedString = ""; 147 | var result; 148 | var searchString = pattern; 149 | 150 | while ((result = regex.exec(searchString))) { 151 | var matchedString = result[0]; 152 | var padding = result[1]; 153 | var truncation = result[2]; 154 | var conversionCharacter = result[3]; 155 | var specifier = result[5]; 156 | var text = result[6]; 157 | 158 | // Check if the pattern matched was just normal text 159 | if (text) { 160 | formattedString += "" + text; 161 | } else { 162 | // Create a raw replacement string based on the conversion 163 | // character and specifier 164 | var replacement = ""; 165 | switch(conversionCharacter) { 166 | case "c": 167 | var loggerName = loggingEvent.categoryName; 168 | if (specifier) { 169 | var precision = parseInt(specifier, 10); 170 | var loggerNameBits = loggingEvent.categoryName.split("."); 171 | if (precision >= loggerNameBits.length) { 172 | replacement = loggerName; 173 | } else { 174 | replacement = loggerNameBits.slice(loggerNameBits.length - precision).join("."); 175 | } 176 | } else { 177 | replacement = loggerName; 178 | } 179 | break; 180 | case "d": 181 | var format = dateFormat.ISO8601_FORMAT; 182 | if (specifier) { 183 | format = specifier; 184 | // Pick up special cases 185 | if (format == "ISO8601") { 186 | format = dateFormat.ISO8601_FORMAT; 187 | } else if (format == "ABSOLUTE") { 188 | format = dateFormat.ABSOLUTETIME_FORMAT; 189 | } else if (format == "DATE") { 190 | format = dateFormat.DATETIME_FORMAT; 191 | } 192 | } 193 | // Format the date 194 | replacement = dateFormat.asString(format, loggingEvent.startTime); 195 | break; 196 | case "m": 197 | replacement = formatLogData(loggingEvent.data); 198 | break; 199 | case "n": 200 | replacement = "\n"; 201 | break; 202 | case "p": 203 | replacement = loggingEvent.level.toString(); 204 | break; 205 | case "r": 206 | replacement = "" + loggingEvent.startTime.toLocaleTimeString(); 207 | break; 208 | case "%": 209 | replacement = "%"; 210 | break; 211 | default: 212 | replacement = matchedString; 213 | break; 214 | } 215 | // Format the replacement according to any padding or 216 | // truncation specified 217 | 218 | var len; 219 | 220 | // First, truncation 221 | if (truncation) { 222 | len = parseInt(truncation.substr(1), 10); 223 | replacement = replacement.substring(0, len); 224 | } 225 | // Next, padding 226 | if (padding) { 227 | if (padding.charAt(0) == "-") { 228 | len = parseInt(padding.substr(1), 10); 229 | // Right pad with spaces 230 | while (replacement.length < len) { 231 | replacement += " "; 232 | } 233 | } else { 234 | len = parseInt(padding, 10); 235 | // Left pad with spaces 236 | while (replacement.length < len) { 237 | replacement = " " + replacement; 238 | } 239 | } 240 | } 241 | formattedString += replacement; 242 | } 243 | searchString = searchString.substr(result.index + result[0].length); 244 | } 245 | return formattedString; 246 | }; 247 | 248 | }; 249 | 250 | 251 | module.exports = { 252 | basicLayout: basicLayout 253 | , messagePassThroughLayout: messagePassThroughLayout 254 | , patternLayout: patternLayout 255 | , colouredLayout: colouredLayout 256 | , coloredLayout: colouredLayout 257 | , layout: function(name, config) { 258 | return layoutMakers[name] && layoutMakers[name](config); 259 | } 260 | }; -------------------------------------------------------------------------------- /node_modules/log4js/lib/levels.js: -------------------------------------------------------------------------------- 1 | function Level(level, levelStr) { 2 | this.level = level; 3 | this.levelStr = levelStr; 4 | } 5 | 6 | /** 7 | * converts given String to corresponding Level 8 | * @param {String} sArg String value of Level 9 | * @param {Log4js.Level} defaultLevel default Level, if no String representation 10 | * @return Level object 11 | * @type Log4js.Level 12 | */ 13 | function toLevel(sArg, defaultLevel) { 14 | 15 | if (sArg === null) { 16 | return defaultLevel; 17 | } 18 | 19 | if (typeof sArg == "string") { 20 | var s = sArg.toUpperCase(); 21 | if (module.exports[s]) { 22 | return module.exports[s]; 23 | } 24 | } 25 | return defaultLevel; 26 | }; 27 | 28 | Level.prototype.toString = function() { 29 | return this.levelStr; 30 | }; 31 | 32 | Level.prototype.isLessThanOrEqualTo = function(otherLevel) { 33 | if (typeof otherLevel === "string") { 34 | otherLevel = toLevel(otherLevel); 35 | } 36 | return this.level <= otherLevel.level; 37 | }; 38 | 39 | Level.prototype.isGreaterThanOrEqualTo = function(otherLevel) { 40 | if (typeof otherLevel === "string") { 41 | otherLevel = toLevel(otherLevel); 42 | } 43 | return this.level >= otherLevel.level; 44 | }; 45 | 46 | module.exports = { 47 | ALL: new Level(Number.MIN_VALUE, "ALL") 48 | , TRACE: new Level(5000, "TRACE") 49 | , DEBUG: new Level(10000, "DEBUG") 50 | , INFO: new Level(20000, "INFO") 51 | , WARN: new Level(30000, "WARN") 52 | , ERROR: new Level(40000, "ERROR") 53 | , FATAL: new Level(50000, "FATAL") 54 | , OFF: new Level(Number.MAX_VALUE, "OFF") 55 | , toLevel: toLevel 56 | }; 57 | -------------------------------------------------------------------------------- /node_modules/log4js/lib/log4js.json: -------------------------------------------------------------------------------- 1 | { 2 | "appenders": [ 3 | { 4 | "type": "console" 5 | } 6 | ] 7 | } -------------------------------------------------------------------------------- /node_modules/log4js/lib/streams.js: -------------------------------------------------------------------------------- 1 | var util = require('util'), 2 | fs = require('fs'), 3 | path = require('path'), 4 | events = require('events'), 5 | async = require('async'); 6 | 7 | function debug(message) { 8 | util.debug(message); 9 | // console.log(message); 10 | } 11 | 12 | function BufferedWriteStream(stream) { 13 | var that = this; 14 | this.stream = stream; 15 | this.buffer = []; 16 | this.canWrite = false; 17 | this.bytes = 0; 18 | 19 | this.stream.on("open", function() { 20 | that.canWrite = true; 21 | that.flushBuffer(); 22 | }); 23 | 24 | this.stream.on("error", function (err) { 25 | that.emit("error", err); 26 | }); 27 | 28 | this.stream.on("drain", function() { 29 | that.canWrite = true; 30 | that.flushBuffer(); 31 | }); 32 | } 33 | 34 | util.inherits(BufferedWriteStream, events.EventEmitter); 35 | 36 | Object.defineProperty( 37 | BufferedWriteStream.prototype, 38 | "fd", 39 | { 40 | get: function() { return this.stream.fd; }, 41 | set: function(newFd) { 42 | this.stream.fd = newFd; 43 | this.bytes = 0; 44 | } 45 | } 46 | ); 47 | 48 | Object.defineProperty( 49 | BufferedWriteStream.prototype, 50 | "bytesWritten", 51 | { 52 | get: function() { return this.bytes; } 53 | } 54 | ); 55 | 56 | BufferedWriteStream.prototype.write = function(data, encoding) { 57 | this.buffer.push({ data: data, encoding: encoding }); 58 | this.flushBuffer(); 59 | }; 60 | 61 | BufferedWriteStream.prototype.end = function(data, encoding) { 62 | if (data) { 63 | this.buffer.push({ data: data, encoding: encoding }); 64 | } 65 | this.flushBufferEvenIfCannotWrite(); 66 | }; 67 | 68 | BufferedWriteStream.prototype.writeToStream = function(toWrite) { 69 | this.bytes += toWrite.data.length; 70 | this.canWrite = this.stream.write(toWrite.data, toWrite.encoding); 71 | }; 72 | 73 | BufferedWriteStream.prototype.flushBufferEvenIfCannotWrite = function() { 74 | while (this.buffer.length > 0) { 75 | this.writeToStream(this.buffer.shift()); 76 | } 77 | }; 78 | 79 | BufferedWriteStream.prototype.flushBuffer = function() { 80 | while (this.buffer.length > 0 && this.canWrite) { 81 | this.writeToStream(this.buffer.shift()); 82 | } 83 | }; 84 | 85 | function BaseRollingFileStream(filename, options) { 86 | this.filename = filename; 87 | this.options = options || { encoding: 'utf8', mode: 0644, flags: 'a' }; 88 | this.rolling = false; 89 | this.writesWhileRolling = []; 90 | this.currentSize = 0; 91 | 92 | function currentFileSize(file) { 93 | var fileSize = 0; 94 | try { 95 | fileSize = fs.statSync(file).size; 96 | } catch (e) { 97 | // file does not exist 98 | } 99 | return fileSize; 100 | } 101 | 102 | function throwErrorIfArgumentsAreNotValid() { 103 | if (!filename) { 104 | throw new Error("You must specify a filename"); 105 | } 106 | } 107 | 108 | throwErrorIfArgumentsAreNotValid(); 109 | 110 | BaseRollingFileStream.super_.call(this, this.filename, this.options); 111 | this.currentSize = currentFileSize(this.filename); 112 | } 113 | util.inherits(BaseRollingFileStream, fs.FileWriteStream); 114 | 115 | BaseRollingFileStream.prototype.initRolling = function() { 116 | var that = this; 117 | 118 | function emptyRollingQueue() { 119 | debug("emptying the rolling queue"); 120 | var toWrite; 121 | while ((toWrite = that.writesWhileRolling.shift())) { 122 | BaseRollingFileStream.super_.prototype.write.call(that, toWrite.data, toWrite.encoding); 123 | that.currentSize += toWrite.data.length; 124 | if (that.shouldRoll()) { 125 | that.flush(); 126 | return true; 127 | } 128 | } 129 | that.flush(); 130 | return false; 131 | } 132 | 133 | this.rolling = true; 134 | this.roll(this.filename, function() { 135 | that.currentSize = 0; 136 | that.rolling = emptyRollingQueue(); 137 | if (that.rolling) { 138 | process.nextTick(function() { that.initRolling(); }); 139 | } 140 | }); 141 | }; 142 | 143 | BaseRollingFileStream.prototype.write = function(data, encoding) { 144 | if (this.rolling) { 145 | this.writesWhileRolling.push({ data: data, encoding: encoding }); 146 | return false; 147 | } else { 148 | var canWrite = BaseRollingFileStream.super_.prototype.write.call(this, data, encoding); 149 | this.currentSize += data.length; 150 | debug('current size = ' + this.currentSize); 151 | if (this.shouldRoll()) { 152 | this.initRolling(); 153 | } 154 | return canWrite; 155 | } 156 | }; 157 | 158 | BaseRollingFileStream.prototype.shouldRoll = function() { 159 | return false; // default behaviour is never to roll 160 | }; 161 | 162 | BaseRollingFileStream.prototype.roll = function(filename, callback) { 163 | callback(); // default behaviour is not to do anything 164 | }; 165 | 166 | 167 | function RollingFileStream (filename, size, backups, options) { 168 | this.size = size; 169 | this.backups = backups || 1; 170 | 171 | function throwErrorIfArgumentsAreNotValid() { 172 | if (!filename || !size || size <= 0) { 173 | throw new Error("You must specify a filename and file size"); 174 | } 175 | } 176 | 177 | throwErrorIfArgumentsAreNotValid(); 178 | 179 | RollingFileStream.super_.call(this, filename, options); 180 | } 181 | util.inherits(RollingFileStream, BaseRollingFileStream); 182 | 183 | RollingFileStream.prototype.shouldRoll = function() { 184 | return this.currentSize >= this.size; 185 | }; 186 | 187 | RollingFileStream.prototype.roll = function(filename, callback) { 188 | var that = this, 189 | nameMatcher = new RegExp('^' + path.basename(filename)); 190 | 191 | function justTheseFiles (item) { 192 | return nameMatcher.test(item); 193 | } 194 | 195 | function index(filename_) { 196 | return parseInt(filename_.substring((path.basename(filename) + '.').length), 10) || 0; 197 | } 198 | 199 | function byIndex(a, b) { 200 | if (index(a) > index(b)) { 201 | return 1; 202 | } else if (index(a) < index(b) ) { 203 | return -1; 204 | } else { 205 | return 0; 206 | } 207 | } 208 | 209 | function increaseFileIndex (fileToRename, cb) { 210 | var idx = index(fileToRename); 211 | debug('Index of ' + fileToRename + ' is ' + idx); 212 | if (idx < that.backups) { 213 | //on windows, you can get a EEXIST error if you rename a file to an existing file 214 | //so, we'll try to delete the file we're renaming to first 215 | fs.unlink(filename + '.' + (idx+1), function (err) { 216 | //ignore err: if we could not delete, it's most likely that it doesn't exist 217 | debug('Renaming ' + fileToRename + ' -> ' + filename + '.' + (idx+1)); 218 | fs.rename(path.join(path.dirname(filename), fileToRename), filename + '.' + (idx + 1), cb); 219 | }); 220 | } else { 221 | cb(); 222 | } 223 | } 224 | 225 | function renameTheFiles(cb) { 226 | //roll the backups (rename file.n to file.n+1, where n <= numBackups) 227 | debug("Renaming the old files"); 228 | fs.readdir(path.dirname(filename), function (err, files) { 229 | async.forEachSeries( 230 | files.filter(justTheseFiles).sort(byIndex).reverse(), 231 | increaseFileIndex, 232 | cb 233 | ); 234 | }); 235 | } 236 | 237 | function openANewFile(cb) { 238 | debug("Opening a new file"); 239 | fs.open( 240 | filename, 241 | that.options.flags, 242 | that.options.mode, 243 | function (err, fd) { 244 | debug("opened new file"); 245 | var oldLogFileFD = that.fd; 246 | that.fd = fd; 247 | that.writable = true; 248 | fs.close(oldLogFileFD, cb); 249 | } 250 | ); 251 | } 252 | 253 | debug("Starting roll"); 254 | debug("Queueing up data until we've finished rolling"); 255 | debug("Flushing underlying stream"); 256 | this.flush(); 257 | 258 | async.series([ 259 | renameTheFiles, 260 | openANewFile 261 | ], callback); 262 | 263 | }; 264 | 265 | 266 | exports.BaseRollingFileStream = BaseRollingFileStream; 267 | exports.RollingFileStream = RollingFileStream; 268 | exports.BufferedWriteStream = BufferedWriteStream; 269 | -------------------------------------------------------------------------------- /node_modules/log4js/log-rolling.js: -------------------------------------------------------------------------------- 1 | var log4js = require('./lib/log4js') 2 | , log 3 | , i = 0; 4 | log4js.configure({ 5 | "appenders": [ 6 | { 7 | type: "console" 8 | , category: "console" 9 | }, 10 | { 11 | "type": "file", 12 | "filename": "tmp-test.log", 13 | "maxLogSize": 1024, 14 | "backups": 3, 15 | "category": "test" 16 | } 17 | ] 18 | }); 19 | log = log4js.getLogger("test"); 20 | 21 | function doTheLogging(x) { 22 | log.info("Logging something %d", x); 23 | } 24 | 25 | for ( ; i < 5000; i++) { 26 | doTheLogging(i); 27 | } -------------------------------------------------------------------------------- /node_modules/log4js/memory-test.js: -------------------------------------------------------------------------------- 1 | var log4js = require('./lib/log4js') 2 | , logger 3 | , usage 4 | , i; 5 | 6 | log4js.configure( 7 | { 8 | appenders: [ 9 | { 10 | category: "memory-test" 11 | , type: "file" 12 | , filename: "memory-test.log" 13 | }, 14 | { 15 | type: "console" 16 | , category: "memory-usage" 17 | }, 18 | { 19 | type: "file" 20 | , filename: "memory-usage.log" 21 | , category: "memory-usage" 22 | , layout: { 23 | type: "messagePassThrough" 24 | } 25 | } 26 | ] 27 | } 28 | ); 29 | logger = log4js.getLogger("memory-test"); 30 | usage = log4js.getLogger("memory-usage"); 31 | 32 | for (i=0; i < 1000000; i++) { 33 | if ( (i % 5000) === 0) { 34 | usage.info("%d %d", i, process.memoryUsage().rss); 35 | } 36 | logger.info("Doing something."); 37 | } 38 | -------------------------------------------------------------------------------- /node_modules/log4js/node_modules/async/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/nodeunit"] 2 | path = deps/nodeunit 3 | url = git://github.com/caolan/nodeunit.git 4 | [submodule "deps/UglifyJS"] 5 | path = deps/UglifyJS 6 | url = https://github.com/mishoo/UglifyJS.git 7 | [submodule "deps/nodelint"] 8 | path = deps/nodelint 9 | url = https://github.com/tav/nodelint.git 10 | -------------------------------------------------------------------------------- /node_modules/log4js/node_modules/async/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Caolan McMahon 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /node_modules/log4js/node_modules/async/Makefile: -------------------------------------------------------------------------------- 1 | PACKAGE = asyncjs 2 | NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node) 3 | 4 | BUILDDIR = dist 5 | 6 | all: build 7 | 8 | build: $(wildcard lib/*.js) 9 | mkdir -p $(BUILDDIR) 10 | uglifyjs lib/async.js > $(BUILDDIR)/async.min.js 11 | 12 | test: 13 | nodeunit test 14 | 15 | clean: 16 | rm -rf $(BUILDDIR) 17 | 18 | lint: 19 | nodelint --config nodelint.cfg lib/async.js 20 | 21 | .PHONY: test build all 22 | -------------------------------------------------------------------------------- /node_modules/log4js/node_modules/async/deps/nodeunit.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Styles taken from qunit.css 3 | */ 4 | 5 | h1#nodeunit-header, h1.nodeunit-header { 6 | padding: 15px; 7 | font-size: large; 8 | background-color: #06b; 9 | color: white; 10 | font-family: 'trebuchet ms', verdana, arial; 11 | margin: 0; 12 | } 13 | 14 | h1#nodeunit-header a { 15 | color: white; 16 | } 17 | 18 | h2#nodeunit-banner { 19 | height: 2em; 20 | border-bottom: 1px solid white; 21 | background-color: #eee; 22 | margin: 0; 23 | font-family: 'trebuchet ms', verdana, arial; 24 | } 25 | h2#nodeunit-banner.pass { 26 | background-color: green; 27 | } 28 | h2#nodeunit-banner.fail { 29 | background-color: red; 30 | } 31 | 32 | h2#nodeunit-userAgent, h2.nodeunit-userAgent { 33 | padding: 10px; 34 | background-color: #eee; 35 | color: black; 36 | margin: 0; 37 | font-size: small; 38 | font-weight: normal; 39 | font-family: 'trebuchet ms', verdana, arial; 40 | font-size: 10pt; 41 | } 42 | 43 | div#nodeunit-testrunner-toolbar { 44 | background: #eee; 45 | border-top: 1px solid black; 46 | padding: 10px; 47 | font-family: 'trebuchet ms', verdana, arial; 48 | margin: 0; 49 | font-size: 10pt; 50 | } 51 | 52 | ol#nodeunit-tests { 53 | font-family: 'trebuchet ms', verdana, arial; 54 | font-size: 10pt; 55 | } 56 | ol#nodeunit-tests li strong { 57 | cursor:pointer; 58 | } 59 | ol#nodeunit-tests .pass { 60 | color: green; 61 | } 62 | ol#nodeunit-tests .fail { 63 | color: red; 64 | } 65 | 66 | p#nodeunit-testresult { 67 | margin-left: 1em; 68 | font-size: 10pt; 69 | font-family: 'trebuchet ms', verdana, arial; 70 | } 71 | -------------------------------------------------------------------------------- /node_modules/log4js/node_modules/async/dist/async.min.js: -------------------------------------------------------------------------------- 1 | /*global setTimeout: false, console: false */(function(){var a={},b=this,c=b.async;typeof module!="undefined"&&module.exports?module.exports=a:b.async=a,a.noConflict=function(){return b.async=c,a};var d=function(a,b){if(a.forEach)return a.forEach(b);for(var c=0;c