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