@@ -20,56 +20,47 @@ import (
2020 "bytes"
2121 "container/heap"
2222 "context"
23- "fmt"
2423 "io"
2524 "os"
2625 "path/filepath"
2726 "regexp"
2827 "sort"
2928 "sync"
30- "text/template"
3129 "time"
3230
3331 "github.com/cockroachdb/cockroach/pkg/util/log"
34- "github.com/cockroachdb/cockroach/pkg/util/timeutil"
3532 "golang.org/x/sync/errgroup"
3633)
3734
3835type logStream interface {
39- fileInfo () * log. FileInfo // FileInfo for the current entry available in peek.
36+ fileInfo () * fileInfo // FileInfo for the current entry available in peek.
4037 peek () (log.Entry , bool )
4138 pop () (log.Entry , bool ) // If called after peek, must return the same values.
4239 error () error
4340}
4441
4542// writeLogStream pops messages off of s and writes them to out prepending
4643// prefix per message and filtering messages which match filter.
47- func writeLogStream (
48- s logStream , out io.Writer , filter * regexp.Regexp , prefix * template.Template ,
49- ) error {
44+ func writeLogStream (s logStream , out io.Writer , filter * regexp.Regexp , prefix string ) error {
5045 const chanSize = 1 << 16 // 64k
5146 const maxWriteBufSize = 1 << 18 // 256kB
5247
53- prefixCache := map [* log. FileInfo ][]byte {}
54- getPrefix := func (fi * log. FileInfo ) ([]byte , error ) {
48+ prefixCache := map [* fileInfo ][]byte {}
49+ getPrefix := func (fi * fileInfo ) ([]byte , error ) {
5550 if prefixBuf , ok := prefixCache [fi ]; ok {
5651 return prefixBuf , nil
5752 }
58- var prefixBuf bytes.Buffer
59- if err := prefix .Execute (& prefixBuf , fi ); err != nil {
60- return nil , err
61- }
62- prefixCache [fi ] = prefixBuf .Bytes ()
53+ prefixCache [fi ] = fi .pattern .ExpandString (nil , prefix , fi .path , fi .matches )
6354 return prefixCache [fi ], nil
6455 }
6556
6657 type entryInfo struct {
6758 log.Entry
68- * log. FileInfo
59+ * fileInfo
6960 }
7061 render := func (ei entryInfo , w io.Writer ) (err error ) {
7162 var prefixBytes []byte
72- if prefixBytes , err = getPrefix (ei .FileInfo ); err != nil {
63+ if prefixBytes , err = getPrefix (ei .fileInfo ); err != nil {
7364 return err
7465 }
7566 if _ , err = w .Write (prefixBytes ); err != nil {
@@ -85,7 +76,7 @@ func writeLogStream(
8576 defer close (entryChan )
8677 for e , ok := s .peek (); ok ; e , ok = s .peek () {
8778 select {
88- case entryChan <- entryInfo {Entry : e , FileInfo : s .fileInfo ()}:
79+ case entryChan <- entryInfo {Entry : e , fileInfo : s .fileInfo ()}:
8980 case <- ctx .Done ():
9081 return nil
9182 }
@@ -146,22 +137,37 @@ type mergedStream []logStream
146137
147138// newMergedStreamFromPatterns creates a new logStream by first glob
148139// expanding pattern, then filtering for matching files which conform to the
149- // log filename pattern and match the program. The returned stream will only
150- // return log entries in [from, to].
140+ // filePattern and if program is non-nil, they match the program which is
141+ // extracted from matching files via the named capture group with the name
142+ // "program". The returned stream will only return log entries in [from, to].
143+ // If no program capture group exists all files match.
151144func newMergedStreamFromPatterns (
152- ctx context.Context , patterns []string , program * regexp.Regexp , from , to time.Time ,
145+ ctx context.Context ,
146+ patterns []string ,
147+ filePattern , programFilter * regexp.Regexp ,
148+ from , to time.Time ,
153149) (logStream , error ) {
154150 paths , err := expandPatterns (patterns )
155151 if err != nil {
156152 return nil , err
157153 }
158- files , err := findLogFiles (paths , program , to )
154+ files , err := findLogFiles (paths , filePattern , programFilter ,
155+ groupIndex (filePattern , "program" ), to )
159156 if err != nil {
160157 return nil , err
161158 }
162159 return newMergedStream (ctx , files , from , to )
163160}
164161
162+ func groupIndex (re * regexp.Regexp , groupName string ) int {
163+ for i , n := range re .SubexpNames () {
164+ if n == groupName {
165+ return i
166+ }
167+ }
168+ return - 1
169+ }
170+
165171func newMergedStream (
166172 ctx context.Context , files []fileInfo , from , to time.Time ,
167173) (* mergedStream , error ) {
@@ -246,7 +252,7 @@ func (l *mergedStream) pop() (log.Entry, bool) {
246252 return e , true
247253}
248254
249- func (l * mergedStream ) fileInfo () * log. FileInfo {
255+ func (l * mergedStream ) fileInfo () * fileInfo {
250256 if len (* l ) == 0 {
251257 return nil
252258 }
@@ -285,57 +291,43 @@ func removeDuplicates(strings []string) (filtered []string) {
285291 return filtered
286292}
287293
288- type parseLogFilenameError struct {
289- path string
290- err error
291- }
292-
293- func (e * parseLogFilenameError ) Error () string {
294- return fmt .Sprintf ("failed to parse filename for %v: %v" , e .path , e .err )
295- }
296-
297- func getLogFileInfo (path string ) (fileInfo , error ) {
298- filename := filepath .Base (path )
299- details , err := log .ParseLogFilename (filename )
300- if err != nil {
301- return fileInfo {}, & parseLogFilenameError {path : path , err : err }
302- }
303- fi , err := os .Stat (path )
304- if err != nil {
305- return fileInfo {}, err
294+ func getLogFileInfo (path string , filePattern * regexp.Regexp ) (fileInfo , bool ) {
295+ if matches := filePattern .FindStringSubmatchIndex (path ); matches != nil {
296+ return fileInfo {path : path , matches : matches , pattern : filePattern }, true
306297 }
307- return fileInfo {
308- path : path ,
309- FileInfo : log .MakeFileInfo (details , fi ),
310- }, nil
298+ return fileInfo {}, false
311299}
312300
313301type fileInfo struct {
314- path string
315- log.FileInfo
302+ path string
303+ pattern * regexp.Regexp
304+ matches []int
316305}
317306
318- func findLogFiles (paths []string , program * regexp.Regexp , to time.Time ) ([]fileInfo , error ) {
319- to = to .Truncate (time .Second ) // Log files only have second resolution.
307+ func findLogFiles (
308+ paths []string , filePattern , programFilter * regexp.Regexp , programGroup int , to time.Time ,
309+ ) ([]fileInfo , error ) {
310+ to = to .Truncate (time .Second ) // log files only have second resolution
320311 fileChan := make (chan fileInfo , len (paths ))
321- var g errgroup.Group
322- for i := range paths {
323- p := paths [i ]
324- g .Go (func () error {
325- fi , err := getLogFileInfo (p )
326- if err == nil && program .MatchString (fi .Details .Program ) {
327- if to .IsZero () || timeutil .Unix (0 , fi .Details .Time ).Before (to ) {
328- fileChan <- fi
312+ var wg sync.WaitGroup
313+ wg .Add (len (paths ))
314+ for _ , p := range paths {
315+ go func (p string ) {
316+ defer wg .Done ()
317+ fi , ok := getLogFileInfo (p , filePattern )
318+ if ! ok {
319+ return
320+ }
321+ if programGroup > 0 {
322+ program := fi .path [fi .matches [2 * programGroup ]:fi .matches [2 * programGroup + 1 ]]
323+ if ! programFilter .MatchString (program ) {
324+ return
329325 }
330- } else if _ , isParseErr := err .(* parseLogFilenameError ); isParseErr {
331- err = nil
332326 }
333- return err
334- })
335- }
336- if err := g .Wait (); err != nil {
337- return nil , err
327+ fileChan <- fi
328+ }(p )
338329 }
330+ wg .Wait ()
339331 files := make ([]fileInfo , 0 , len (fileChan ))
340332 close (fileChan )
341333 for f := range fileChan {
@@ -393,7 +385,7 @@ func (bs *bufferedLogStream) run() {
393385
394386func (bs * bufferedLogStream ) peek () (log.Entry , bool ) {
395387 if bs .ok && ! bs .read {
396- if bs .c == nil { // Indicates that run has not been called.
388+ if bs .c == nil { // indicates that run has not been called
397389 bs .runOnce .Do (bs .run )
398390 }
399391 bs .e , bs .ok = <- bs .c
@@ -431,7 +423,6 @@ type fileLogStream struct {
431423// file is always closed before returning from this constructor so the initial
432424// peek does not consume resources.
433425func newFileLogStream (fi fileInfo , from , to time.Time ) (logStream , error ) {
434- // we want to create the struct, do a peek, then close the file
435426 s := & fileLogStream {
436427 fi : fi ,
437428 from : from ,
@@ -511,8 +502,8 @@ func (s *fileLogStream) pop() (e log.Entry, ok bool) {
511502 return e , ok
512503}
513504
514- func (s * fileLogStream ) fileInfo () * log. FileInfo { return & s .fi . FileInfo }
515- func (s * fileLogStream ) error () error { return s .err }
505+ func (s * fileLogStream ) fileInfo () * fileInfo { return & s .fi }
506+ func (s * fileLogStream ) error () error { return s .err }
516507
517508// seekToFirstAfterFrom uses binary search to seek to an offset after all
518509// entries which occur before from.
0 commit comments