diff --git a/internal/config/state.go b/internal/config/state.go index 437f8394..bd899185 100644 --- a/internal/config/state.go +++ b/internal/config/state.go @@ -7,9 +7,12 @@ import ( "net/http" "os" "strconv" + "strings" "sync" + "github.com/goccy/go-yaml" "github.com/puzpuzpuz/xsync/v4" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/yusing/godoxy/agent/pkg/agent" "github.com/yusing/godoxy/internal/acl" @@ -21,6 +24,7 @@ import ( "github.com/yusing/godoxy/internal/maxmind" "github.com/yusing/godoxy/internal/notif" route "github.com/yusing/godoxy/internal/route/provider" + "github.com/yusing/godoxy/internal/route/routes" "github.com/yusing/godoxy/internal/serialization" "github.com/yusing/godoxy/internal/types" gperr "github.com/yusing/goutils/errs" @@ -238,6 +242,15 @@ func (state *state) storeProvider(p types.RouteProvider) { } func (state *state) loadRouteProviders() error { + // disable pool logging temporary since we will have pretty logging below + routes.HTTP.ToggleLog(false) + routes.Stream.ToggleLog(false) + + defer func() { + routes.HTTP.ToggleLog(true) + routes.Stream.ToggleLog(true) + }() + providers := &state.Providers errs := gperr.NewBuilderWithConcurrency("route provider errors") results := gperr.NewBuilder("loaded route providers") @@ -316,9 +329,68 @@ func (state *state) loadRouteProviders() error { providersLoader.Wait() log.Info().Msg(results.String()) + + state.printRoutesByProvider(lenLongestName) + state.printState() return errs.Error() } +func (state *state) printRoutesByProvider(lenLongestName int) { + var routeResults strings.Builder + routeResults.Grow(4096) // more than enough + routeResults.WriteString("routes by provider\n") + + lenLongestName += 2 // > + space + for _, p := range state.providers.Range { + providerName := p.String() + routeCount := p.NumRoutes() + + // Print provider header + fmt.Fprintf(&routeResults, "> %-"+strconv.Itoa(lenLongestName)+"s %d routes:\n", providerName, routeCount) + + if routeCount == 0 { + continue + } + + // calculate longest name + for alias, r := range p.IterRoutes { + if r.ShouldExclude() { + continue + } + displayName := r.DisplayName() + if displayName != alias { + displayName = fmt.Sprintf("%s (%s)", displayName, alias) + } + if len(displayName)+3 > lenLongestName { // 3 spaces + "-" + lenLongestName = len(displayName) + 3 + } + } + + for alias, r := range p.IterRoutes { + if r.ShouldExclude() { + continue + } + displayName := r.DisplayName() + if displayName != alias { + displayName = fmt.Sprintf("%s (%s)", displayName, alias) + } + fmt.Fprintf(&routeResults, " - %-"+strconv.Itoa(lenLongestName-2)+"s -> %s\n", displayName, r.TargetURL().String()) + } + } + + // Always print the routes since we want to show even empty providers + routeStr := routeResults.String() + if routeStr != "" { + log.Info().Msg(routeStr) + } +} + +func (state *state) printState() { + log.Info().Msg("active config") + l := log.Level(zerolog.InfoLevel) + yaml.NewEncoder(l).Encode(state.Config) +} + func (state *state) startRouteProviders() error { errs := gperr.NewGroup("provider errors") for _, p := range state.providers.Range { diff --git a/internal/utils/pool/pool.go b/internal/utils/pool/pool.go index a029af94..2e086461 100644 --- a/internal/utils/pool/pool.go +++ b/internal/utils/pool/pool.go @@ -2,6 +2,7 @@ package pool import ( "sort" + "sync/atomic" "github.com/puzpuzpuz/xsync/v4" "github.com/rs/zerolog/log" @@ -11,20 +12,24 @@ type ( Pool[T Object] struct { m *xsync.Map[string, T] name string - disableLog bool + disableLog atomic.Bool } Object interface { Key() string Name() string } + ObjectWithDisplayName interface { + Object + DisplayName() string + } ) func New[T Object](name string) Pool[T] { - return Pool[T]{xsync.NewMap[string, T](), name, false} + return Pool[T]{m: xsync.NewMap[string, T](), name: name} } -func (p *Pool[T]) DisableLog() { - p.disableLog = true +func (p *Pool[T]) ToggleLog(v bool) { + p.disableLog.Store(v) } func (p *Pool[T]) Name() string { @@ -34,35 +39,31 @@ func (p *Pool[T]) Name() string { func (p *Pool[T]) Add(obj T) { p.checkExists(obj.Key()) p.m.Store(obj.Key(), obj) - if !p.disableLog { - log.Info().Msgf("%s: added %s", p.name, obj.Name()) - } + p.logAction("added", obj) } func (p *Pool[T]) AddKey(key string, obj T) { p.checkExists(key) p.m.Store(key, obj) - if !p.disableLog { - log.Info().Msgf("%s: added %s", p.name, obj.Name()) - } + p.logAction("added", obj) } func (p *Pool[T]) AddIfNotExists(obj T) (actual T, added bool) { actual, loaded := p.m.LoadOrStore(obj.Key(), obj) + if !loaded { + p.logAction("added", obj) + } return actual, !loaded } func (p *Pool[T]) Del(obj T) { p.m.Delete(obj.Key()) - if !p.disableLog { - log.Info().Msgf("%s: removed %s", p.name, obj.Name()) - } + p.logAction("removed", obj) } func (p *Pool[T]) DelKey(key string) { - p.m.Delete(key) - if !p.disableLog { - log.Info().Msgf("%s: removed %s", p.name, key) + if v, exists := p.m.LoadAndDelete(key); exists { + p.logAction("removed", v) } } @@ -92,3 +93,19 @@ func (p *Pool[T]) Slice() []T { }) return slice } + +func (p *Pool[T]) logAction(action string, obj T) { + if p.disableLog.Load() { + return + } + if obj, ok := any(obj).(ObjectWithDisplayName); ok { + disp, name := obj.DisplayName(), obj.Name() + if disp != name { + log.Info().Msgf("%s: %s %s (%s)", p.name, action, disp, name) + } else { + log.Info().Msgf("%s: %s %s", p.name, action, name) + } + } else { + log.Info().Msgf("%s: %s %s", p.name, action, obj.Name()) + } +}