|
|
|
@ -7,6 +7,7 @@ import ( |
|
|
|
|
"io" |
|
|
|
|
"os" |
|
|
|
|
"path/filepath" |
|
|
|
|
"strings" |
|
|
|
|
"sync" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
@ -83,8 +84,9 @@ func (s *submitter) shipFile(ctx context.Context, name string, deleteAfterRead b |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *submitter) shipFileRoutine(ctx context.Context, name string, deleteOk func() bool, sendQueue chan<- mLog, sigCatchupped chan<- struct{}) error { |
|
|
|
|
// TODO: better way for respecting ?killing sender for retry
|
|
|
|
|
baseName := filepath.Base(name) |
|
|
|
|
|
|
|
|
|
// TODO: better way for respecting ?killing sender for retry
|
|
|
|
|
for { |
|
|
|
|
if len(sendQueue) == 0 { |
|
|
|
|
break |
|
|
|
@ -92,34 +94,31 @@ func (s *submitter) shipFileRoutine(ctx context.Context, name string, deleteOk f |
|
|
|
|
time.Sleep(time.Second) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Initialize in case of new file
|
|
|
|
|
log := mLog{ |
|
|
|
|
HostInfo: s.hostInfo, |
|
|
|
|
Filename: name, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// get files with offset
|
|
|
|
|
offsetResult, err := mongoWithErr(s.db.FindOne(mongoTimeoutCtx(ctx), |
|
|
|
|
bson.D{{Key: "hostinfo.id", Value: s.hostInfo.id}, {Key: "filename", Value: name}}, |
|
|
|
|
bson.D{{Key: "hostinfo.id", Value: s.hostInfo.id}, {Key: "file", Value: baseName}}, |
|
|
|
|
&mongoOpt.FindOneOptions{Sort: bson.D{{Key: "offset", Value: -1}}}, // sort descending (get largest)
|
|
|
|
|
)) |
|
|
|
|
|
|
|
|
|
if err != nil && !errors.Is(err, mongo.ErrNoDocuments) { |
|
|
|
|
return fmt.Errorf("retrieving mongo offset: %w", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// offsetResult.DecodeBytes() //TODO: check for extra fields
|
|
|
|
|
|
|
|
|
|
var log mLog |
|
|
|
|
if err := offsetResult.Decode(&log); err != nil && !errors.Is(err, mongo.ErrNoDocuments) { |
|
|
|
|
return fmt.Errorf("decoding mongo offset: %w", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fi, err := os.Stat(log.Filename) |
|
|
|
|
fi, err := os.Stat(name) |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("getting original file size") |
|
|
|
|
} |
|
|
|
|
startSize := fi.Size() |
|
|
|
|
|
|
|
|
|
// TODO: use inotify for file, and end with file deletion or replacement
|
|
|
|
|
lineChan, errChan, err := util.TailFile(ctx, log.Filename, log.Offset, io.SeekStart) |
|
|
|
|
lineChan, errChan, err := util.TailFile(ctx, name, log.Offset, io.SeekStart) |
|
|
|
|
if err != nil { |
|
|
|
|
return fmt.Errorf("tailing file: %w", err) |
|
|
|
|
} |
|
|
|
@ -137,22 +136,47 @@ func (s *submitter) shipFileRoutine(ctx context.Context, name string, deleteOk f |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var collectTime time.Time |
|
|
|
|
var stdErr, format, log string |
|
|
|
|
|
|
|
|
|
split := strings.SplitN(line.String, " ", 4) |
|
|
|
|
if len(split) != 4 { |
|
|
|
|
log = line.String |
|
|
|
|
promLineParsingErr.WithLabelValues(baseName).Add(1) |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
stdErr, format, log = split[1], split[2], split[3] |
|
|
|
|
|
|
|
|
|
collectTime, err = time.Parse(time.RFC3339Nano, split[0]) |
|
|
|
|
if err != nil { |
|
|
|
|
promLineParsingErr.WithLabelValues(baseName).Add(1) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
select { |
|
|
|
|
case sendQueue <- mLog{ |
|
|
|
|
HostInfo: s.hostInfo, |
|
|
|
|
Filename: *line.Filename, |
|
|
|
|
File: baseName, |
|
|
|
|
|
|
|
|
|
Offset: line.EndOffset, |
|
|
|
|
Content: line.String, |
|
|
|
|
ShipTime: time.Now(), |
|
|
|
|
|
|
|
|
|
CollectTime: collectTime, |
|
|
|
|
StdErr: stdErr == "stderr", // or stdout
|
|
|
|
|
Format: format, |
|
|
|
|
Content: log, |
|
|
|
|
}: |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
promShipperDropped.WithLabelValues(*line.Filename).Add(1) |
|
|
|
|
promShipperDropped.WithLabelValues(baseName).Add(1) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// no new lines
|
|
|
|
|
default: |
|
|
|
|
if deleteOk() { |
|
|
|
|
return os.Remove(name) |
|
|
|
|
} |
|
|
|
|
// TODO: ensure we don't instantly jump here
|
|
|
|
|
// default:
|
|
|
|
|
// if deleteOk() {
|
|
|
|
|
// return os.Remove(name)
|
|
|
|
|
// }
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|