This commit is contained in:
Suyono 2023-09-03 12:11:35 +10:00
parent 91147f073f
commit badd2e3543
10 changed files with 343 additions and 49 deletions

View File

@ -9,15 +9,16 @@ import (
)
const (
envPrefix = "wingmate"
configPathKey = "config_path"
configSearchPathKey = "config_search_path"
EnvPrefix = "wingmate"
PathKey = "config_path"
SearchPathKey = "config_search_path"
defaultPath = "/etc/wingmate/"
defaultName = "config"
ServiceKey = "service"
CronKey = "cron"
CommandKey = "command"
)
var (
@ -27,9 +28,9 @@ var (
)
func init() {
viper.SetEnvPrefix(envPrefix)
_ = viper.BindEnv(configPathKey)
_ = viper.BindEnv(configSearchPathKey)
viper.SetEnvPrefix(EnvPrefix)
_ = viper.BindEnv(PathKey)
_ = viper.BindEnv(SearchPathKey)
}
func BindFlags(command *cobra.Command) {
@ -44,13 +45,13 @@ func Read(cmd *cobra.Command, args []string) error {
_, _ = cmd, args // prevent warning for unused arguments
if viper.IsSet(configPathKey) {
configPath = viper.GetString(configPathKey)
if viper.IsSet(PathKey) {
configPath = viper.GetString(PathKey)
}
if viper.IsSet(configSearchPathKey) {
if err = viper.UnmarshalKey(configSearchPathKey, &configSearchPath); err != nil {
return fmt.Errorf("reading %s: %w %w", configSearchPathKey, err, debugframes.GetTraces())
if viper.IsSet(SearchPathKey) {
if err = viper.UnmarshalKey(SearchPathKey, &configSearchPath); err != nil {
return fmt.Errorf("reading %s: %w %w", SearchPathKey, err, debugframes.GetTraces())
}
}

View File

@ -32,8 +32,8 @@ func TestRead(t *testing.T) {
{
name: "env",
env: map[string]string{
strings.ToUpper(envPrefix + "_" + configPathKey): "/path/to/config",
strings.ToUpper(envPrefix + "_" + configSearchPathKey): "/path/one,/path/two",
strings.ToUpper(EnvPrefix + "_" + PathKey): "/path/to/config",
strings.ToUpper(EnvPrefix + "_" + SearchPathKey): "/path/one,/path/two",
},
args: testReadArgs{
nil,
@ -66,7 +66,7 @@ func TestRead(t *testing.T) {
}
tc.env = map[string]string{
strings.ToUpper(envPrefix + "_" + configPathKey): fname,
strings.ToUpper(EnvPrefix + "_" + PathKey): fname,
}
tc.post = func(t *testing.T, tc *testRead) {
@ -142,7 +142,7 @@ func TestGet(t *testing.T) {
}
tc.env = map[string]string{
strings.ToUpper(envPrefix + "_" + configPathKey): fname,
strings.ToUpper(EnvPrefix + "_" + PathKey): fname,
}
tc.post = func(t *testing.T, tc *testRead) {

View File

@ -1,10 +1,13 @@
package daemon
import (
"fmt"
"gitea.suyono.dev/suyono/wingmate/config"
"gitea.suyono.dev/suyono/wingmate/debugframes"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os/exec"
"runtime/debug"
)
type InstanceType int
@ -23,10 +26,11 @@ type process struct {
}
type daemon struct {
running map[string]process
available map[string]process
services map[string]WingMateDescriptor
cron map[string]WingMateDescriptor
running map[string]process
services map[string]*service
cron map[string]WingMateDescriptor
logChannel chan<- any
errorChannel chan<- error
}
const (
@ -45,27 +49,61 @@ func (t InstanceType) String() string {
}
}
func Start(cmd *cobra.Command, args []string) error {
var (
err error
)
func Start(cmd *cobra.Command, args []string) (err error) {
defer func() {
if o := recover(); o != nil {
if err != nil {
err = fmt.Errorf("panic: %v %w and error: %w", o, debugframes.PanicTrace(debug.Stack()), err)
} else {
err = fmt.Errorf("panic: %v %w", o, debugframes.PanicTrace(debug.Stack()))
}
}
}()
_, _ = cmd, args // prevent warnings for unused arguments
_, err = start()
return
}
func start() (*daemon, error) {
var (
err error
k string
svc *service
cmd *exec.Cmd
)
d := &daemon{
running: make(map[string]process),
available: make(map[string]process),
services: make(map[string]WingMateDescriptor),
cron: make(map[string]WingMateDescriptor),
running: make(map[string]process),
services: make(map[string]*service),
cron: make(map[string]WingMateDescriptor),
logChannel: make(chan<- any, 16),
errorChannel: make(chan<- error, 4),
}
if err = d.buildServiceMap(); err != nil {
return err
return nil, err
}
return nil
for k, svc = range d.services {
if cmd, err = StartProcess(svc); err != nil {
return nil, err //TODO: this is not supposed to return, log and start the next service
}
d.running[k] = process{
cmd: cmd,
descriptor: svc,
}
}
//TODO: create loop to receive and process log
//TODO: create loop to receive error
//TODO: create signal handler
return d, nil
}
func (d *daemon) buildServiceMap() error {
for s := range viper.GetStringMap(Service.String()) {
_ = s //TODO: resume work
d.services[s] = newService(s, d.errorChannel, d.logChannel)
}
return nil
}

View File

@ -7,27 +7,28 @@ import (
"io"
"os"
"os/exec"
"runtime/debug"
)
type ProcessConfig interface {
Descriptor() WingMateDescriptor
// Name returns the binary name to be executed, to be passed in to exec.Command
Name() string
// ExecutableName returns the executable/binary name to be executed, to be passed in to exec.Command
ExecutableName() string
// Args returns the arguments for the process, to be passed in to exec.Command
Args() []string
Env() map[string]string
WorkingDir() string
LogChannel() chan<- any
ControlChannel() chan<- any
ErrorChannel() chan<- error
}
type StreamID int
type ProcessLogEntry struct {
ID WingMateDescriptor
LogEntry string
Descriptor WingMateDescriptor
LogEntry string
}
const (
@ -53,17 +54,19 @@ func StartProcess(config ProcessConfig) (*exec.Cmd, error) {
k, v string
stdoutPipe, stderrPipe io.ReadCloser
)
cmd = exec.Command(config.Name(), config.Args()...)
cmd = exec.Command(config.ExecutableName(), config.Args()...)
cmd.Env = os.Environ()
for k, v = range config.Env() {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
}
cmd.Dir = config.WorkingDir()
if stdoutPipe, err = cmd.StdoutPipe(); err != nil {
return nil, fmt.Errorf("set up stdout pipe on process %w: %w %w", config.Descriptor(), err, debugframes.GetTraces())
return nil, fmt.Errorf("set up stdout pipe on process %w: %w %w",
config.Descriptor(), err, debugframes.GetTraces())
}
if stderrPipe, err = cmd.StderrPipe(); err != nil {
return nil, fmt.Errorf("set up stderr pipe on process %w: %w %w", config.Descriptor(), err, debugframes.GetTraces())
return nil, fmt.Errorf("set up stderr pipe on process %w: %w %w",
config.Descriptor(), err, debugframes.GetTraces())
}
if err = cmd.Start(); err != nil {
@ -80,38 +83,41 @@ func StartProcess(config ProcessConfig) (*exec.Cmd, error) {
func serviceLogReader(config ProcessConfig, stream io.Reader, id StreamID) {
out := config.LogChannel()
eChan := config.ErrorChannel()
defer func() {
if o := recover(); o != nil {
out <- o
//TODO: include stack trace, make the return object an error
eChan <- fmt.Errorf("log reader %s of %w panic: %v %w",
id.String(), config.Descriptor(), o, debugframes.PanicTrace(debug.Stack()))
} else {
eChan <- fmt.Errorf("log reader %s of %w returned", id.String(), config.Descriptor())
}
}()
scanner := bufio.NewScanner(stream)
for scanner.Scan() {
out <- ProcessLogEntry{
ID: config.Descriptor(),
LogEntry: scanner.Text(),
Descriptor: config.Descriptor(),
LogEntry: scanner.Text(),
}
}
if err := scanner.Err(); err != nil {
out <- fmt.Errorf("read stream from %w: %w %w", config.Descriptor(), err, debugframes.GetTraces())
eChan <- fmt.Errorf("read %s from %w: %w %w",
id.String(), config.Descriptor(), err, debugframes.GetTraces())
}
}
func serviceWaiter(config ProcessConfig, cmd *exec.Cmd) {
ctrl := config.ControlChannel()
eChan := config.ErrorChannel()
defer func() {
if o := recover(); o != nil {
ctrl <- o
//TODO: include stack trace
eChan <- fmt.Errorf("%w panic: %v %w", config.Descriptor(), o, debugframes.PanicTrace(debug.Stack()))
}
}()
if err := cmd.Wait(); err != nil {
ctrl <- fmt.Errorf("wait error on service %w: %w", config.Descriptor(), err)
eChan <- fmt.Errorf("wait error on service %w: %w", config.Descriptor(), err)
} else {
ctrl <- config.Descriptor()
eChan <- config.Descriptor()
}
}

86
daemon/service.go Normal file
View File

@ -0,0 +1,86 @@
package daemon
import (
"fmt"
"gitea.suyono.dev/suyono/wingmate/config"
"github.com/spf13/viper"
)
type service struct {
name string
logChannel chan<- any
errChannel chan<- error
}
func newService(name string, error chan<- error, logging chan<- any) *service {
return &service{
name: name,
logChannel: logging,
errChannel: error,
}
}
//TODO: review back the implementation of service as WingMateDescriptor
func (s *service) String() string {
return s.name
}
func (s *service) Name() string {
return s.name
}
func (s *service) InstanceName() string {
return s.name
}
func (s *service) InstanceType() InstanceType {
return Service
}
func (s *service) Error() string {
return s.name
}
func (s *service) parseCommand() []string {
key := fmt.Sprintf("%s.%s.%s", Service.String(), s.name, config.CommandKey)
if !viper.IsSet(key) {
panic(fmt.Errorf("parse command: key %s is not set", key))
}
result := viper.GetStringSlice(key)
if len(result) < 1 {
panic(fmt.Errorf("parse command: zero command length"))
}
return result
}
func (s *service) ExecutableName() string {
return s.parseCommand()[0]
}
func (s *service) Args() []string {
return s.parseCommand()[1:]
}
func (s *service) Descriptor() WingMateDescriptor {
return s
}
func (s *service) Env() map[string]string {
//TODO: stub implementation; FIX!
return make(map[string]string)
}
func (s *service) WorkingDir() string {
//TODO: stub implementation; FIX!
return ""
}
func (s *service) LogChannel() chan<- any {
return s.logChannel
}
func (s *service) ErrorChannel() chan<- error {
return s.errChannel
}

82
daemon/service_test.go Normal file
View File

@ -0,0 +1,82 @@
package daemon
import (
"gitea.suyono.dev/suyono/wingmate/config"
"gitea.suyono.dev/suyono/wingmate/files/testconfig"
"github.com/spf13/viper"
"os"
"strings"
"testing"
)
type parseCommandTestCase struct {
name string
pre func(t *testing.T, tt *parseCommandTestCase)
post func(t *testing.T, tt *parseCommandTestCase)
}
func TestParseCommand(t *testing.T) {
tests := []parseCommandTestCase{
{
name: "initial",
pre: func(t *testing.T, tt *parseCommandTestCase) {
var (
f *os.File
err error
fname, key string
)
if f, err = os.CreateTemp("", "config-*.yml"); err != nil {
t.Fatal("create temp:", err)
}
fname = f.Name()
if _, err = f.WriteString(testconfig.One); err != nil {
t.Fatal("writing temp:", err)
}
if err = f.Close(); err != nil {
t.Fatal("closing temp:", err)
}
key = strings.ToUpper(config.EnvPrefix + "_" + config.PathKey)
if err = os.Setenv(key, fname); err != nil {
t.Fatal("set up env failed", err)
}
tt.post = func(t *testing.T, tt *parseCommandTestCase) {
_ = os.Unsetenv(key)
_ = os.Remove(fname)
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var (
err error
d *daemon
s *service
)
if tt.pre != nil {
tt.pre(t, &tt)
}
if tt.post != nil {
defer tt.post(t, &tt)
}
if err = config.Read(nil, []string{}); err != nil {
t.Fatal("reading config", err)
}
if d, err = start(); err != nil {
t.Fatal("starting daemon", err)
}
for _, s = range d.services {
s.parseCommand()
}
x := viper.GetStringSlice("non.existent")
t.Log(x)
})
}
}

7
debugframes/panic.go Normal file
View File

@ -0,0 +1,7 @@
package debugframes
type PanicTrace []byte
func (p PanicTrace) Error() string {
return "[panic trace available]"
}

5
go.mod
View File

@ -6,7 +6,7 @@ require (
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.4
golang.org/x/sys v0.11.0
golang.org/x/sys v0.12.0
)
require (
@ -15,9 +15,12 @@ require (
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rs/zerolog v1.30.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect

18
go.sum
View File

@ -46,6 +46,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -62,6 +63,7 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -136,6 +138,13 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
@ -147,6 +156,9 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
@ -309,9 +321,15 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

53
log/log.go Normal file
View File

@ -0,0 +1,53 @@
package log
import (
"gitea.suyono.dev/suyono/wingmate/daemon"
"github.com/rs/zerolog"
"os"
)
type Level zerolog.Level
const (
Trace Level = Level(zerolog.TraceLevel)
Debug Level = Level(zerolog.DebugLevel)
Info Level = Level(zerolog.InfoLevel)
Warn Level = Level(zerolog.WarnLevel)
Error Level = Level(zerolog.ErrorLevel)
Fatal Level = Level(zerolog.FatalLevel)
Panic Level = Level(zerolog.PanicLevel)
)
var (
globalLevel Level = Error
logger zerolog.Logger
)
func init() {
SetGlobalLevel(globalLevel)
logger = zerolog.New(os.Stderr)
}
func (l Level) Print(a ...any) {
event := logger.WithLevel(zerolog.Level(l))
for _, i := range a {
switch v := i.(type) {
case daemon.ProcessLogEntry:
event = event.Str("type", v.Descriptor.InstanceType().String()).Str("origin", "child process").
Str("name", v.Descriptor.Name())
if v.Descriptor.InstanceType() == daemon.Cron {
event = event.Str("instance_name", v.Descriptor.InstanceName())
}
event.Str("entry", v.LogEntry).Send()
}
}
}
func Print(a ...any) {
globalLevel.Print(a...)
}
func SetGlobalLevel(l Level) {
globalLevel = l
zerolog.SetGlobalLevel(zerolog.Level(globalLevel))
}