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