logging.go raw

   1  package pipe
   2  
   3  import (
   4  	"github.com/niubaoshu/gotiny"
   5  	"github.com/p9c/p9/pkg/interrupt"
   6  	"github.com/p9c/p9/pkg/qu"
   7  	"go.uber.org/atomic"
   8  
   9  	"github.com/p9c/p9/pkg/log"
  10  )
  11  
  12  func LogConsume(
  13  	quit qu.C, handler func(ent *log.Entry) (e error,),
  14  	filter func(pkg string) (out bool), args ...string,
  15  ) *Worker {
  16  	D.Ln("starting log consumer")
  17  	return Consume(
  18  		quit, func(b []byte) (e error) {
  19  			// we are only listening for entries
  20  			if len(b) >= 4 {
  21  				magic := string(b[:4])
  22  				switch magic {
  23  				case "entr":
  24  					var ent log.Entry
  25  					n := gotiny.Unmarshal(b, &ent)
  26  					D.Ln("consume", n)
  27  					if filter(ent.Package) {
  28  						// if the worker filter is out of sync this stops it printing
  29  						return
  30  					}
  31  					switch ent.Level {
  32  					case log.Fatal:
  33  					case log.Error:
  34  					case log.Warn:
  35  					case log.Info:
  36  					case log.Check:
  37  					case log.Debug:
  38  					case log.Trace:
  39  					default:
  40  						D.Ln("got an empty log entry")
  41  						return
  42  					}
  43  					if e = handler(&ent); E.Chk(e) {
  44  					}
  45  				}
  46  			}
  47  			return
  48  		}, args...,
  49  	)
  50  }
  51  
  52  func Start(w *Worker) {
  53  	D.Ln("sending start signal")
  54  	var n int
  55  	var e error
  56  	if n, e = w.StdConn.Write([]byte("run ")); n < 1 || E.Chk(e) {
  57  		D.Ln("failed to write", w.Args)
  58  	}
  59  }
  60  
  61  // Stop running the worker
  62  func Stop(w *Worker) {
  63  	D.Ln("sending stop signal")
  64  	var n int
  65  	var e error
  66  	if n, e = w.StdConn.Write([]byte("stop")); n < 1 || E.Chk(e) {
  67  		D.Ln("failed to write", w.Args)
  68  	}
  69  }
  70  
  71  // Kill sends a kill signal via the pipe logger
  72  func Kill(w *Worker) {
  73  	var e error
  74  	if w == nil {
  75  		D.Ln("asked to kill worker that is already nil")
  76  		return
  77  	}
  78  	var n int
  79  	D.Ln("sending kill signal")
  80  	if n, e = w.StdConn.Write([]byte("kill")); n < 1 || E.Chk(e) {
  81  		D.Ln("failed to write")
  82  		return
  83  	}
  84  	if e = w.Cmd.Wait(); E.Chk(e) {
  85  	}
  86  	D.Ln("sent kill signal")
  87  }
  88  
  89  // SetLevel sets the level of logging from the worker
  90  func SetLevel(w *Worker, level string) {
  91  	if w == nil {
  92  		return
  93  	}
  94  	D.Ln("sending set level", level)
  95  	lvl := 0
  96  	for i := range log.Levels {
  97  		if level == log.Levels[i] {
  98  			lvl = i
  99  		}
 100  	}
 101  	var n int
 102  	var e error
 103  	if n, e = w.StdConn.Write([]byte("slvl" + string(byte(lvl)))); n < 1 ||
 104  		E.Chk(e) {
 105  		D.Ln("failed to write")
 106  	}
 107  }
 108  
 109  // LogServe starts up a handler to listen to logs from the child process worker
 110  func LogServe(quit qu.C, appName string) {
 111  	D.Ln("starting log server")
 112  	lc := log.AddLogChan()
 113  	var logOn atomic.Bool
 114  	logOn.Store(false)
 115  	p := Serve(
 116  		quit, func(b []byte) (e error) {
 117  			// listen for commands to enable/disable logging
 118  			if len(b) >= 4 {
 119  				magic := string(b[:4])
 120  				switch magic {
 121  				case "run ":
 122  					D.Ln("setting to run")
 123  					logOn.Store(true)
 124  				case "stop":
 125  					D.Ln("stopping")
 126  					logOn.Store(false)
 127  				case "slvl":
 128  					D.Ln("setting level", log.Levels[b[4]])
 129  					log.SetLogLevel(log.Levels[b[4]])
 130  				case "kill":
 131  					D.Ln("received kill signal from pipe, shutting down", appName)
 132  					interrupt.Request()
 133  					quit.Q()
 134  				}
 135  			}
 136  			return
 137  		},
 138  	)
 139  	go func() {
 140  	out:
 141  		for {
 142  			select {
 143  			case <-quit.Wait():
 144  				if !log.LogChanDisabled.Load() {
 145  					log.LogChanDisabled.Store(true)
 146  				}
 147  				D.Ln("quitting pipe logger")
 148  				interrupt.Request()
 149  				logOn.Store(false)
 150  			out2:
 151  				// drain log channel
 152  				for {
 153  					select {
 154  					case <-lc:
 155  						break
 156  					default:
 157  						break out2
 158  					}
 159  				}
 160  				break out
 161  			case ent := <-lc:
 162  				if !logOn.Load() {
 163  					break out
 164  				}
 165  				var n int
 166  				var e error
 167  				if n, e = p.Write(gotiny.Marshal(&ent)); !E.Chk(e) {
 168  					if n < 1 {
 169  						E.Ln("short write")
 170  					}
 171  				} else {
 172  					break out
 173  				}
 174  			}
 175  		}
 176  		<-interrupt.HandlersDone
 177  		D.Ln("finished pipe logger")
 178  	}()
 179  }
 180  
 181  // FilterNone is a filter that doesn't
 182  func FilterNone(string) bool {
 183  	return false
 184  }
 185  
 186  // SimpleLog is a very simple log printer
 187  func SimpleLog(name string) func(ent *log.Entry) (e error) {
 188  	return func(ent *log.Entry) (e error) {
 189  		D.F(
 190  			"%s[%s] %s %s",
 191  			name,
 192  			ent.Level,
 193  			// ent.Time.Format(time.RFC3339),
 194  			ent.Text,
 195  			ent.CodeLocation,
 196  		)
 197  		return
 198  	}
 199  }
 200