diff options
| author | Shulhan <ms@kilabit.info> | 2025-08-13 18:47:49 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2025-08-20 02:43:53 +0700 |
| commit | f9c0f7d0e73c89b7473b7c0123427b934fcbff65 (patch) | |
| tree | a91c46b963889e67bc4a0e65ba2ad9829540e72b | |
| parent | d34a7d3def91fb614fb657b81ff8265f19e078c8 (diff) | |
| download | lilin-f9c0f7d0e73c89b7473b7c0123427b934fcbff65.tar.xz | |
all: store the service logs into file
| -rw-r--r-- | lilin.go | 3 | ||||
| -rw-r--r-- | scan_report.go | 11 | ||||
| -rw-r--r-- | server.go | 5 | ||||
| -rw-r--r-- | server_options.go | 19 | ||||
| -rw-r--r-- | service_report.go | 44 | ||||
| -rw-r--r-- | service_report_test.go | 21 | ||||
| -rw-r--r-- | worker.go | 27 | ||||
| -rw-r--r-- | worker_test.go | 25 |
8 files changed, 130 insertions, 25 deletions
@@ -11,5 +11,8 @@ import ( // defTimeout define default timeout for service and client connection. const defTimeout = 5 * time.Second +// defAddress default server address. +const defAddress = `127.0.0.1:14507` + //go:embed _www var wwwFS embed.FS diff --git a/scan_report.go b/scan_report.go index 6544dc7..4b4938d 100644 --- a/scan_report.go +++ b/scan_report.go @@ -18,3 +18,14 @@ type ScanReport struct { // Success is true if service available. Success bool } + +func (report *ScanReport) toCSV() (record []string) { + record = append(record, report.At.String()) + if report.Success { + record = append(record, "true") + } else { + record = append(record, "false") + } + record = append(record, report.Error) + return record +} @@ -57,7 +57,7 @@ func NewServer(opts ServerOptions) (srv *Server, err error) { return nil, fmt.Errorf(`%s: %w`, logp, err) } - srv.worker, err = newWorker(srv.Options.configDir) + srv.worker, err = newWorker(srv.Options) if err != nil { return nil, fmt.Errorf(`%s: %w`, logp, err) } @@ -86,9 +86,6 @@ func NewServer(opts ServerOptions) (srv *Server, err error) { func (srv *Server) initFS() (err error) { if srv.Options.IsDevelopment { var dirfs = os.DirFS(`_www`) - if err != nil { - return err - } srv.fs = dirfs.(fs.ReadFileFS) } else { var subfs fs.FS diff --git a/server_options.go b/server_options.go index 4a033fc..48c47b6 100644 --- a/server_options.go +++ b/server_options.go @@ -22,6 +22,12 @@ type ServerOptions struct { // The BaseDir + "/etc/lilin/". configDir string + // The BaseDir + "/etc/lilin/service.d/". + configServiceDir string + + // The BaseDir + "/var/log/lilin/service.d/". + logServiceDir string + // The address to listen for HTTP server and APIs. Address string `ini:"server::address"` @@ -40,6 +46,15 @@ func (opts *ServerOptions) init() (err error) { } opts.configDir = filepath.Join(opts.BaseDir, `etc`, `lilin`) + opts.configServiceDir = filepath.Join(opts.configDir, `service.d`) + opts.logServiceDir = filepath.Join(opts.BaseDir, `var`, `log`, `lilin`, `service.d`) + + err = os.MkdirAll(opts.logServiceDir, 0700) + if err != nil { + return fmt.Errorf(`%s: %w`, logp, err) + } + + // Load main configuration. var cfg = filepath.Join(opts.configDir, `lilin.cfg`) _, err = os.Stat(cfg) @@ -57,8 +72,8 @@ func (opts *ServerOptions) init() (err error) { return fmt.Errorf(`%s: %w`, logp, err) } - if opts.Address == "" { - return fmt.Errorf(`%s: empty Address`, logp) + if opts.Address == `` { + opts.Address = defAddress } return nil diff --git a/service_report.go b/service_report.go index 291cd2b..7aee321 100644 --- a/service_report.go +++ b/service_report.go @@ -3,6 +3,14 @@ package lilin +import ( + "encoding/csv" + "fmt" + "log" + "os" + "path/filepath" +) + // historyKeepSize maximum report to keep in history. const historyKeepSize = 120 @@ -11,17 +19,44 @@ const historyMax = historyKeepSize * 2 // ServiceReport contains the scan report for single service. type ServiceReport struct { + logf *os.File + writer *csv.Writer + logPath string + Name string Last ScanReport History []ScanReport } -func NewServiceReport(name string) (svcReport *ServiceReport) { +func NewServiceReport(opts ServerOptions, name string) ( + svcReport *ServiceReport, err error, +) { + var logp = `NewServiceReport` + svcReport = &ServiceReport{ + logPath: filepath.Join(opts.logServiceDir, name+`.log`), Name: name, History: make([]ScanReport, 0, historyMax), } - return svcReport + + svcReport.logf, err = os.OpenFile(svcReport.logPath, + os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + return nil, fmt.Errorf(`%s: %w`, logp, err) + } + + svcReport.writer = csv.NewWriter(svcReport.logf) + + return svcReport, nil +} + +// Close release any resources opened by report. +func (svcReport *ServiceReport) Close() (err error) { + err = svcReport.logf.Close() + if err != nil { + return fmt.Errorf(`Close: %w`, err) + } + return nil } func (svcReport *ServiceReport) Store(scanReport ScanReport) { @@ -32,4 +67,9 @@ func (svcReport *ServiceReport) Store(scanReport ScanReport) { copy(svcReport.History, svcReport.History[start:]) svcReport.History = svcReport.History[:historyKeepSize-1] } + var record = scanReport.toCSV() + var err = svcReport.writer.Write(record) + if err != nil { + log.Printf(`Store: %s`, err) + } } diff --git a/service_report_test.go b/service_report_test.go index 71652f6..95fc27d 100644 --- a/service_report_test.go +++ b/service_report_test.go @@ -4,14 +4,31 @@ package lilin import ( + "os" "testing" "time" "git.sr.ht/~shulhan/pakakeh.go/lib/test" ) -func TestServiceReport_push(t *testing.T) { - var svcReport = NewServiceReport(`test`) +func TestServiceReport_Store(t *testing.T) { + var serverOpts = ServerOptions{ + BaseDir: `testdata`, + } + var err = serverOpts.init() + if err != nil { + t.Fatal(err) + } + + var svcReport *ServiceReport + svcReport, err = NewServiceReport(serverOpts, `testStore`) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + _ = os.Remove(svcReport.logPath) + }) + var x int64 for x = 1; x <= historyMax+1; x++ { svcReport.Store(ScanReport{At: time.Unix(x, 0)}) @@ -4,6 +4,7 @@ package lilin import ( + "log" "os" "path/filepath" "strings" @@ -21,12 +22,12 @@ type worker struct { Reports Reports } -func newWorker(configDir string) (wrk *worker, err error) { +func newWorker(opts ServerOptions) (wrk *worker, err error) { wrk = &worker{ Services: make(map[string]*Service), } - err = wrk.loadServiceDir(configDir) + err = wrk.loadServiceDir(opts) if err != nil { return nil, err } @@ -35,7 +36,10 @@ func newWorker(configDir string) (wrk *worker, err error) { Services: make(map[string]*ServiceReport, len(wrk.Services)), } for name := range wrk.Services { - wrk.Reports.Services[name] = NewServiceReport(name) + wrk.Reports.Services[name], err = NewServiceReport(opts, name) + if err != nil { + return nil, err + } } return wrk, nil } @@ -45,11 +49,9 @@ type serviceConfigs struct { } // loadServiceDir Load all the service configurations. -func (wrk *worker) loadServiceDir(configDir string) (err error) { - var serviceDir = filepath.Join(configDir, `service.d`) - +func (wrk *worker) loadServiceDir(opts ServerOptions) (err error) { var listde []os.DirEntry - listde, err = os.ReadDir(serviceDir) + listde, err = os.ReadDir(opts.configServiceDir) if err != nil { return err } @@ -69,7 +71,7 @@ func (wrk *worker) loadServiceDir(configDir string) (err error) { continue } - var serviceCfg = filepath.Join(serviceDir, name) + var serviceCfg = filepath.Join(opts.configServiceDir, name) var rawcfg []byte rawcfg, err = os.ReadFile(serviceCfg) @@ -124,4 +126,13 @@ func (wrk *worker) stop() { for _, svc = range wrk.Services { svc.Stop() } + + var svcReport *ServiceReport + var err error + for _, svcReport = range wrk.Reports.Services { + err = svcReport.Close() + if err != nil { + log.Printf(`worker: stop: %s`, err) + } + } } diff --git a/worker_test.go b/worker_test.go index 46e7cd3..a149b5a 100644 --- a/worker_test.go +++ b/worker_test.go @@ -14,18 +14,24 @@ import ( func TestNewWorker(t *testing.T) { type testCase struct { expServices map[string]*Service - configDir string expError string + serverOpts ServerOptions } var listCase = []testCase{{ - configDir: `testdata/worker/withoutServiceDir/etc/lilin/`, - expError: `open testdata/worker/withoutServiceDir/etc/lilin/service.d: no such file or directory`, + serverOpts: ServerOptions{ + BaseDir: `testdata/worker/withoutServiceDir/`, + }, + expError: `open testdata/worker/withoutServiceDir/etc/lilin/service.d: no such file or directory`, }, { - configDir: `testdata/worker/readError/etc/lilin/`, - expError: `open testdata/worker/readError/etc/lilin/service.d/broken.cfg: no such file or directory`, + serverOpts: ServerOptions{ + BaseDir: `testdata/worker/readError/`, + }, + expError: `open testdata/worker/readError/etc/lilin/service.d/broken.cfg: no such file or directory`, }, { - configDir: `testdata/etc/lilin/`, + serverOpts: ServerOptions{ + BaseDir: `testdata/`, + }, expServices: map[string]*Service{ `example http`: &Service{ opts: ServiceOptions{ @@ -72,7 +78,12 @@ func TestNewWorker(t *testing.T) { }} for _, tcase := range listCase { - wrk, err := newWorker(tcase.configDir) + err := tcase.serverOpts.init() + if err != nil { + t.Fatal(err) + } + + wrk, err := newWorker(tcase.serverOpts) if err != nil { test.Assert(t, ``, tcase.expError, err.Error()) continue |
