Skip to content

Commit

Permalink
add query logging as an option
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanfaerman committed Feb 12, 2024
1 parent 59adfc9 commit 8416924
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 101 deletions.
32 changes: 32 additions & 0 deletions cmd/netctl/logadapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main

import (
"context"

"github.com/charmbracelet/log"
sqldblogger "github.com/simukti/sqldb-logger"
)

type logadapter struct {
logger *log.Logger
}

func (l logadapter) Log(_ context.Context, level sqldblogger.Level, msg string, data map[string]interface{}) {
keyvals := []interface{}{}
for k, v := range data {
keyvals = append(keyvals, k, v)
}

switch level {
case sqldblogger.LevelError:
l.logger.Error(msg, keyvals...)
case sqldblogger.LevelInfo:
l.logger.Info(msg, keyvals...)
case sqldblogger.LevelDebug:
l.logger.Debug(msg, keyvals...)
case sqldblogger.LevelTrace:
l.logger.Debug(msg, keyvals...)
default:
l.logger.Debug(msg, keyvals)
}
}
201 changes: 107 additions & 94 deletions cmd/netctl/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,109 +15,122 @@ import (
"github.com/ryanfaerman/netctl/internal/models"
"github.com/ryanfaerman/netctl/internal/services"
"github.com/ryanfaerman/netctl/web"
sqldblogger "github.com/simukti/sqldb-logger"
"github.com/spf13/cobra"

_ "modernc.org/sqlite"
)

var cmdService = &cobra.Command{
Use: "service",
Args: cobra.NoArgs,
RunE: func(_ *cobra.Command, _ []string) error {
userCacheDir, err := os.UserCacheDir()
if err != nil {
panic(err.Error())
}
dbPath := filepath.Join(userCacheDir, "netctl", "netctl.db")
logger.Debug("running initial setup", "cachedir", userCacheDir, "db-path", dbPath)

if err := os.MkdirAll(filepath.Dir(dbPath), 0750); err != nil {
return err
}

db, err := sql.Open("sqlite", dbPath+"?_pragma=journal_mode(WAL)&_pragma=foreign_keys(on)")
if err != nil {
return err
}

if err := models.Setup(logger, db); err != nil {
return err
}

if err := services.Setup(logger, db); err != nil {
return err
}

// TODO: Add --skip-recovery flag
// TODO: perform recovery
services.Event.StartRecoveryService(30 * time.Second) // TODO: make this configurable

if err := handlers.Setup(logger, db); err != nil {
return err
}

s, err := web.NewServer(web.WithLogger(logger))
if err != nil {
return err
}

l, err := net.Listen("tcp4", webAddr)
if err != nil {
return errors.Join(err, bindErr)
}
defer l.Close()

if err := s.Start(l); err != nil {
logger.Error("could not start", "err", err)
return err
}

signalCh := make(chan os.Signal, 10)
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGPIPE)

for {
sig := <-signalCh

switch sig {
case syscall.SIGHUP:
logger.Info("caught signal reloading", "signal", sig)

if err := s.Restart(); err != nil {
logger.Error("reloading failed", "err", err)
}
var (
dbLog = false
cmdService = &cobra.Command{
Use: "service",
Args: cobra.NoArgs,
RunE: func(_ *cobra.Command, _ []string) error {
userCacheDir, err := os.UserCacheDir()
if err != nil {
panic(err.Error())
}
dbPath := filepath.Join(userCacheDir, "netctl", "netctl.db")
logger.Debug("running initial setup", "cachedir", userCacheDir, "db-path", dbPath)

if err := os.MkdirAll(filepath.Dir(dbPath), 0750); err != nil {
return err
}

dsn := dbPath + "?_pragma=journal_mode(WAL)&_pragma=foreign_keys(on)"
db, err := sql.Open("sqlite", dsn)
if err != nil {
return err
}
if dbLog {
loggerAdapter := logadapter{logger}
db = sqldblogger.OpenDriver(dsn, db.Driver(), loggerAdapter)
}

if err := models.Setup(logger, db); err != nil {
return err
}

if err := services.Setup(logger, db); err != nil {
return err
}

// TODO: Add --skip-recovery flag
// TODO: perform recovery
services.Event.StartRecoveryService(30 * time.Second) // TODO: make this configurable

if err := handlers.Setup(logger, db); err != nil {
return err
}

s, err := web.NewServer(web.WithLogger(logger))
if err != nil {
return err
}

l, err := net.Listen("tcp4", webAddr)
if err != nil {
return errors.Join(err, bindErr)
}
defer l.Close()

if err := s.Start(l); err != nil {
logger.Error("could not start", "err", err)
return err
}

logger.Info("reload complete")
default:
logger.Info("gracefully shutting down", "signal", sig)
gracefulCh := make(chan struct{})
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := s.Shutdown(ctx); err != nil {
logger.Error("unable to stop!", "err", err)
return
signalCh := make(chan os.Signal, 10)
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGPIPE)

for {
sig := <-signalCh

switch sig {
case syscall.SIGHUP:
logger.Info("caught signal reloading", "signal", sig)

if err := s.Restart(); err != nil {
logger.Error("reloading failed", "err", err)
}

services.Event.StopRecoveryService()

close(gracefulCh)
}()

gracefulTimeout := 15 * time.Second
select {
case <-signalCh:
logger.Info("caught second signal. Exiting", "signal", sig)
os.Exit(1)
case <-time.After(gracefulTimeout):
logger.Error("graceful shutdown timed out. Exiting")
os.Exit(1)
case <-gracefulCh:
logger.Info("graceful exit complete")
os.Exit(0)
logger.Info("reload complete")
default:
logger.Info("gracefully shutting down", "signal", sig)
gracefulCh := make(chan struct{})
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := s.Shutdown(ctx); err != nil {
logger.Error("unable to stop!", "err", err)
return
}

services.Event.StopRecoveryService()

close(gracefulCh)
}()

gracefulTimeout := 15 * time.Second
select {
case <-signalCh:
logger.Info("caught second signal. Exiting", "signal", sig)
os.Exit(1)
case <-time.After(gracefulTimeout):
logger.Error("graceful shutdown timed out. Exiting")
os.Exit(1)
case <-gracefulCh:
logger.Info("graceful exit complete")
os.Exit(0)
}
}
}
}

return nil
},
return nil
},
}
)

func init() {
cmdService.PersistentFlags().BoolVar(&dbLog, "log-queries", dbLog, "enable the query log")
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ require (
github.com/r3labs/sse/v2 v2.10.0
github.com/rubyist/circuitbreaker v2.2.1+incompatible
github.com/ryanfaerman/version v0.0.1
github.com/simukti/sqldb-logger v0.0.0-20230108155151-646c1a075551
github.com/spf13/cobra v1.8.0
github.com/sqlc-dev/sqlc v1.25.0
github.com/unrolled/render v1.6.0
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08O
github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/simukti/sqldb-logger v0.0.0-20230108155151-646c1a075551 h1:+EXKKt7RC4HyE/iE8zSeFL+7YBL8Z7vpBaEE3c7lCnk=
github.com/simukti/sqldb-logger v0.0.0-20230108155151-646c1a075551/go.mod h1:ztTX0ctjRZ1wn9OXrzhonvNmv43yjFUXJYJR95JQAJE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sosodev/duration v1.2.0 h1:pqK/FLSjsAADWY74SyWDCjOcd5l7H8GSnnOGEB9A1Us=
Expand All @@ -299,6 +301,7 @@ github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AV
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
Expand Down
5 changes: 5 additions & 0 deletions internal/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package handlers

import (
"database/sql"
"net/http"
"sync"

"github.com/charmbracelet/log"
"github.com/davecgh/go-spew/spew"
"github.com/go-chi/chi"
"github.com/go-playground/form"
sse "github.com/r3labs/sse/v2"
Expand Down Expand Up @@ -58,6 +60,9 @@ func Setup(logger *log.Logger, db *sql.DB) error {
web.HookServerRoutes.Register(func(e hook.Event[web.Router]) {
for _, h := range global.handlers {
e.Payload.Routes().Group(h.Routes)
e.Payload.Routes().NotFound(func(w http.ResponseWriter, r *http.Request) {
spew.Dump(r)
})
}
})

Expand Down
15 changes: 8 additions & 7 deletions internal/models/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import (
)

type Email struct {
ID int64
Address string

CreatedAt time.Time
UpdatedAt time.Time
DeletedAt time.Time

Address string
IsPrimary bool
IsPublic bool
IsNotifiable bool
VerifiedAt time.Time
IsVerified bool

CreatedAt time.Time
UpdatedAt time.Time
DeletedAt time.Time

VerifiedAt time.Time
ID int64
}
41 changes: 41 additions & 0 deletions internal/views/settings.templ
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import "github.com/ryanfaerman/netctl/internal/models"
import "github.com/ryanfaerman/netctl/web/named"

import "strings"
import "fmt"

type Settings struct {
Account *models.Account
Memberships []*models.Membership
Emails []*models.Email
Delegated bool
}

Expand Down Expand Up @@ -125,6 +127,8 @@ templ (v Settings) ShowWithErrors(section string, settings any, settingsErrs map
@v.MembershipOverview(models.AccountKindClub)
case "organizations":
@v.MembershipOverview(models.AccountKindOrganization)
case "emails":
@v.EmailsForm()
}
</div>
<div>
Expand All @@ -142,6 +146,8 @@ templ (v Settings) ShowWithErrors(section string, settings any, settingsErrs map
@v.MembershipContext(models.AccountKindOrganization)
case "clubs":
@v.MembershipContext(models.AccountKindClub)
case "emails":
@v.EmailsContext()
}
</div>
</div>
Expand Down Expand Up @@ -292,3 +298,38 @@ templ (v Settings) MembershipContext(kind models.AccountKind) {
Create a { kind.String() }
</a>
}

func DaEmails(account *models.Account) []models.Email {
emails, err := account.Emails()
if err != nil {
fmt.Println(err.Error())
panic("oh noes... no emails")
}
return emails
}

templ (v Settings) EmailsForm() {
<h3>Emails</h3>
<div class="emails">
for _, e := range DaEmails(v.Account) {
<div class="email">
<div class="email-address">
{ e.Address }
</div>
<div class="email-actions">
<a href="#" class="button danger">Remove</a>
</div>
</div>
}
@InputEmail("email", InputAttrs{
Label: "Add Email",
Placeholder: "Add an email address",
})
</div>
}

templ (v Settings) EmailsContext() {
<p>
Emails are used to send you notifications and other important information.
</p>
}

0 comments on commit 8416924

Please sign in to comment.