Skip to content

Commit

Permalink
log to a file instead of system service runner
Browse files Browse the repository at this point in the history
systemd's makes it easy enough to access service logs, and macOS is fine as well.
But Windows `services.msc` and `eventvwr` GUIs are archiac and a terrible experience
all around.

We're far better off logging everything to a file on disk as much as possible.

Signed-off-by: Stephen Gutekanst <[email protected]>
  • Loading branch information
emidoots committed Dec 22, 2022
1 parent 7c37337 commit d3dbd0a
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 44 deletions.
7 changes: 0 additions & 7 deletions dev/logs.sh

This file was deleted.

41 changes: 34 additions & 7 deletions internal/wrench/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"os"
"os/signal"
"path/filepath"
"strings"
"sync"
"syscall"
"time"

"github.com/bwmarrin/discordgo"
"github.com/google/go-github/v48/github"
Expand All @@ -23,6 +25,7 @@ type Bot struct {
Config *Config

started bool
logFile *os.File
store *Store
github *github.Client
discordSession *discordgo.Session
Expand Down Expand Up @@ -50,8 +53,12 @@ func (b *Bot) logf(format string, v ...any) {
}

func (b *Bot) idLogf(id, format string, v ...any) {
log.Printf(format, v...)
b.store.Log(context.Background(), id, fmt.Sprintf(format, v...))
msg := fmt.Sprintf(format, v...)
if !strings.HasSuffix(msg, "\n") {
msg = msg + "\n"
}
fmt.Fprintf(b.logFile, "%s %s", time.Now().Format(time.RFC3339), msg)
b.store.Log(context.Background(), id, msg)
}

func (b *Bot) idWriter(id string) io.Writer {
Expand All @@ -68,13 +75,19 @@ func (w writerFunc) Write(p []byte) (n int, err error) {
}

func (b *Bot) Start(s service.Service) error {
logger, err := s.Logger(nil)
if err != nil {
return errors.Wrap(err, "Logger")
serviceLogger, _ := s.Logger(nil)
if serviceLogger != nil && !service.Interactive() {
serviceLogger.Info("wrench service started")
}

go func() {
if err := b.run(s); err != nil {
logger.Error(err)
if !service.Interactive() {
b.logf("wrench service: FATAL: %s", err)
if serviceLogger != nil {
serviceLogger.Error("wrench service: FATAL:", err)
}
}
log.Fatal(err)
}
}()
Expand All @@ -89,7 +102,16 @@ func (b *Bot) run(s service.Service) error {
return errors.Wrap(err, "loading config")
}

logFilePath := b.Config.LogFilePath()
var err error
b.logFile, err = os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("creating log file %s", logFilePath))
}
if !service.Interactive() {
b.logf("wrench service: STARTED")
}

b.store, err = OpenStore(filepath.Join(b.Config.WrenchDir, "wrench.db") + "?_pragma=busy_timeout%3d10000")
if err != nil {
return errors.Wrap(err, "OpenStore")
Expand Down Expand Up @@ -118,19 +140,24 @@ func (b *Bot) run(s service.Service) error {
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, syscall.SIGTERM)
<-sc

fmt.Println("Interrupted, shutting down..")
b.logf("Interrupted, shutting down..")

return errors.Wrap(b.stop(), "stop")
}

func (b *Bot) Stop(s service.Service) error {
serviceLogger, _ := s.Logger(nil)
if serviceLogger != nil && !service.Interactive() {
serviceLogger.Info("wrench service stopped")
}
return b.stop()
}

func (b *Bot) stop() error {
if !b.started {
return nil
}
b.logFile.Close()
if err := b.githubStop(); err != nil {
return errors.Wrap(err, "github")
}
Expand Down
11 changes: 4 additions & 7 deletions internal/wrench/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ type Config struct {
WrenchDir string `toml:"WrenchDir,omitempty"`
}

func (c *Config) LogFilePath() string {
return filepath.Join(c.WrenchDir, "logs")
}

func (c *Config) WriteTo(file string) error {
if err := os.MkdirAll(filepath.Dir(file), os.ModePerm); err != nil {
return errors.Wrap(err, "MkdirAll")
Expand All @@ -87,13 +91,6 @@ func (c *Config) WriteTo(file string) error {

func LoadConfig(file string, out *Config) error {
_, err := toml.DecodeFile(file, out)
if errors.Is(err, os.ErrNotExist) {
_, err2 := toml.DecodeFile("../wrench-private/config.toml", out)
if err2 == nil {
return nil
}
return err
}
if err != nil {
return err
}
Expand Down
12 changes: 11 additions & 1 deletion service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"log"
"os"
"os/user"
"path/filepath"
"runtime"
"strings"

Expand All @@ -18,7 +20,7 @@ var serviceCommands cmder.Commander

var (
serviceFlagSet = flag.NewFlagSet("service", flag.ExitOnError)
serviceConfigFile = serviceFlagSet.String("config", "config.toml", "Path to TOML configuration file (see config.go)")
serviceConfigFile = serviceFlagSet.String("config", defaultConfigFilePath(), "Path to TOML configuration file (see config.go)")
)

func init() {
Expand Down Expand Up @@ -62,6 +64,14 @@ Use "wrench service <command> -h" for more information about a command.
})
}

func defaultConfigFilePath() string {
u, err := user.Current()
if err == nil {
return filepath.Join(u.HomeDir, "wrench/config.toml")
}
return "config.toml"
}

func newServiceBot() (service.Service, *wrench.Bot) {
return newServiceBotWithConfig(&ServiceConfig{
ConfigFile: *serviceConfigFile,
Expand Down
39 changes: 17 additions & 22 deletions service_logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package main
import (
"flag"
"fmt"
"runtime"
"os"
"strings"
"time"

"github.com/hexops/cmder"
"github.com/hexops/wrench/internal/errors"
"github.com/hexops/wrench/internal/wrench"
"github.com/nxadm/tail"
"golang.org/x/exp/slices"
)
Expand Down Expand Up @@ -41,22 +42,20 @@ Examples:
return nil
}

grep := map[string]struct{}{}
var files []string
if runtime.GOOS == "darwin" {
files = []string{
"/var/log/com.apple.xpc.launchd/launchd.log",
"/var/log/wrench.out.log",
"/var/log/wrench.err.log",
}
grep["/var/log/com.apple.xpc.launchd/launchd.log"] = struct{}{}
} else {
fmt.Println("'wrench svc logs' not supported on this OS.")
return nil
var cfg wrench.Config
err = wrench.LoadConfig(*serviceConfigFile, &cfg)
if err != nil {
return errors.Wrap(err, "LoadConfig")
}

files := []string{cfg.LogFilePath()}

tails := []*tail.Tail{}
for _, path := range files {
_, err := os.Stat(path)
if err != nil {
fmt.Println("warning:", err)
}
tf, err := tail.TailFile(path, tail.Config{
Follow: *followFlag,
ReOpen: *followFlag,
Expand All @@ -71,8 +70,8 @@ Examples:
return nil
}

start := time.Now()
lastLine := time.Now()
delayed := false
for {
for i, tail := range tails {
select {
Expand All @@ -82,22 +81,18 @@ Examples:
tails = slices.Delete(tails, i, i)
break
}
if _, ok := grep[tail.Filename]; ok {
if !strings.Contains(line.Text, "wrench") {
continue
}
}
if !*showAllFlag {
if !delayed {
split := strings.Split(line.Text, " ")
t, err := time.Parse(time.RFC3339, split[0])
if err == nil && !t.After(start) {
continue
}
}
fmt.Println(tail.Filename + ": " + line.Text)
fmt.Println(line.Text)
default:
}
}
if time.Since(lastLine) > 1*time.Second {
delayed = true
time.Sleep(100 * time.Millisecond)
}
}
Expand Down
5 changes: 5 additions & 0 deletions service_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ Examples:
fmt.Println("services: services.msc")
fmt.Println("logs: eventvwr")
}
var cfg wrench.Config
err = wrench.LoadConfig(*serviceConfigFile, &cfg)
if err == nil {
fmt.Println("wrench logs:", cfg.LogFilePath())
}
fmt.Println("")
fmt.Println(status)
return nil
Expand Down

0 comments on commit d3dbd0a

Please sign in to comment.