]> Cypherpunks.ru repositories - goredo.git/blob - log.go
Download link for 2.6.2 release
[goredo.git] / log.go
1 // goredo -- djb's redo implementation on pure Go
2 // Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 // Logging facilities
17
18 package main
19
20 import (
21         "bytes"
22         "flag"
23         "fmt"
24         "os"
25         "strings"
26         "sync"
27
28         "golang.org/x/term"
29 )
30
31 const (
32         EnvLevel      = "REDO_LEVEL"
33         EnvNoProgress = "REDO_NO_PROGRESS"
34         EnvDebug      = "REDO_DEBUG"
35         EnvLogWait    = "REDO_LOG_WAIT"
36         EnvLogLock    = "REDO_LOG_LOCK"
37         EnvLogPid     = "REDO_LOG_PID"
38         EnvLogJS      = "REDO_LOG_JS"
39         EnvNoColor    = "NO_COLOR"
40 )
41
42 var (
43         Level      = 0
44         NoColor    bool
45         NoProgress bool
46         Debug      bool
47         LogWait    bool
48         LogLock    bool
49         LogJS      bool
50         MyPID      int
51
52         CDebug string
53         CRedo  string
54         CWait  string
55         CLock  string
56         CErr   string
57         CWarn  string
58         CJS    string
59         CReset string
60         CNone  = "NONE"
61
62         flagDebug      = flag.Bool("d", false, fmt.Sprintf("enable debug logging (%s=1)", EnvDebug))
63         flagNoProgress *bool
64         flagLogWait    *bool
65         flagLogLock    *bool
66         flagLogPid     *bool
67         flagLogJS      *bool
68
69         LogMutex      sync.Mutex
70         KeyEraseLine  string
71         LastLoggedTgt string
72 )
73
74 func init() {
75         var b bytes.Buffer
76         t := term.NewTerminal(&b, "")
77         CDebug = string(t.Escape.Yellow)
78         CRedo = string(t.Escape.Green)
79         CWait = string(t.Escape.Blue)
80         CLock = string(t.Escape.Cyan)
81         CErr = string(t.Escape.Red)
82         CWarn = string(t.Escape.Magenta)
83         CJS = string(t.Escape.White)
84         CReset = string(t.Escape.Reset)
85         KeyEraseLine = fmt.Sprintf("%s[K", CReset[0:1])
86
87         cmdName := CmdName()
88         if !(cmdName == CmdNameRedo || cmdName == CmdNameRedoIfchange) {
89                 return
90         }
91         flagNoProgress = flag.Bool("no-progress", false,
92                 fmt.Sprintf("no progress printing (%s=1), also implies -no-status", EnvNoProgress))
93         flagLogWait = flag.Bool("log-wait", false,
94                 fmt.Sprintf("enable wait messages logging (%s=1)", EnvLogWait))
95         flagLogLock = flag.Bool("log-lock", false,
96                 fmt.Sprintf("enable lock messages logging (%s=1)", EnvLogLock))
97         flagLogPid = flag.Bool("log-pid", false,
98                 fmt.Sprintf("append PIDs (%s=1)", EnvLogPid))
99         flagLogJS = flag.Bool("log-js", false,
100                 fmt.Sprintf("enable jobserver messages logging (%s=1)", EnvLogJS))
101 }
102
103 func erasedStatus(s, end string) string {
104         if NoProgress || NoColor {
105                 return s + end
106         }
107         return s + KeyEraseLine + end
108 }
109
110 func withPrependedTgt(s string) {
111         if s[0] != '[' {
112                 stderrWrite(erasedStatus(s, "\n"))
113                 return
114         }
115         i := strings.IndexByte(s, ']')
116         if i == -1 {
117                 stderrWrite(s)
118                 return
119         }
120         tgt, s := s[1:i], s[i+1:]
121         if tgt != LastLoggedTgt {
122                 LastLoggedTgt = tgt
123                 tgt = "redo " + tgt + " ..."
124                 if MyPID != 0 {
125                         tgt = fmt.Sprintf("[%d] %s", MyPID, tgt)
126                 }
127                 stderrWrite(erasedStatus(colourize(CDebug, tgt), "\n"))
128         }
129         stderrWrite(erasedStatus(s, "\n"))
130 }
131
132 func stderrWrite(s string) {
133         LogMutex.Lock()
134         os.Stderr.WriteString(s)
135         LogMutex.Unlock()
136 }
137
138 func tracef(level, format string, args ...interface{}) {
139         var p string
140         if MyPID != 0 {
141                 p = fmt.Sprintf("[%d] ", MyPID)
142         }
143         switch level {
144         case CNone:
145                 p = erasedStatus(StderrPrefix+p+fmt.Sprintf(format, args...), "\n")
146                 stderrWrite(p)
147                 return
148         case CDebug:
149                 if !Debug {
150                         return
151                 }
152                 p += "dbg  "
153         case CWait:
154                 if !(LogWait || Debug) {
155                         return
156                 }
157                 p += "wait "
158         case CRedo:
159                 if NoProgress {
160                         return
161                 }
162                 p += "redo "
163         case CLock:
164                 if !(LogLock || Debug) {
165                         return
166                 }
167                 p += "lock "
168         case CJS:
169                 if !(LogJS || Debug) {
170                         return
171                 }
172                 p += "js   "
173         case CErr:
174                 p += "err  "
175         case CWarn:
176                 p += "warn "
177         }
178         msg := fmt.Sprintf(format, args...)
179         msg = StderrPrefix + colourize(level, p+strings.Repeat(". ", Level)+msg)
180         msg = erasedStatus(msg, "\n")
181         stderrWrite(msg)
182 }
183
184 func colourize(colour, s string) string {
185         if NoColor {
186                 return s
187         }
188         return colour + s + CReset
189 }