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