diff --git a/tail.go b/tail.go index ec0a255..a90f117 100644 --- a/tail.go +++ b/tail.go @@ -5,6 +5,7 @@ package tail import ( "bufio" "fmt" + "github.com/ActiveState/tail/util" "github.com/ActiveState/tail/watch" "io" "launchpad.net/tomb" @@ -63,7 +64,7 @@ type Tail struct { // `Lines` channel. func TailFile(filename string, config Config) (*Tail, error) { if config.ReOpen && !config.Follow { - panic("cannot set ReOpen without Follow.") + util.Fatal("cannot set ReOpen without Follow.") } t := &Tail{ @@ -286,7 +287,7 @@ func (tail *Tail) sendLine(line []byte) bool { // Split longer lines if tail.MaxLineSize > 0 && len(line) > tail.MaxLineSize { - lines = partitionString( + lines = util.PartitionString( string(line), tail.MaxLineSize) } diff --git a/util.go b/util/util.go similarity index 50% rename from util.go rename to util/util.go index fe55d94..f58b3ee 100644 --- a/util.go +++ b/util/util.go @@ -1,10 +1,30 @@ // Copyright (c) 2013 ActiveState Software Inc. All rights reserved. -package tail +package util + +import ( + "fmt" + "log" + "os" + "runtime/debug" +) + +type Logger struct { + *log.Logger +} + +var LOGGER = &Logger{log.New(os.Stderr, "", log.LstdFlags)} + +// fatal is like panic except it displays only the current goroutine's stack. +func Fatal(format string, v ...interface{}) { + // https://github.com/ActiveState/log/blob/master/log.go#L45 + LOGGER.Output(2, fmt.Sprintf("FATAL -- "+format, v...)+"\n"+string(debug.Stack())) + os.Exit(1) +} // partitionString partitions the string into chunks of given size, // with the last chunk of variable size. -func partitionString(s string, chunkSize int) []string { +func PartitionString(s string, chunkSize int) []string { if chunkSize <= 0 { panic("invalid chunkSize") } diff --git a/watch/inotify.go b/watch/inotify.go index 5d231fe..8acb07e 100644 --- a/watch/inotify.go +++ b/watch/inotify.go @@ -3,6 +3,7 @@ package watch import ( + "github.com/ActiveState/tail/util" "github.com/howeyc/fsnotify" "launchpad.net/tomb" "os" @@ -61,11 +62,11 @@ func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, fi os.FileInfo) *FileCh w, err := fsnotify.NewWatcher() if err != nil { - panic(err) + util.Fatal("Error creating fsnotify watcher: %v", err) } err = w.Watch(fw.Filename) if err != nil { - panic(err) + util.Fatal("Error watching %v: %v", fw.Filename, err) } fw.Size = fi.Size() @@ -101,8 +102,8 @@ func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, fi os.FileInfo) *FileCh changes.NotifyDeleted() return } - // XXX: no panic here - panic(err) + // XXX: report this error back to the user + util.Fatal("Failed to stat file %v: %v", fw.Filename, err) } fw.Size = fi.Size() diff --git a/watch/polling.go b/watch/polling.go index bd048ae..b32bf9e 100644 --- a/watch/polling.go +++ b/watch/polling.go @@ -3,6 +3,7 @@ package watch import ( + "github.com/ActiveState/tail/util" "launchpad.net/tomb" "os" "time" @@ -43,7 +44,7 @@ func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, origFi os.FileInfo) *Fi var prevModTime time.Time // XXX: use tomb.Tomb to cleanly manage these goroutines. replace - // the panic (below) with tomb's Kill. + // the fatal (below) with tomb's Kill. fw.Size = origFi.Size() @@ -66,8 +67,8 @@ func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, origFi os.FileInfo) *Fi changes.NotifyDeleted() return } - /// XXX: do not panic here. - panic(err) + // XXX: report this error back to the user + util.Fatal("Failed to stat file %v: %v", fw.Filename, err) } // File got moved/renamed?