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");
}
}
|