Tizen 2.0 Release
[platform/framework/web/web-ui-fw.git] / libs / js / jquery-mobile-1.2.0 / node_modules / grunt / node_modules / prompt / node_modules / winston / lib / winston / transports / file.js
1 /*
2  * file.js: Transport for outputting to a local log file
3  *
4  * (C) 2010 Charlie Robbins
5  * MIT LICENCE
6  *
7  */
8
9 var events = require('events'),
10     fs = require('fs'),
11     path = require('path'),
12     util = require('util'),
13     colors = require('colors'),
14     common = require('../common'),
15     Transport = require('./transport').Transport;
16     
17 //
18 // ### function File (options)
19 // #### @options {Object} Options for this instance.
20 // Constructor function for the File transport object responsible
21 // for persisting log messages and metadata to one or more files.
22 //
23 var File = exports.File = function (options) {
24   Transport.call(this, options);
25   
26   //
27   // Helper function which throws an `Error` in the event
28   // that any of the rest of the arguments is present in `options`. 
29   //
30   function throwIf (target /*, illegal... */) {
31     Array.prototype.slice.call(arguments, 1).forEach(function (name) {
32       if (options[name]) {
33         throw new Error('Cannot set ' + name + ' and ' + target + 'together');
34       }
35     });
36   }
37   
38   if (options.filename || options.dirname) {
39     throwIf('filename or dirname', 'stream');
40     this._basename = this.filename = path.basename(options.filename) || 'winston.log';
41     this.dirname   = options.dirname || path.dirname(options.filename);
42     this.options   = options.options || { flags: 'a' };    
43   }
44   else if (options.stream) {
45     throwIf('stream', 'filename', 'maxsize');
46     this.stream = options.stream;
47   }
48   else {
49     throw new Error('Cannot log to file without filename or stream.');
50   }
51     
52   this.json      = options.json !== false;
53   this.colorize  = options.colorize  || false;
54   this.maxsize   = options.maxsize   || null;
55   this.maxFiles  = options.maxFiles  || null;
56   this.timestamp = typeof options.timestamp !== 'undefined' ? options.timestamp : false;
57
58   //
59   // Internal state variables representing the number
60   // of files this instance has created and the current
61   // size (in bytes) of the current logfile.
62   //
63   this._size     = 0;
64   this._created  = 0;
65   this._buffer   = [];
66   this._draining = false;
67 };
68
69 //
70 // Inherit from `winston.Transport`.
71 //
72 util.inherits(File, Transport);
73
74 //
75 // Expose the name of this Transport on the prototype
76 //
77 File.prototype.name = 'file';
78
79 //
80 // ### function log (level, msg, [meta], callback)
81 // #### @level {string} Level at which to log the message.
82 // #### @msg {string} Message to log
83 // #### @meta {Object} **Optional** Additional metadata to attach
84 // #### @callback {function} Continuation to respond to when complete.
85 // Core logging method exposed to Winston. Metadata is optional.
86 //
87 File.prototype.log = function (level, msg, meta, callback) {
88   if (this.silent) {
89     return callback(null, true);
90   }
91
92   var self = this, output = common.log({
93     level:     level,
94     message:   msg,
95     meta:      meta,
96     json:      this.json,
97     colorize:  this.colorize,
98     timestamp: this.timestamp
99   }) + '\n';
100   
101   this._size += output.length;
102   
103   if (!this.filename) {
104     //
105     // If there is no `filename` on this instance then it was configured
106     // with a raw `WriteableStream` instance and we should not perform any
107     // size restrictions.
108     //
109     this.stream.write(output);
110     self._lazyDrain();
111   }
112   else {
113     this.open(function (err) {
114       if (err) {
115         //
116         // If there was an error enqueue the message
117         //
118         return self._buffer.push(output);
119       }
120       
121       self.stream.write(output);
122       self._lazyDrain();
123     });
124   }
125
126   callback(null, true);
127 };
128
129 //
130 // ### function open (callback)
131 // #### @callback {function} Continuation to respond to when complete
132 // Checks to see if a new file needs to be created based on the `maxsize`
133 // (if any) and the current size of the file used.
134 //
135 File.prototype.open = function (callback) {
136   if (this.opening) {
137     //
138     // If we are already attempting to open the next
139     // available file then respond with a value indicating
140     // that the message should be buffered.
141     //
142     return callback(true);
143   }
144   else if (!this.stream || (this.maxsize && this._size >= this.maxsize)) {
145     //
146     // If we dont have a stream or have exceeded our size, then create
147     // the next stream and respond with a value indicating that 
148     // the message should be buffered.
149     //
150     callback(true);
151     return this._createStream();
152   }
153   
154   //
155   // Otherwise we have a valid (and ready) stream.
156   //
157   callback();
158 };
159
160 //
161 // ### function close ()
162 // Closes the stream associated with this instance.
163 //
164 File.prototype.close = function () {
165   var self = this;
166
167   if (this.stream) {
168     this.stream.end();
169     this.stream.destroySoon();
170     
171     this.stream.once('drain', function () {
172       self.emit('flush');
173       self.emit('closed');
174     });
175   }
176 };
177
178 //
179 // ### function flush ()
180 // Flushes any buffered messages to the current `stream`
181 // used by this instance.
182 //
183 File.prototype.flush = function () {
184   var self = this;
185
186   //
187   // Iterate over the `_buffer` of enqueued messaged
188   // and then write them to the newly created stream. 
189   //
190   this._buffer.forEach(function (str) {
191     process.nextTick(function () {
192       self.stream.write(str);
193       self._size += str.length;
194     });
195   });
196   
197   //
198   // Quickly truncate the `_buffer` once the write operations
199   // have been started
200   //
201   self._buffer.length = 0;
202   
203   //
204   // When the stream has drained we have flushed
205   // our buffer.
206   //
207   self.stream.once('drain', function () {
208     self.emit('flush');
209     self.emit('logged');
210   });
211 };
212
213 //
214 // ### @private function _createStream ()
215 // Attempts to open the next appropriate file for this instance
216 // based on the common state (such as `maxsize` and `_basename`).
217 //
218 File.prototype._createStream = function () {
219   var self = this;
220   this.opening = true;
221     
222   (function checkFile (target) {
223     var fullname = path.join(self.dirname, target);
224     
225     //
226     // Creates the `WriteStream` and then flushes any
227     // buffered messages.
228     //
229     function createAndFlush (size) {
230       if (self.stream) {
231         self.stream.end();
232         self.stream.destroySoon();
233       }
234       
235       self._size = size;
236       self.filename = target;
237       self.stream = fs.createWriteStream(fullname, self.options);
238       
239       //
240       // When the current stream has finished flushing 
241       // then we can be sure we have finished opening 
242       // and thus can emit the `open` event.
243       //
244       self.once('flush', function () {
245         self.opening = false;
246         self.emit('open', fullname);
247       });
248
249       //
250       // Remark: It is possible that in the time it has taken to find the
251       // next logfile to be written more data than `maxsize` has been buffered,
252       // but for sensible limits (10s - 100s of MB) this seems unlikely in less
253       // than one second.
254       //
255       self.flush();
256     }
257
258     fs.stat(fullname, function (err, stats) {
259       if (err) {
260         if (err.code !== 'ENOENT') {
261           return self.emit('error', err);
262         }
263         
264         return createAndFlush(0);
265       }
266       
267       if (!stats || (self.maxsize && stats.size >= self.maxsize)) {
268         //
269         // If `stats.size` is greater than the `maxsize` for 
270         // this instance then try again 
271         //
272         return checkFile(self._getFile(true));
273       }
274       
275       createAndFlush(stats.size);
276     });
277   })(this._getFile());  
278 };
279
280 //
281 // ### @private function _getFile ()
282 // Gets the next filename to use for this instance
283 // in the case that log filesizes are being capped.
284 //
285 File.prototype._getFile = function (inc) {
286   var self = this,
287       ext = path.extname(this._basename),
288       basename = path.basename(this._basename, ext),
289       remaining;
290   
291   if (inc) {
292     //
293     // Increment the number of files created or 
294     // checked by this instance.
295     //
296     // Check for maxFiles option and delete file
297     if (this.maxFiles && (this._created >= (this.maxFiles - 1))) {
298       remaining = this._created - (this.maxFiles - 1);
299       if (remaining === 0) {
300         fs.unlinkSync(path.join(this.dirname, basename + ext));
301       }
302       else {
303         fs.unlinkSync(path.join(this.dirname, basename + remaining + ext));
304       }
305     }
306     
307     this._created += 1;
308   }
309   
310   return this._created 
311     ? basename + this._created + ext
312     : basename + ext;
313 };
314
315 //
316 // ### @private function _lazyDrain ()
317 // Lazily attempts to emit the `logged` event when `this.stream` has 
318 // drained. This is really just a simple mutex that only works because
319 // Node.js is single-threaded.
320 //
321 File.prototype._lazyDrain = function () {
322   var self = this;
323   
324   if (!this._draining && this.stream) {
325     this._draining = true;
326     
327     this.stream.once('drain', function () {
328       this._draining = false;
329       self.emit('logged');
330     });
331   } 
332 };