github.com/openshift/installer@v1.4.17/pkg/lineprinter/lineprinter.go (about)

     1  // Package lineprinter wraps a Print implementation to provide an io.WriteCloser.
     2  package lineprinter
     3  
     4  import (
     5  	"bytes"
     6  	"io"
     7  	"sync"
     8  )
     9  
    10  // Print is a type that can hold fmt.Print and other implementations
    11  // which match that signature.  For example, you can use:
    12  //
    13  //	trimmer := &lineprinter.Trimmer{WrappedPrint: logrus.StandardLogger().Debug}
    14  //	linePrinter := &linePrinter{Print: trimmer.Print}
    15  //
    16  // to connect the line printer to logrus at the debug level.
    17  type Print func(args ...interface{})
    18  
    19  // LinePrinter is an io.WriteCloser that buffers written bytes.
    20  // During each Write, newline-terminated lines are removed from the
    21  // buffer and passed to Print.  On Close, any content remaining in the
    22  // buffer is also passed to Print.
    23  //
    24  // One use-case is connecting a subprocess's standard streams to a
    25  // logger:
    26  //
    27  //	linePrinter := &linePrinter{
    28  //	  Print: &Trimmer{WrappedPrint: logrus.StandardLogger().Debug}.Print,
    29  //	}
    30  //	defer linePrinter.Close()
    31  //	cmd := exec.Command(...)
    32  //	cmd.Stdout = linePrinter
    33  //
    34  // LinePrinter buffers the subcommand's byte stream and splits it into
    35  // lines for the logger.  Sometimes we might have a partial line
    36  // written to the buffer.  We don't want to push that partial line into
    37  // the logs if the next read attempt will pull in the remainder of the
    38  // line.  But we do want to push that partial line into the logs if there
    39  // will never be a next read.  So LinePrinter.Write pushes any
    40  // complete lines to the wrapped printer, and LinePrinter.Close pushes
    41  // any remaining partial line.
    42  type LinePrinter struct {
    43  	buf   bytes.Buffer
    44  	Print Print
    45  
    46  	sync.Mutex
    47  }
    48  
    49  // Write writes len(p) bytes from p to an internal buffer.  Then it
    50  // retrieves any newline-terminated lines from the internal buffer and
    51  // prints them with lp.Print.  Partial lines are left in the internal
    52  // buffer.
    53  func (lp *LinePrinter) Write(p []byte) (int, error) {
    54  	lp.Lock()
    55  	defer lp.Unlock()
    56  	n, err := lp.buf.Write(p)
    57  	if err != nil {
    58  		return n, err
    59  	}
    60  
    61  	for {
    62  		line, err := lp.buf.ReadString(byte('\n'))
    63  		if err == io.EOF {
    64  			_, err = lp.buf.Write([]byte(line))
    65  			return n, err
    66  		} else if err != nil {
    67  			return n, err
    68  		}
    69  
    70  		lp.Print(line)
    71  	}
    72  }
    73  
    74  // Close prints anything that remains in the buffer.
    75  func (lp *LinePrinter) Close() error {
    76  	lp.Lock()
    77  	defer lp.Unlock()
    78  	line := lp.buf.String()
    79  	if len(line) > 0 {
    80  		lp.Print(line)
    81  	}
    82  	return nil
    83  }