From bc9a40106c7aa1f383fbce1d8b4a066f0cf0ca35 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Sat, 14 Dec 2024 14:04:33 +0900 Subject: [PATCH] improving logger to write to a file --- client.go | 56 ++++++++++++++++++++++++------------ cmd/main.go | 81 ++++++++++++++++++++++++++++++++++++++++------------- hodu.go | 1 + server.go | 3 ++ 4 files changed, 104 insertions(+), 37 deletions(-) diff --git a/client.go b/client.go index 2af6709..79d47bb 100644 --- a/client.go +++ b/client.go @@ -721,36 +721,53 @@ func (cts *ClientConn) AddClientRoutes(peer_addrs []string) error { for _, v = range peer_addrs { va = strings.Split(v, ",") - if len(va) <= 0 || len(va) >= 4 { - return fmt.Errorf("invalid address %v", va) - } + if len(va) <= 0 { return fmt.Errorf("blank value") } + if len(va) >= 5 { return fmt.Errorf("too many fields in %v", v) } - _, port, err = net.SplitHostPort(va[0]) + _, port, err = net.SplitHostPort(strings.TrimSpace(va[0])) if err != nil { return fmt.Errorf("invalid address %s", va[0], err.Error()) } if len(va) >= 2 { - _, _, err = net.SplitHostPort(va[1]) + var f string + f = strings.TrimSpace(va[1]) + _, _, err = net.SplitHostPort(f) if err != nil { return fmt.Errorf("invalid address %s", va[1], err.Error()) } - svc_addr = va[1] - } - - if len(va) >= 3 { - ptc_name = va[2] + svc_addr = f } option = RouteOption(ROUTE_OPTION_TCP) - // automatic determination of protocol for common ports - switch port { - case "22": - option |= RouteOption(ROUTE_OPTION_SSH) - case "80": - option |= RouteOption(ROUTE_OPTION_HTTP) - case "443": - option |= RouteOption(ROUTE_OPTION_HTTPS) + if len(va) >= 3 { + switch strings.ToLower(strings.TrimSpace(va[2])) { + case "ssh": + option |= RouteOption(ROUTE_OPTION_SSH) + case "http": + option |= RouteOption(ROUTE_OPTION_HTTP) + case "https": + option |= RouteOption(ROUTE_OPTION_HTTPS) + + case "": + fallthrough + case "auto": + // automatic determination of protocol for common ports + switch port { + case "22": + option |= RouteOption(ROUTE_OPTION_SSH) + case "80": + option |= RouteOption(ROUTE_OPTION_HTTP) + case "443": + option |= RouteOption(ROUTE_OPTION_HTTPS) + } + default: + return fmt.Errorf("invalid option value %s", va[2]) + } + } + + if len(va) >= 4 { + ptc_name = strings.TrimSpace(va[3]) } _, err = cts.AddNewClientRoute(va[0], ptc_name, svc_addr, "", option) @@ -1460,6 +1477,9 @@ func (c *Client) StopServices() { } } +func (s *Client) FixServices() { +} + func (c *Client) WaitForTermination() { c.wg.Wait() } diff --git a/cmd/main.go b/cmd/main.go index 0bf8fd0..3d57ef5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -29,10 +29,30 @@ var hodul_tls_key_text []byte // -------------------------------------------------------------------- type AppLogger struct { - id string - out io.Writer - mtx sync.Mutex - mask hodu.LogMask + Id string + Out io.Writer + Mask hodu.LogMask + + _mtx sync.Mutex + _file *os.File +} + +func NewAppLogger (id string, w io.Writer, mask hodu.LogMask) *AppLogger { + return &AppLogger{Id: id, Out: w, Mask: mask} +} + +func NewAppLoggerToFile (id string, file string, mask hodu.LogMask) (*AppLogger, error) { + var err error + var f *os.File + + f, err = os.OpenFile(file, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { return nil, err } + + return &AppLogger{Id: id, Out: f, Mask: mask, _file: f}, nil +} + +func (l* AppLogger) Close() { + if l._file != nil { l._file.Close() } } func (l* AppLogger) Write(id string, level hodu.LogLevel, fmtstr string, args ...interface{}) { @@ -47,7 +67,7 @@ func (l* AppLogger) Write(id string, level hodu.LogLevel, fmtstr string, args .. var caller_line int var caller_ok bool - if l.mask & hodu.LogMask(level) == 0 { return } + if l.Mask & hodu.LogMask(level) == 0 { return } now = time.Now() @@ -65,20 +85,20 @@ func (l* AppLogger) Write(id string, level hodu.LogLevel, fmtstr string, args .. // TODO: add pid? msg = fmt.Sprintf(fmtstr, args...) if id == "" { - lid = fmt.Sprintf("%s: ", l.id) + lid = fmt.Sprintf("%s: ", l.Id) } else { - lid = fmt.Sprintf("%s(%s): ", l.id, id) + lid = fmt.Sprintf("%s(%s): ", l.Id, id) } - l.mtx.Lock() - l.out.Write([]byte(hdr)) + l._mtx.Lock() + l.Out.Write([]byte(hdr)) if caller_ok { - l.out.Write([]byte(fmt.Sprintf("[%s:%d] ", filepath.Base(caller_file), caller_line))) + l.Out.Write([]byte(fmt.Sprintf("[%s:%d] ", filepath.Base(caller_file), caller_line))) } - if lid != "" { l.out.Write([]byte(lid)) } - l.out.Write([]byte(msg)) - if msg[len(msg) - 1] != '\n' { l.out.Write([]byte("\n")) } - l.mtx.Unlock() + if lid != "" { l.Out.Write([]byte(lid)) } + l.Out.Write([]byte(msg)) + if msg[len(msg) - 1] != '\n' { l.Out.Write([]byte("\n")) } + l._mtx.Unlock() } // -------------------------------------------------------------------- @@ -105,8 +125,8 @@ chan_loop: for { select { case <-sighup_chan: - // TODO: - //sh.svc.ReqReload() + sh.svc.FixServices() + case sig = <-sigterm_chan: sh.svc.StopServices() sh.svc.WriteLog ("", hodu.LOG_INFO, "Received %s signal", sig) @@ -132,6 +152,9 @@ func (sh *signal_handler) StopServices() { syscall.Kill(syscall.Getpid(), syscall.SIGTERM) // TODO: find a better to terminate the signal handler... } +func (sh *signal_handler) FixServices() { +} + func (sh *signal_handler) WaitForTermination() { // not implemented. see the comment in StartServices() // sh.wg.Wait() @@ -151,6 +174,7 @@ func server_main(ctl_addrs []string, rpc_addrs []string, pxy_addrs []string, cfg var ctl_prefix string var logger *AppLogger var log_mask hodu.LogMask + var log_file string var max_rpc_conns int var max_peers int var err error @@ -179,6 +203,7 @@ func server_main(ctl_addrs []string, rpc_addrs []string, pxy_addrs []string, cfg ctl_prefix = cfg.CTL.Service.Prefix log_mask = log_strings_to_mask(cfg.APP.LogMask) + log_file = cfg.APP.LogFile max_rpc_conns = cfg.APP.MaxRpcConns max_peers = cfg.APP.MaxPeers } @@ -187,8 +212,15 @@ func server_main(ctl_addrs []string, rpc_addrs []string, pxy_addrs []string, cfg return fmt.Errorf("no rpc service addresses specified") } - // TODO: Change out field depending on cfg.APP.LogFile - logger = &AppLogger{id: "server", out: os.Stderr, mask: log_mask} + if log_file == "" { + logger = NewAppLogger("server", os.Stderr, log_mask) + } else { + logger, err = NewAppLoggerToFile("server", log_file, log_mask) + if err != nil { + return fmt.Errorf("failed to initialize logger - %s", err.Error()) + } + } + s, err = hodu.NewServer( context.Background(), logger, @@ -210,6 +242,7 @@ func server_main(ctl_addrs []string, rpc_addrs []string, pxy_addrs []string, cfg s.StartPxyService() s.StartExtService(&signal_handler{svc:s}, nil) s.WaitForTermination() + logger.Close() return nil } @@ -224,6 +257,7 @@ func client_main(ctl_addrs []string, rpc_addrs []string, peer_addrs []string, cf var cc hodu.ClientConfig var logger *AppLogger var log_mask hodu.LogMask + var log_file string var max_rpc_conns int var max_peers int var peer_conn_tmout time.Duration @@ -247,6 +281,7 @@ func client_main(ctl_addrs []string, rpc_addrs []string, peer_addrs []string, cf cc.ServerSeedTmout = cfg.RPC.Endpoint.SeedTmout cc.ServerAuthority = cfg.RPC.Endpoint.Authority log_mask = log_strings_to_mask(cfg.APP.LogMask) + log_file = cfg.APP.LogFile max_rpc_conns = cfg.APP.MaxRpcConns max_peers = cfg.APP.MaxPeers peer_conn_tmout = cfg.APP.PeerConnTmout @@ -257,7 +292,14 @@ func client_main(ctl_addrs []string, rpc_addrs []string, peer_addrs []string, cf cc.ServerAddrs = rpc_addrs cc.PeerAddrs = peer_addrs - logger = &AppLogger{id: "client", out: os.Stderr, mask: log_mask} + if log_file == "" { + logger = NewAppLogger("server", os.Stderr, log_mask) + } else { + logger, err = NewAppLoggerToFile("server", log_file, log_mask) + if err != nil { + return fmt.Errorf("failed to initialize logger - %s", err.Error()) + } + } c = hodu.NewClient( context.Background(), logger, @@ -273,6 +315,7 @@ func client_main(ctl_addrs []string, rpc_addrs []string, peer_addrs []string, cf c.StartCtlService() // control channel c.StartExtService(&signal_handler{svc:c}, nil) // signal handler task c.WaitForTermination() + logger.Close() return nil } diff --git a/hodu.go b/hodu.go index f936bcf..0fd0ad4 100644 --- a/hodu.go +++ b/hodu.go @@ -34,6 +34,7 @@ type Service interface { RunTask(wg *sync.WaitGroup) // blocking. run the actual task loop. it must call wg.Done() upon exit from itself. StartService(data interface{}) // non-blocking. spin up a service. it may be invokded multiple times for multiple instances StopServices() // non-blocking. send stop request to all services spun up + FixServices() // do some fixup as needed WaitForTermination() // blocking. must wait until all services are stopped WriteLog(id string, level LogLevel, fmtstr string, args ...interface{}) } diff --git a/server.go b/server.go index 69f47c6..2f38e99 100644 --- a/server.go +++ b/server.go @@ -1373,6 +1373,9 @@ func (s *Server) StopServices() { } } +func (s *Server) FixServices() { +} + func (s *Server) WaitForTermination() { s.wg.Wait() }