| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 | /******************************************************************************* copyright: Copyright (c) 2004 Kris Bell. All rights reserved license: BSD style: $(LICENSE) version: Initial release: May 2004 author: Kris *******************************************************************************/ module tango.util.log.AppendFiles; private import tango.time.Time; private import Path = tango.io.Path, tango.io.device.File; private import tango.io.model.IFile; private import tango.util.log.Log, tango.util.log.AppendFile; /******************************************************************************* Append log messages to a file set *******************************************************************************/ public class AppendFiles : Filer { private Mask mask_; private const(char)[][] paths; private int index; private long maxSize, fileSize; /*********************************************************************** Create an AppendFiles upon a file-set with the specified path and optional layout. The minimal file count is two and the maximum is 1000 (explicitly 999). Note that files are numbered starting with zero rather than one. A path of "my.log" will be expanded to "my.0.log". maxSize is the advisory maximum size of a single log-file, in bytes. Where a file set already exists, we resume appending to the one with the most recent activity timestamp ***********************************************************************/ this (const(char)[] path, int count, long maxSize, Appender.Layout how = null) { --count; assert (path); assert (count > 0 && count < 1000); // Get a unique fingerprint for this instance mask_ = register (path); // split the path into components auto c = Path.parse (path); char[3] x; Time mostRecent; for (int i=0; i <= count; ++i) { x[0] = cast(char)('0' + i/100); x[1] = cast(char)('0' + i/10%10); x[2] = cast(char)('0' + i%10); auto p = c.toString()[0..$-c.suffix.length] ~ x ~ c.suffix; paths ~= p; // use the most recent file in the set if (Path.exists(p)) { auto modified = Path.modified(p); if (modified > mostRecent) { mostRecent = modified; index = i; } } } // remember the maximum size this.maxSize = maxSize; // adjust index and open the appropriate log file --index; nextFile (false); // set provided layout (ignored when null) layout (how); } /*********************************************************************** Return the fingerprint for this class ***********************************************************************/ @property override final const Mask mask () { return mask_; } /*********************************************************************** Return the name of this class ***********************************************************************/ @property override final const const(char)[] name () { return this.classinfo.name; } /*********************************************************************** Append an event to the output ***********************************************************************/ override final void append (LogEvent event) { synchronized(this) { char[] msg; // file already full? if (fileSize >= maxSize) nextFile (true); size_t write (const(void)[] content) { fileSize += content.length; return buffer.write (content); } // write log message and flush it layout.format (event, &write); write (FileConst.NewlineString); buffer.flush(); } } /*********************************************************************** Switch to the next file within the set ***********************************************************************/ private void nextFile (bool reset) { // select next file in the set if (++index >= paths.length) index = 0; // close any existing conduit close(); // make it shareable for read auto style = File.WriteAppending; style.share = File.Share.Read; auto conduit = new File (paths[index], style); configure (conduit); // reset file size if (reset) conduit.truncate (fileSize = 0); else fileSize = conduit.length; } } /******************************************************************************* *******************************************************************************/ debug (AppendFiles) { void main() { Log.root.add (new AppendFiles ("foo", 5, 6)); auto log = Log.lookup ("fu.bar"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); log.trace ("hello {}", "world"); } } |