123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
|
/*******************************************************************************
copyright: Copyright (c) 2004 Kris Bell. All rights reserved
license: BSD style: $(LICENSE)
version: Initial release: May 2004
author: Kris & Marenz
*******************************************************************************/
module tango.util.log.AppendSyslog;
private import tango.time.Time;
private import Path = tango.io.Path, tango.io.device.File, tango.io.FilePath;
private import tango.io.model.IFile;
private import tango.util.log.Log, tango.util.log.AppendFile;
private import Integer = tango.text.convert.Integer;
private import tango.text.convert.Format;
private import tango.util.MinMax;
private import tango.sys.Process;
/*******************************************************************************
Append log messages to a file set
*******************************************************************************/
public class AppendSyslog: Filer
{
private Mask mask_;
private long max_size, file_size, max_files, compress_index;
private FilePath file_path;
private char[] path;
private char[] compress_suffix;
private Process compress_cmd;
/***********************************************************************
Create an AppendSyslog upon a file-set with the specified
path and optional layout. The minimal file count is two
and the maximum is 1000 (explicitly 999).
The minimal compress_begin index is 2.
Params:
path = path to the first logfile
count = maximum number of logfiles
max_size = maximum size of a logfile in bytes
compress_cmd = command to use to compress logfiles
compress_suffix = suffix for compressed logfiles
compress_begin = index after which logfiles should be compressed
how = which layout to use
***********************************************************************/
this ( char[] path, uint count, long max_size,
char[] compress_cmd = null, char[] compress_suffix = null,
size_t compress_begin = 2, Appender.Layout how = null )
{
assert (path);
assert (count < 1000);
assert (compress_begin >= 2);
// Get a unique fingerprint for this instance
mask_ = register(path);
auto style = File.WriteAppending;
style.share = File.Share.Read;
auto conduit = new File(path, style);
configure(conduit);
// remember the maximum size
this.max_size = max_size;
// and the current size
this.file_size = conduit.length;
this.max_files = count;
// set provided layout (ignored when null)
layout(how);
this.file_path = new FilePath(path);
this.file_path.pop();
this.path = path.dup;
// "gzip {}" this.path.{}
char[512] buf, buf1;
auto compr_path = Format.sprint(buf, "{}.{}", this.path, compress_begin);
auto cmd = Format.sprint(buf1, compress_cmd, compr_path);
this.compress_cmd = new Process(cmd.dup);
this.compress_suffix = "." ~ compress_suffix;
this.compress_index = compress_begin;
}
/***********************************************************************
Return the fingerprint for this class
***********************************************************************/
@property override final Mask mask ( ) const
{
return mask_;
}
/***********************************************************************
Return the name of this class
***********************************************************************/
@property override final const(char)[] name ( ) const
{
return this.classinfo.name;
}
/***********************************************************************
Append an event to the output
***********************************************************************/
override final void append ( LogEvent event )
{
synchronized(this)
{
char[] msg;
// file already full?
if (file_size >= max_size) nextFile();
size_t write ( const(void)[] content )
{
file_size += content.length;
return buffer.write(content);
}
// write log message and flush it
layout.format(event, &write);
write(tango.io.model.IFile.FileConst.NewlineString);
buffer.flush();
}
}
private void openConduit ()
{
this.file_size = 0;
// make it shareable for read
auto style = File.WriteAppending;
style.share = File.Share.Read;
(cast(File) this.conduit).open(this.path, style);
//this.buffer.output(this.conduit);
}
/***********************************************************************
Switch to the next file within the set
***********************************************************************/
private void nextFile ( )
{
size_t free, used;
long oldest = 1;
char[512] buf;
buf[0 .. this.path.length] = this.path;
buf[this.path.length] = '.';
// release currently opened file
this.conduit.detach();
foreach ( ref file; this.file_path )
{
auto pathlen = file.path.length;
if ( file.name.length > this.path.length + 1 - pathlen &&
file.name[0 .. this.path.length - pathlen] == this.path[pathlen .. $] )
{
size_t ate = 0;
auto num = Integer.parse(file.name[this.path.length - pathlen + 1 .. $], 0, &ate);
if ( ate != 0 )
{
oldest = max!(long)(oldest, num);
}
}
}
for ( long i = oldest; i > 0; --i )
{
const(char)[] compress = i >= this.compress_index ?
this.compress_suffix : "";
auto path = Format.sprint(buf, "{}.{}{}", this.path, i,
compress);
this.file_path.set(path, true);
if ( this.file_path.exists() )
{
if ( i + 1 < this.max_files)
{
path = Format.sprint(buf, "{}.{}{}\0", this.path, i+1,
compress);
this.file_path.rename(path);
if ( i + 1 == this.compress_index ) with (this.compress_cmd)
{
if ( isRunning() )
{
wait();
close();
}
execute();
}
}
else this.file_path.remove();
}
}
this.file_path.set(this.path);
if ( this.file_path.exists() )
{
auto path = Format.sprint(buf, "{}.{}\0", this.path, 1);
this.file_path.rename(path);
}
this.openConduit ();
this.file_path.set(this.path);
this.file_path.pop();
}
}
/*******************************************************************************
*******************************************************************************/
debug (AppendSyslog)
{
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");
}
}
|