Skip to content

Commit

Permalink
feat: pass slog.Logger calls to windows event log in daemon
Browse files Browse the repository at this point in the history
  • Loading branch information
tinyzimmer committed Nov 7, 2023
1 parent 0a8e7c2 commit f7c81fe
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 22 deletions.
37 changes: 19 additions & 18 deletions cmd/webmeshd/main_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,47 +29,47 @@ import (
"golang.org/x/sys/windows/svc/eventlog"

"github.com/webmeshproj/webmesh/pkg/cmd/daemoncmd"
"github.com/webmeshproj/webmesh/pkg/logging"
)

const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown

var elog debug.Log

func run() {
var err error
elog, err = eventlog.Open("webmeshd")
elog, err := eventlog.Open("webmeshd")
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to open event log:", err)
return
}
defer elog.Close()
logInfo("Starting webmesh daemon helper")
err = svc.Run("webmeshd", &helperDaemon{})
logInfo(elog, "Starting webmesh daemon helper")
err = svc.Run("webmeshd", &helperDaemon{elog})
if err != nil {
logError("Failed to start helper daemon", err)
logError(elog, "Failed to start helper daemon", err)
return
}
logInfo("Webmesh daemon helper stopped")
logInfo(elog, "Webmesh daemon helper stopped")
}

func logError(msg string, err error) {
func logError(elog debug.Log, msg string, err error) {
msg = fmt.Sprintf("%s: %v", msg, err)
fmt.Fprintln(os.Stderr, msg)
if elog != nil {
elog.Error(1, msg)
}
}

func logInfo(msg string) {
func logInfo(elog debug.Log, msg string) {
fmt.Fprintln(os.Stdout, msg)
if elog != nil {
elog.Info(1, msg)
}
}

type helperDaemon struct{}
type helperDaemon struct {
elog debug.Log
}

func (d *helperDaemon) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown

changes <- svc.Status{State: svc.StartPending}
config := daemoncmd.NewDefaultConfig()
config.Enabled = true
Expand All @@ -78,15 +78,16 @@ func (d *helperDaemon) Execute(args []string, r <-chan svc.ChangeRequest, change
config.Bind = "127.0.0.1:58080"
config.KeyFile = `C:\ProgramData\Webmesh\key`
config.Persistence.Path = `C:\ProgramData\Webmesh`
elog.Info(1, fmt.Sprintf("Starting webmesh daemon with config: %+v", config))
config.Logger = logging.NewServiceLogAdapter(d.elog, "info")
logInfo(d.elog, fmt.Sprintf("Starting webmesh daemon with config: %+v", config))
var wg sync.WaitGroup
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
errs := make(chan error, 1)
wg.Add(1)
go func() {
defer wg.Done()
logInfo("Starting webmesh daemon")
logInfo(d.elog, "Starting webmesh daemon")
if err := daemoncmd.Run(ctx, *config); err != nil {
errs <- err
}
Expand All @@ -97,19 +98,19 @@ EventLoop:
for {
select {
case err := <-errs:
logError("Daemon exited with error", err)
logError(d.elog, "Daemon exited with error", err)
errno = 2
return
case c := <-r:
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
logInfo("Received stop request, shutting down")
logInfo(d.elog, "Received stop request, shutting down")
cancel()
break EventLoop
default:
logError("Unexpected service control request", fmt.Errorf("cmd=%d", c.Cmd))
logError(d.elog, "Unexpected service control request", fmt.Errorf("cmd=%d", c.Cmd))
}
}
}
Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/daemoncmd/connmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ func (m *ConnManager) NewConn(ctx context.Context, req *v1.ConnectRequest) (id s
node, err = embed.NewNode(ctx, embed.Options{
Config: cfg,
Key: m.key,
Logger: m.log.With("connection-id", connID),
})
if err != nil {
return "", nil, status.Errorf(codes.Internal, "failed to create node: %v", err)
Expand Down
13 changes: 9 additions & 4 deletions pkg/embed/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ type Options struct {
Key crypto.PrivateKey
// Host is the libp2p host for the node.
Host libp2p.Host
// Logger is the logger for the node.
Logger *slog.Logger
}

// NewNode creates a new embedded webmesh node.
Expand All @@ -82,11 +84,14 @@ func NewNode(ctx context.Context, opts Options) (Node, error) {
if config.Mesh.DisableIPv4 && config.Mesh.DisableIPv6 {
return nil, fmt.Errorf("cannot disable both IPv4 and IPv6")
}
log := logging.SetupLogging(config.Global.LogLevel, config.Global.LogFormat)
if config.Global.LogLevel == "" || config.Global.LogLevel == "silent" {
log = slog.New(slog.NewTextHandler(io.Discard, nil))
ctx = context.WithLogger(ctx, log)
log := opts.Logger
if log == nil {
log = logging.SetupLogging(config.Global.LogLevel, config.Global.LogFormat)
if config.Global.LogLevel == "" || config.Global.LogLevel == "silent" {
log = slog.New(slog.NewTextHandler(io.Discard, nil))
}
}
ctx = context.WithLogger(ctx, log)
// Create a new mesh connection
meshConfig, err := config.NewMeshConfig(ctx, opts.Key)
if err != nil {
Expand Down
97 changes: 97 additions & 0 deletions pkg/logging/service_logger_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
Copyright 2023 Avi Zimmerman <[email protected]>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package logging

import (
"context"
"fmt"
"log/slog"
"strings"

"golang.org/x/sys/windows/svc/debug"
)

func NewServiceLogAdapter(l debug.Log, logLevel string) *slog.Logger {
return slog.New(&ServiceLogHandler{
log: l,
logLevel: logLevel,
})
}

type ServiceLogHandler struct {
log debug.Log
group string
attrs []slog.Attr
logLevel string
}

func (s *ServiceLogHandler) Enabled(ctx context.Context, lvl slog.Level) bool {
current := func() slog.Level {
switch strings.ToLower(s.logLevel) {
case "debug":
return slog.LevelDebug
case "info":
return slog.LevelInfo
case "warn":
return slog.LevelWarn
case "error":
return slog.LevelError
default:
return slog.LevelInfo
}
}()
return lvl >= current
}

func (s *ServiceLogHandler) Handle(ctx context.Context, record slog.Record) error {
msg := fmt.Sprintf("[%s]\t%s\t%s", record.Time.String(), record.Level.String(), record.Message)
if s.group != "" {
msg = s.group + ": " + msg
}
for _, attr := range s.attrs {
msg += " " + attr.Key + "=" + attr.Value.String()
}
switch record.Level {
case slog.LevelDebug:
s.log.Info(1, msg)
case slog.LevelInfo:
s.log.Info(1, msg)
case slog.LevelWarn:
s.log.Warning(1, msg)
case slog.LevelError:
s.log.Error(1, msg)
}
return nil
}

func (s *ServiceLogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return &ServiceLogHandler{
log: s.log,
group: s.group,
attrs: append(s.attrs, attrs...),
logLevel: s.logLevel,
}
}

func (s *ServiceLogHandler) WithGroup(name string) slog.Handler {
return &ServiceLogHandler{
log: s.log,
group: name,
attrs: s.attrs,
logLevel: s.logLevel,
}
}

0 comments on commit f7c81fe

Please sign in to comment.