feat: Implement initial Go backend and React frontend project structure for the gotail application.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user