@ -8,11 +8,6 @@ import (
"reflect"
"reflect"
"sync"
"sync"
"io/ioutil"
"path/filepath"
"regexp"
"strings"
"github.com/go-stack/stack"
"github.com/go-stack/stack"
)
)
@ -75,111 +70,6 @@ func FileHandler(path string, fmtr Format) (Handler, error) {
return closingHandler { f , StreamHandler ( f , fmtr ) } , nil
return closingHandler { f , StreamHandler ( f , fmtr ) } , nil
}
}
// countingWriter wraps a WriteCloser object in order to count the written bytes.
type countingWriter struct {
w io . WriteCloser // the wrapped object
count uint // number of bytes written
}
// Write increments the byte counter by the number of bytes written.
// Implements the WriteCloser interface.
func ( w * countingWriter ) Write ( p [ ] byte ) ( n int , err error ) {
n , err = w . w . Write ( p )
w . count += uint ( n )
return n , err
}
// Close implements the WriteCloser interface.
func ( w * countingWriter ) Close ( ) error {
return w . w . Close ( )
}
// prepFile opens the log file at the given path, and cuts off the invalid part
// from the end, because the previous execution could have been finished by interruption.
// Assumes that every line ended by '\n' contains a valid log record.
func prepFile ( path string ) ( * countingWriter , error ) {
f , err := os . OpenFile ( path , os . O_RDWR | os . O_APPEND , 0600 )
if err != nil {
return nil , err
}
_ , err = f . Seek ( - 1 , io . SeekEnd )
if err != nil {
return nil , err
}
buf := make ( [ ] byte , 1 )
var cut int64
for {
if _ , err := f . Read ( buf ) ; err != nil {
return nil , err
}
if buf [ 0 ] == '\n' {
break
}
if _ , err = f . Seek ( - 2 , io . SeekCurrent ) ; err != nil {
return nil , err
}
cut ++
}
fi , err := f . Stat ( )
if err != nil {
return nil , err
}
ns := fi . Size ( ) - cut
if err = f . Truncate ( ns ) ; err != nil {
return nil , err
}
return & countingWriter { w : f , count : uint ( ns ) } , nil
}
// RotatingFileHandler returns a handler which writes log records to file chunks
// at the given path. When a file's size reaches the limit, the handler creates
// a new file named after the timestamp of the first log record it will contain.
func RotatingFileHandler ( path string , limit uint , formatter Format ) ( Handler , error ) {
if err := os . MkdirAll ( path , 0700 ) ; err != nil {
return nil , err
}
files , err := ioutil . ReadDir ( path )
if err != nil {
return nil , err
}
re := regexp . MustCompile ( ` \.log$ ` )
last := len ( files ) - 1
for last >= 0 && ( ! files [ last ] . Mode ( ) . IsRegular ( ) || ! re . MatchString ( files [ last ] . Name ( ) ) ) {
last --
}
var counter * countingWriter
if last >= 0 && files [ last ] . Size ( ) < int64 ( limit ) {
// Open the last file, and continue to write into it until it's size reaches the limit.
if counter , err = prepFile ( filepath . Join ( path , files [ last ] . Name ( ) ) ) ; err != nil {
return nil , err
}
}
if counter == nil {
counter = new ( countingWriter )
}
h := StreamHandler ( counter , formatter )
return FuncHandler ( func ( r * Record ) error {
if counter . count > limit {
counter . Close ( )
counter . w = nil
}
if counter . w == nil {
f , err := os . OpenFile (
filepath . Join ( path , fmt . Sprintf ( "%s.log" , strings . Replace ( r . Time . Format ( "060102150405.00" ) , "." , "" , 1 ) ) ) ,
os . O_CREATE | os . O_APPEND | os . O_WRONLY ,
0600 ,
)
if err != nil {
return err
}
counter . w = f
counter . count = 0
}
return h . Log ( r )
} ) , nil
}
// NetHandler opens a socket to the given address and writes records
// NetHandler opens a socket to the given address and writes records
// over the connection.
// over the connection.
func NetHandler ( network , addr string , fmtr Format ) ( Handler , error ) {
func NetHandler ( network , addr string , fmtr Format ) ( Handler , error ) {