github.com/haraldrudell/parl@v0.4.176/nb-chan-logger.go (about)

     1  /*
     2  © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package parl
     7  
     8  import (
     9  	"strconv"
    10  	"time"
    11  
    12  	"github.com/haraldrudell/parl/perrors"
    13  )
    14  
    15  const (
    16  	// [NBChanLogger] logging thread does not close until channel is closed
    17  	NBChanExpectClose = true
    18  	// [NBChanLogger] logging thread exits when channel has been read to empty
    19  	NBChanWillNotClose = false
    20  )
    21  
    22  // generates labels "1"… separating different channel instances
    23  var nbChanLoggerID UniqueIDUint64
    24  
    25  // NBChanLogger is a debug logger for an NBChan instance
    26  //   - label is a string leading printouts, default a small integer
    27  //   - NBChan is the channel watched
    28  //   - printout continues until the channel is empty and the thread has exited
    29  //   - if expectClose is true, printout will continue until the underlying channel is closed
    30  //   - log default is parl.Log
    31  func NBChanLogger[T any](label string, n *NBChan[T], expectClose bool, log ...PrintfFunc) {
    32  	if n == nil {
    33  		panic(perrors.NewPF("nbChan cannot be nil"))
    34  	}
    35  	if label == "" {
    36  		label = strconv.Itoa(int(nbChanLoggerID.ID()))
    37  	}
    38  	var log0 PrintfFunc
    39  	if len(log) > 0 {
    40  		log0 = log[0]
    41  	}
    42  	if log0 == nil {
    43  		log0 = Log
    44  	}
    45  	go doLogging(label, n, expectClose, log0)
    46  }
    47  
    48  // doLogging prints NBChan status output every second
    49  func doLogging[T any](label string, n *NBChan[T], expectClose bool, log PrintfFunc) {
    50  
    51  	// ticker for periodic printing
    52  	var ticker = time.NewTicker(time.Second)
    53  	defer ticker.Stop()
    54  
    55  	var endCh = n.WaitForCloseCh()
    56  	for {
    57  		log(label + "\x20" + NBChanState(n))
    58  
    59  		if n.Count() == 0 &&
    60  			n.ThreadStatus() == NBChanExit &&
    61  			n.sends.Load() == 0 &&
    62  			n.gets.Load() == 0 &&
    63  			(!expectClose || n.DidClose()) {
    64  			return
    65  		}
    66  
    67  		select {
    68  		case <-endCh:
    69  			endCh = nil
    70  		case <-ticker.C:
    71  		}
    72  	}
    73  }
    74  
    75  // “length/i/o: 1/0/0 close-now:true-false thread: send sends: 0 gets: 0 always: true-true chClosed: false err: false”
    76  func NBChanState[T any](n *NBChan[T]) (s string) {
    77  	n.inputLock.Lock()
    78  	var in = len(n.inputQueue)
    79  	n.inputLock.Unlock()
    80  	n.outputLock.Lock()
    81  	var out = len(n.outputQueue)
    82  	n.outputLock.Unlock()
    83  	var threadType string
    84  	if n.isNoThread.Load() {
    85  		threadType = "-" + NBChanNone.String()
    86  	} else if n.isOnDemandThread.Load() {
    87  		threadType = "-" + NBChanOnDemand.String()
    88  	}
    89  	var alertValue string
    90  	if n.tcAlertActive.Load() || n.alertChan2Active.Load() != nil {
    91  		alertValue = "-alertValue"
    92  	}
    93  	var hasData string
    94  	if n.isDataAvailable.Load() {
    95  		hasData = "data"
    96  	} else {
    97  		hasData = "empty"
    98  	}
    99  
   100  	return Sprintf("length/i/o:%d/%d/%d-%s%s sends/gets:%d/%d thread:%s%s close/now/ch:%t/%t/%t err: %t",
   101  
   102  		// “length/i/o: 1/0/0” unsentCount, len input, len output
   103  		n.Count(), in, out, hasData, alertValue,
   104  
   105  		// “send sends: 0 gets: 0” pending Send/SendMany, Get
   106  		n.sends.Load(), n.gets.Load(),
   107  
   108  		// “thread: chSend” ThreadStatus
   109  		n.ThreadStatus(), threadType,
   110  
   111  		// “close-now:true-false” Close-CloseNow
   112  		n.isCloseInvoked.IsInvoked(), n.isCloseNow.IsInvoked(), n.IsClosed(),
   113  
   114  		// “err: false” if NBCHan had panic or close error
   115  		n.GetError() != nil,
   116  	)
   117  }