Files

116 lines
2.5 KiB
Go

package tail
import (
"context"
"os"
"sync"
"github.com/nxadm/tail"
"github.com/rs/zerolog/log"
"github.com/seu-usuario/go-react-web-tail/internal/models"
)
// Tailer gerencia o monitoramento de múltiplos arquivos de log
type Tailer struct {
files []string
tails []*tail.Tail
mu sync.Mutex
ctx context.Context
cancel context.CancelFunc
onEntry func(models.LogEntry)
}
// New cria uma nova instância do Tailer
func New(ctx context.Context, files []string, onEntry func(models.LogEntry)) *Tailer {
ctx, cancel := context.WithCancel(ctx)
return &Tailer{
files: files,
tails: make([]*tail.Tail, 0, len(files)),
ctx: ctx,
cancel: cancel,
onEntry: onEntry,
}
}
// Start inicia o monitoramento de todos os arquivos
func (t *Tailer) Start() error {
log.Info().Int("file_count", len(t.files)).Msg("Starting tailer")
for _, filename := range t.files {
if err := t.startTailing(filename); err != nil {
log.Error().Err(err).Str("file", filename).Msg("Failed to start tailing file")
continue
}
}
return nil
}
// startTailing inicia o monitoramento de um arquivo específico
func (t *Tailer) startTailing(filename string) error {
config := tail.Config{
Follow: true,
ReOpen: true,
Location: &tail.SeekInfo{Offset: 0, Whence: os.SEEK_END},
Logger: tail.DiscardingLogger,
}
tailFile, err := tail.TailFile(filename, config)
if err != nil {
return err
}
t.mu.Lock()
t.tails = append(t.tails, tailFile)
t.mu.Unlock()
log.Info().Str("file", filename).Msg("Started tailing file")
go t.processTail(tailFile, filename)
return nil
}
// processTail processa as linhas de um arquivo
func (t *Tailer) processTail(tailFile *tail.Tail, filename string) {
for {
select {
case <-t.ctx.Done():
log.Debug().Str("file", filename).Msg("Stopping tail processing")
return
case line, ok := <-tailFile.Lines:
if !ok {
log.Warn().Str("file", filename).Msg("Tail channel closed")
return
}
if line.Err != nil {
log.Error().Err(line.Err).Str("file", filename).Msg("Error reading line")
continue
}
entry := models.NewLogEntry(filename, line.Text)
t.onEntry(entry)
}
}
}
// Shutdown para o monitoramento de todos os arquivos
func (t *Tailer) Shutdown() error {
log.Info().Msg("Shutting down tailer")
t.cancel()
t.mu.Lock()
defer t.mu.Unlock()
for _, tailFile := range t.tails {
if err := tailFile.Stop(); err != nil {
log.Error().Err(err).Msg("Error stopping tail")
}
}
log.Info().Msg("All tail operations stopped")
return nil
}