enhanced logging by supporting --log-file and handling a log file under /dev specially

This commit is contained in:
hyung-hwan 2024-12-16 00:41:32 +09:00
parent 8cab165cc4
commit fe67d9e16f
3 changed files with 62 additions and 34 deletions

View File

@ -10,6 +10,11 @@ import "strings"
import "sync" import "sync"
import "time" import "time"
type app_logger_msg_t struct {
code int
data string
}
type AppLogger struct { type AppLogger struct {
Id string Id string
Out io.Writer Out io.Writer
@ -19,39 +24,48 @@ type AppLogger struct {
file_name string // you can get the file name from file but this is to preserve the original. file_name string // you can get the file name from file but this is to preserve the original.
file_rotate int file_rotate int
file_max_size int64 file_max_size int64
msg_chan chan string msg_chan chan app_logger_msg_t
stop_req_chan chan struct{}
rotate_req_chan chan struct{}
wg sync.WaitGroup wg sync.WaitGroup
} }
func NewAppLogger (id string, w io.Writer, mask hodu.LogMask) *AppLogger { func NewAppLogger (id string, w io.Writer, mask hodu.LogMask) *AppLogger {
var l *AppLogger var l *AppLogger
l = &AppLogger{Id: id, Out: w, Mask: mask, msg_chan: make(chan string, 256), stop_req_chan: make(chan struct{}, 1), rotate_req_chan: make(chan struct{}, 16) } l = &AppLogger{
Id: id,
Out: w,
Mask: mask,
msg_chan: make(chan app_logger_msg_t, 256),
}
l.wg.Add(1) l.wg.Add(1)
go l.logger_task() go l.logger_task()
return l return l
} }
func NewAppLoggerToFile (id string, file string, max_size int64, rotate int, mask hodu.LogMask) (*AppLogger, error) { func NewAppLoggerToFile (id string, file_name string, max_size int64, rotate int, mask hodu.LogMask) (*AppLogger, error) {
var l *AppLogger var l *AppLogger
var f *os.File var f *os.File
var matched bool
var err error var err error
f, err = os.OpenFile(file, os.O_CREATE | os.O_APPEND | os.O_WRONLY, 0666) f, err = os.OpenFile(file_name, os.O_CREATE | os.O_APPEND | os.O_WRONLY, 0666)
if err != nil { return nil, err } if err != nil { return nil, err }
matched, _ = filepath.Match("/dev/*", file_name)
if matched {
// if the log file is under /dev, disable rotation
max_size = 0
rotate = 0
}
l = &AppLogger{ l = &AppLogger{
Id: id, Id: id,
Out: f, Out: f,
Mask: mask, Mask: mask,
file: f, file: f,
file_name: file, file_name: file_name,
file_max_size: max_size, file_max_size: max_size,
file_rotate: rotate, file_rotate: rotate,
msg_chan: make(chan string, 256), msg_chan: make(chan app_logger_msg_t, 256),
stop_req_chan: make(chan struct{}, 1),
rotate_req_chan: make(chan struct{}, 16),
} }
l.wg.Add(1) l.wg.Add(1)
go l.logger_task() go l.logger_task()
@ -59,21 +73,26 @@ func NewAppLoggerToFile (id string, file string, max_size int64, rotate int, mas
} }
func (l *AppLogger) Close() { func (l *AppLogger) Close() {
l.stop_req_chan <- struct{}{} l.msg_chan <- app_logger_msg_t{code: 1}
l.wg.Wait() l.wg.Wait()
if l.file != nil { l.file.Close() } if l.file != nil { l.file.Close() }
} }
func (l *AppLogger) Rotate() {
l.msg_chan <- app_logger_msg_t{code: 2}
}
func (l *AppLogger) logger_task() { func (l *AppLogger) logger_task() {
var msg string var msg app_logger_msg_t
defer l.wg.Done() defer l.wg.Done()
main_loop: main_loop:
for { for {
select { select {
case msg = <-l.msg_chan: case msg = <-l.msg_chan:
if msg.code == 0 {
//l.Out.Write([]byte(msg)) //l.Out.Write([]byte(msg))
io.WriteString(l.Out, msg) io.WriteString(l.Out, msg.data)
if l.file_max_size > 0 && l.file != nil { if l.file_max_size > 0 && l.file != nil {
var fi os.FileInfo var fi os.FileInfo
var err error var err error
@ -82,19 +101,16 @@ main_loop:
l.rotate() l.rotate()
} }
} }
} else if msg.code == 1 {
case <-l.stop_req_chan:
break main_loop break main_loop
} else if msg.code == 2 {
case <- l.rotate_req_chan:
l.rotate() l.rotate()
} }
// other code must not appear here.
}
} }
} }
func (l *AppLogger) Rotate() {
l.rotate_req_chan <- struct{}{}
}
func (l *AppLogger) Write(id string, level hodu.LogLevel, fmtstr string, args ...interface{}) { func (l *AppLogger) Write(id string, level hodu.LogLevel, fmtstr string, args ...interface{}) {
if l.Mask & hodu.LogMask(level) == 0 { return } if l.Mask & hodu.LogMask(level) == 0 { return }
@ -148,7 +164,7 @@ func (l *AppLogger) write(id string, level hodu.LogLevel, call_depth int, fmtstr
if msg[len(msg) - 1] != '\n' { sb.WriteRune('\n') } if msg[len(msg) - 1] != '\n' { sb.WriteRune('\n') }
// use queue to avoid blocking operation as much as possible // use queue to avoid blocking operation as much as possible
l.msg_chan <- sb.String() l.msg_chan <- app_logger_msg_t{ code: 0, data: sb.String() }
} }
func (l *AppLogger) rotate() { func (l *AppLogger) rotate() {

View File

@ -262,6 +262,7 @@ func main() {
var ctl_addrs []string var ctl_addrs []string
var pxy_addrs []string var pxy_addrs []string
var cfgfile string var cfgfile string
var logfile string
var cfg *ServerConfig var cfg *ServerConfig
ctl_addrs = make([]string, 0) ctl_addrs = make([]string, 0)
@ -281,6 +282,10 @@ func main() {
pxy_addrs = append(pxy_addrs, v) pxy_addrs = append(pxy_addrs, v)
return nil return nil
}) })
flgs.Func("log-file", "specify a log file", func(v string) error {
logfile = v
return nil
})
flgs.Func("config-file", "specify a configuration file path", func(v string) error { flgs.Func("config-file", "specify a configuration file path", func(v string) error {
cfgfile = v cfgfile = v
return nil return nil
@ -302,6 +307,7 @@ func main() {
goto oops goto oops
} }
} }
if logfile != "" { cfg.APP.LogFile = logfile }
err = server_main(ctl_addrs, rpc_addrs, pxy_addrs, cfg) err = server_main(ctl_addrs, rpc_addrs, pxy_addrs, cfg)
if err != nil { if err != nil {
@ -312,6 +318,7 @@ func main() {
var rpc_addrs []string var rpc_addrs []string
var ctl_addrs []string var ctl_addrs []string
var cfgfile string var cfgfile string
var logfile string
var cfg *ClientConfig var cfg *ClientConfig
ctl_addrs = make([]string, 0) ctl_addrs = make([]string, 0)
@ -326,6 +333,10 @@ func main() {
rpc_addrs = append(rpc_addrs, v) rpc_addrs = append(rpc_addrs, v)
return nil return nil
}) })
flgs.Func("log-file", "specify a log file", func(v string) error {
logfile = v
return nil
})
flgs.Func("config-file", "specify a configuration file path", func(v string) error { flgs.Func("config-file", "specify a configuration file path", func(v string) error {
cfgfile = v cfgfile = v
return nil return nil
@ -338,13 +349,14 @@ func main() {
goto wrong_usage goto wrong_usage
} }
if (cfgfile != "") { if cfgfile != "" {
cfg, err = load_client_config(cfgfile) cfg, err = load_client_config(cfgfile)
if err != nil { if err != nil {
fmt.Printf ("ERROR: failed to load configuration file %s - %s\n", cfgfile, err.Error()) fmt.Printf ("ERROR: failed to load configuration file %s - %s\n", cfgfile, err.Error())
goto oops goto oops
} }
} }
if logfile != "" { cfg.APP.LogFile = logfile }
err = client_main(ctl_addrs, rpc_addrs, flgs.Args(), cfg) err = client_main(ctl_addrs, rpc_addrs, flgs.Args(), cfg)
if err != nil { if err != nil {

View File

@ -278,7 +278,7 @@ func (r *ServerRoute) RunTask(wg *sync.WaitGroup) {
r.ReqStop() r.ReqStop()
r.pts_wg.Wait() r.pts_wg.Wait()
r.cts.svr.log.Write(r.cts.sid, LOG_DEBUG, "All service-side peer handlers completed on route(%d)", r.id) r.cts.svr.log.Write(r.cts.sid, LOG_DEBUG, "All service-side peer handlers ended on route(%d)", r.id)
r.cts.RemoveServerRoute(r) // final phase... r.cts.RemoveServerRoute(r) // final phase...
} }
@ -1060,10 +1060,10 @@ task_loop:
s.ReqStop() s.ReqStop()
s.rpc_wg.Wait() s.rpc_wg.Wait()
s.log.Write("", LOG_DEBUG, "All RPC listeners completed") s.log.Write("", LOG_DEBUG, "All RPC listeners ended")
s.cts_wg.Wait() s.cts_wg.Wait()
s.log.Write("", LOG_DEBUG, "All CTS handlers completed") s.log.Write("", LOG_DEBUG, "All CTS handlers ended")
// stop the main grpc server after all the other tasks are finished. // stop the main grpc server after all the other tasks are finished.
s.rpc_svr.Stop() s.rpc_svr.Stop()