logging.go raw

   1  // Package subscribers provides domain event subscriber implementations.
   2  package subscribers
   3  
   4  import (
   5  	"encoding/hex"
   6  
   7  	"next.orly.dev/pkg/lol/log"
   8  
   9  	"next.orly.dev/pkg/domain/events"
  10  )
  11  
  12  // LoggingSubscriber logs domain events for analytics and debugging.
  13  type LoggingSubscriber struct {
  14  	logLevel string // "debug", "info", "trace"
  15  }
  16  
  17  // NewLoggingSubscriber creates a new logging subscriber.
  18  // logLevel controls verbosity: "trace" logs all events, "debug" logs important events,
  19  // "info" logs only significant events like membership changes.
  20  func NewLoggingSubscriber(logLevel string) *LoggingSubscriber {
  21  	if logLevel == "" {
  22  		logLevel = "debug"
  23  	}
  24  	return &LoggingSubscriber{logLevel: logLevel}
  25  }
  26  
  27  // Handle processes a domain event by logging it.
  28  func (s *LoggingSubscriber) Handle(event events.DomainEvent) {
  29  	switch e := event.(type) {
  30  	case *events.EventSaved:
  31  		s.logEventSaved(e)
  32  	case *events.EventDeleted:
  33  		s.logEventDeleted(e)
  34  	case *events.FollowListUpdated:
  35  		s.logFollowListUpdated(e)
  36  	case *events.ACLMembershipChanged:
  37  		s.logACLMembershipChanged(e)
  38  	case *events.PolicyConfigUpdated:
  39  		s.logPolicyConfigUpdated(e)
  40  	case *events.UserAuthenticated:
  41  		s.logUserAuthenticated(e)
  42  	case *events.MemberJoined:
  43  		s.logMemberJoined(e)
  44  	case *events.MemberLeft:
  45  		s.logMemberLeft(e)
  46  	case *events.ConnectionOpened:
  47  		s.logConnectionOpened(e)
  48  	case *events.ConnectionClosed:
  49  		s.logConnectionClosed(e)
  50  	default:
  51  		if s.logLevel == "trace" {
  52  			log.T.F("domain event: %s", event.EventType())
  53  		}
  54  	}
  55  }
  56  
  57  // Supports returns true for all event types.
  58  func (s *LoggingSubscriber) Supports(eventType string) bool {
  59  	return true
  60  }
  61  
  62  func (s *LoggingSubscriber) logEventSaved(e *events.EventSaved) {
  63  	if s.logLevel == "trace" {
  64  		pubkeyHex := hex.EncodeToString(e.Event.Pubkey)
  65  		log.T.F("event saved: kind=%d pubkey=%s admin=%v owner=%v",
  66  			e.Event.Kind, pubkeyHex[:16], e.IsAdmin, e.IsOwner)
  67  	}
  68  }
  69  
  70  func (s *LoggingSubscriber) logEventDeleted(e *events.EventDeleted) {
  71  	if s.logLevel == "trace" || s.logLevel == "debug" {
  72  		eventIDHex := hex.EncodeToString(e.EventID)
  73  		deletedByHex := hex.EncodeToString(e.DeletedBy)
  74  		log.D.F("event deleted: id=%s by=%s", eventIDHex[:16], deletedByHex[:16])
  75  	}
  76  }
  77  
  78  func (s *LoggingSubscriber) logFollowListUpdated(e *events.FollowListUpdated) {
  79  	if s.logLevel == "trace" || s.logLevel == "debug" {
  80  		adminHex := hex.EncodeToString(e.AdminPubkey)
  81  		log.D.F("follow list updated: admin=%s added=%d removed=%d",
  82  			adminHex[:16], len(e.AddedFollows), len(e.RemovedFollows))
  83  	}
  84  }
  85  
  86  func (s *LoggingSubscriber) logACLMembershipChanged(e *events.ACLMembershipChanged) {
  87  	// Always log ACL changes at info level - they're significant
  88  	pubkeyHex := hex.EncodeToString(e.Pubkey)
  89  	log.I.F("ACL membership changed: pubkey=%s %s->%s reason=%s",
  90  		pubkeyHex[:16], e.PrevLevel, e.NewLevel, e.Reason)
  91  }
  92  
  93  func (s *LoggingSubscriber) logPolicyConfigUpdated(e *events.PolicyConfigUpdated) {
  94  	// Always log policy changes at info level
  95  	updatedByHex := hex.EncodeToString(e.UpdatedBy)
  96  	log.I.F("policy config updated by %s: %d changes", updatedByHex[:16], len(e.Changes))
  97  }
  98  
  99  func (s *LoggingSubscriber) logUserAuthenticated(e *events.UserAuthenticated) {
 100  	if s.logLevel == "trace" || s.logLevel == "debug" {
 101  		pubkeyHex := hex.EncodeToString(e.Pubkey)
 102  		log.D.F("user authenticated: pubkey=%s level=%s firstTime=%v",
 103  			pubkeyHex[:16], e.AccessLevel, e.IsFirstTime)
 104  	}
 105  }
 106  
 107  func (s *LoggingSubscriber) logMemberJoined(e *events.MemberJoined) {
 108  	// Always log member joins at info level
 109  	pubkeyHex := hex.EncodeToString(e.Pubkey)
 110  	log.I.F("member joined: pubkey=%s invite=%s", pubkeyHex[:16], e.InviteCode)
 111  }
 112  
 113  func (s *LoggingSubscriber) logMemberLeft(e *events.MemberLeft) {
 114  	// Always log member departures at info level
 115  	pubkeyHex := hex.EncodeToString(e.Pubkey)
 116  	log.I.F("member left: pubkey=%s", pubkeyHex[:16])
 117  }
 118  
 119  func (s *LoggingSubscriber) logConnectionOpened(e *events.ConnectionOpened) {
 120  	if s.logLevel == "trace" {
 121  		log.T.F("connection opened: id=%s remote=%s", e.ConnectionID, e.RemoteAddr)
 122  	}
 123  }
 124  
 125  func (s *LoggingSubscriber) logConnectionClosed(e *events.ConnectionClosed) {
 126  	if s.logLevel == "trace" || s.logLevel == "debug" {
 127  		log.D.F("connection closed: id=%s duration=%v events_rx=%d events_tx=%d",
 128  			e.ConnectionID, e.Duration, e.EventsReceived, e.EventsPublished)
 129  	}
 130  }
 131