// lineのNotifyType別に処理を分岐 func (f *Ftail) lineNotifyAction(ctx context.Context, line *tail.Line, w chan bool) error { var err error if line.NotifyType == tail.NewLineNotify { // 新しいライン if err = f.Write(line); err != nil { return err } if f.buf.Len() < f.MaxBufSize { return err } w <- true defer func() { <-w }() return f.Flush() } w <- true defer func() { <-w }() switch line.NotifyType { case tail.TickerNotify, tailex.GlobLoopNotify: // 定期flush処理 if err := f.Flush(); err != nil { return err } timeSlice := tailex.Truncate(line.Time, f.Period) if f.lastSlice.Sub(timeSlice) < 0 { // 新しいDBを開く if _, err = f.rec.CreateDB(timeSlice, f.Pos); err != nil { log.Printf("CreateDB err:%s", err) return err } f.lastSlice = timeSlice } // 古いDBを閉じる if _, cerr := f.rec.CloseOldDbs(line.Time); cerr != nil { log.Printf("CloseOldDbs err:%s", cerr) return cerr } case tail.NewFileNotify: f.lastTime = line.Time f.Pos.Name = line.Filename f.Pos.CreateAt = line.OpenTime f.Pos.Offset = line.Offset maxsize := line.Offset if f.MaxHeadHashSize < line.Offset { maxsize = f.MaxHeadHashSize } f.Pos.HeadHash, f.Pos.HashLength, err = f.getHeadHash(f.Pos.Name, maxsize) if err != nil { log.Printf("getHeadHash err:%s", err) return err } log.Printf("NewFileNotify getHeadHash :%s", f.Pos) } return nil }
// ポジション情報がない場合に実ファイルから取得 func (f *Ftail) position(c Config) (pos *core.Position, err error) { var fi os.FileInfo var filePath string if c.PathFmt != "" { // cronolog timeSlice := tailex.Truncate(c.Config.Time, c.RotatePeriod) searchPath := tailex.Time2Path(c.PathFmt, timeSlice) filePath, err = tailex.GlobSearch(searchPath) if err == tailex.ErrNoSuchFile { log.Printf("ftail position() GlobSearch(%s) err: %s", searchPath, err) return &core.Position{}, nil } else if err != nil { log.Printf("ftail position() GlobSearch(%s) err: %s", searchPath, err) return nil, err } } else { filePath, err = tailex.GlobSearch(c.Path) if err == tailex.ErrNoSuchFile { log.Printf("ftail position() GlobSearch(%s) err: %s", c.Path, err) return &core.Position{}, nil } else if err != nil { log.Printf("ftail position() GlobSearch(%s) err: %s", c.Path, err) return nil, err } } if fi, err = os.Stat(filePath); err != nil { log.Printf("Start os.Stat('%s') err: %s, ", filePath, err) return nil, err } offset := int64(0) // 現在のファイルサイズがf.MaxHeadHashSizeより大きいものだけオフセットを現在のサイズにする。 if (!c.Config.NoSeek) && (fi.Size() > f.MaxHeadHashSize) { offset = fi.Size() } pos = &core.Position{ Name: filePath, CreateAt: fi.ModTime(), Offset: offset, } return }
func Start(ctx context.Context, c Config, w chan bool) error { w <- true f := &Ftail{ Config: c, headHash: fnv.New64(), head: []byte{}, } //if f.MaxHeadHashSize == 0 { // f.MaxHeadHashSize = defaultMaxHeadHashSize //} var err error f.rec, err = core.NewRecorder(c.BufDir, c.Name, c.Period) if err != nil { log.Fatalln("NewRecorder err:", err) } defer f.rec.AllClose() f.Pos = f.rec.Position() if f.Pos == nil { if f.Pos, err = f.position(c); err != nil { log.Fatalln("position err:", err) } } f.Config.Config.Config = tailDefaultConfig f.ReOpenDelay = 5 * time.Second if f.Delay != 0 { f.ReOpenDelay = f.Delay } log.Printf("f.Pos: %s", f.Pos) if f.MaxHeadHashSize != 0 && f.Pos.Name != "" { oldhead := f.head hash, length, hherr := f.getHeadHash(f.Pos.Name, f.Pos.HashLength) if hherr != nil { log.Printf("getHeadHash err:%s", hherr) } else { if f.Pos.HeadHash == hash && f.Pos.HashLength == length { // ポジションファイルのハッシュ値と一致した場合はSeekInfoをセット log.Printf("match headHash: %s, head:%s", f.Pos, f.head) f.Location = &tail.SeekInfo{Offset: f.Pos.Offset} } else { log.Printf("not match headHash old: %s, head:%s", f.Pos, oldhead) f.Pos.HeadHash = hash f.Pos.HashLength = length log.Printf("not match headHash new: %s, head:%s", f.Pos, f.head) } } } else { posTimeSlise := tailex.Truncate(f.Pos.CreateAt, c.RotatePeriod) nowTimeSlise := tailex.Truncate(time.Now(), c.RotatePeriod) if nowTimeSlise.Equal(posTimeSlise) { // 読み込んだポジションのcreateAtが現在のtimesliseと同じ場合 f.Location = &tail.SeekInfo{Offset: f.Pos.Offset} } } t := tailex.NewTailEx(ctx, f.Config.Config, w) //var buf bytes.Buffer f.buf = bytes.Buffer{} /* f.Writer, err = zlib.NewWriterLevel(&f.buf, zlib.BestCompression) if err != nil { log.Fatalln("NewZlibWriter err:", err) } */ <-w f.Writer = NopCloser(&f.buf) defer func() { if err := f.Flush(); err != nil { log.Printf("f.Flush err:%s", err) } }() for { select { case <-ctx.Done(): // キャンセル処理 return ctx.Err() case line, ok := <-t.Lines: // 新しい入力行の取得 if !ok { return err } err := f.lineNotifyAction(ctx, line, w) if err != nil { return err } } } }