2022-11-09 16:07:28 +00:00
|
|
|
package lines
|
2022-11-06 20:02:29 +00:00
|
|
|
|
|
|
|
import (
|
2022-11-09 17:56:44 +00:00
|
|
|
"context"
|
2022-11-09 12:19:56 +00:00
|
|
|
"log"
|
2022-11-06 20:02:29 +00:00
|
|
|
"sync"
|
|
|
|
|
2022-11-09 18:24:57 +00:00
|
|
|
"git.k-space.ee/k-space/logmower-shipper/pkg/globals"
|
2022-11-09 16:07:28 +00:00
|
|
|
m "git.k-space.ee/k-space/logmower-shipper/pkg/mongo"
|
2022-11-06 20:02:29 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2022-11-09 16:07:28 +00:00
|
|
|
RawC <-chan Raw
|
|
|
|
Raw struct {
|
|
|
|
*File
|
2022-11-09 12:19:56 +00:00
|
|
|
Offset int64
|
|
|
|
B []byte
|
2022-11-06 20:02:29 +00:00
|
|
|
}
|
2022-11-09 16:07:28 +00:00
|
|
|
|
|
|
|
// file.File, but avoiding import cycle
|
|
|
|
File struct {
|
|
|
|
*m.File
|
|
|
|
MetricsName string // filepath.Base()
|
|
|
|
}
|
2022-11-06 20:02:29 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// assumes all lines are from same file
|
2022-11-09 18:24:57 +00:00
|
|
|
func (unparsed RawC) Process(ctx context.Context, parsed chan<- m.Record) {
|
2022-11-06 20:02:29 +00:00
|
|
|
lines := make(chan singleLine)
|
2022-11-09 12:19:56 +00:00
|
|
|
go unparsed.parse(lines)
|
2022-11-06 20:02:29 +00:00
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(2)
|
|
|
|
|
|
|
|
stdOut, stdErr := make(chan singleLine), make(chan singleLine)
|
|
|
|
go func() {
|
2022-11-09 18:24:57 +00:00
|
|
|
singleLines(stdOut).process(ctx, parsed)
|
2022-11-06 20:02:29 +00:00
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
go func() {
|
2022-11-09 18:24:57 +00:00
|
|
|
singleLines(stdErr).process(ctx, parsed)
|
2022-11-06 20:02:29 +00:00
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
|
2022-11-09 17:56:44 +00:00
|
|
|
defer func() {
|
|
|
|
close(stdOut)
|
|
|
|
close(stdErr)
|
|
|
|
wg.Wait()
|
|
|
|
close(parsed)
|
|
|
|
}()
|
|
|
|
|
2022-11-06 20:02:29 +00:00
|
|
|
// split stdout and stderr
|
|
|
|
for {
|
2022-11-09 17:56:44 +00:00
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
2022-11-06 20:02:29 +00:00
|
|
|
return
|
|
|
|
|
2022-11-09 17:56:44 +00:00
|
|
|
case line, ok := <-lines:
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if line.StdErr {
|
|
|
|
stdErr <- line
|
|
|
|
} else {
|
|
|
|
stdOut <- line
|
|
|
|
}
|
2022-11-06 20:02:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-09 18:24:57 +00:00
|
|
|
func (lines singleLines) process(ctx context.Context, parsed chan<- m.Record) {
|
2022-11-09 16:07:28 +00:00
|
|
|
var firstMetadata *m.ParsedMetadata
|
2022-11-06 20:02:29 +00:00
|
|
|
var buffer []byte
|
|
|
|
|
|
|
|
for {
|
|
|
|
line, ok := <-lines
|
|
|
|
if !ok {
|
2022-11-09 12:19:56 +00:00
|
|
|
// partial line should always be finished with full line
|
|
|
|
// discard any partial lines without end (full line)
|
2022-11-06 20:02:29 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(buffer) == 0 {
|
2022-11-09 12:19:56 +00:00
|
|
|
firstMetadata = &line.ParsedMetadata
|
2022-11-06 20:02:29 +00:00
|
|
|
}
|
|
|
|
|
2022-11-09 12:19:56 +00:00
|
|
|
buffer = append(buffer, line.B...)
|
2022-11-06 20:02:29 +00:00
|
|
|
|
2022-11-09 18:24:57 +00:00
|
|
|
if len(buffer) > globals.BufferLimitBytes {
|
2022-11-09 16:07:28 +00:00
|
|
|
promRecordDroppedTooLarge.WithLabelValues(line.MetricsName).Add(1)
|
2022-11-09 18:24:57 +00:00
|
|
|
log.Printf("dropped record: size in bytes exceeds limit of %d", globals.BufferLimitBytes)
|
2022-11-09 12:19:56 +00:00
|
|
|
|
2022-11-06 20:02:29 +00:00
|
|
|
buffer = nil
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-11-09 12:19:56 +00:00
|
|
|
if !line.partial {
|
2022-11-09 17:56:44 +00:00
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
|
|
|
|
case parsed <- m.Record{
|
2022-11-09 16:07:28 +00:00
|
|
|
File: line.File.File,
|
2022-11-09 12:19:56 +00:00
|
|
|
Offset: line.Offset,
|
|
|
|
|
|
|
|
String: string(buffer),
|
|
|
|
ParsedMetadata: *firstMetadata,
|
2022-11-09 17:56:44 +00:00
|
|
|
}:
|
2022-11-09 12:19:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
buffer = nil
|
2022-11-06 20:02:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|