github.com/prebid/prebid-server/v2@v2.18.0/analytics/pubstack/pubstack_module.go (about)

     1  package pubstack
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"os"
     7  	"os/signal"
     8  	"sync"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/benbjohnson/clock"
    13  	"github.com/golang/glog"
    14  
    15  	"github.com/prebid/prebid-server/v2/analytics"
    16  	"github.com/prebid/prebid-server/v2/analytics/pubstack/eventchannel"
    17  	"github.com/prebid/prebid-server/v2/analytics/pubstack/helpers"
    18  )
    19  
    20  type Configuration struct {
    21  	ScopeID  string          `json:"scopeId"`
    22  	Endpoint string          `json:"endpoint"`
    23  	Features map[string]bool `json:"features"`
    24  }
    25  
    26  // routes for events
    27  const (
    28  	auction    = "auction"
    29  	cookieSync = "cookiesync"
    30  	amp        = "amp"
    31  	setUID     = "setuid"
    32  	video      = "video"
    33  )
    34  
    35  type bufferConfig struct {
    36  	timeout time.Duration
    37  	count   int64
    38  	size    int64
    39  }
    40  
    41  type PubstackModule struct {
    42  	eventChannels map[string]*eventchannel.EventChannel
    43  	httpClient    *http.Client
    44  	sigTermCh     chan os.Signal
    45  	stopCh        chan struct{}
    46  	scope         string
    47  	cfg           *Configuration
    48  	buffsCfg      *bufferConfig
    49  	muxConfig     sync.RWMutex
    50  	clock         clock.Clock
    51  }
    52  
    53  func NewModule(client *http.Client, scope, endpoint, configRefreshDelay string, maxEventCount int, maxByteSize, maxTime string, clock clock.Clock) (analytics.Module, error) {
    54  	configUpdateTask, err := NewConfigUpdateHttpTask(
    55  		client,
    56  		scope,
    57  		endpoint,
    58  		configRefreshDelay)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	return NewModuleWithConfigTask(client, scope, endpoint, maxEventCount, maxByteSize, maxTime, configUpdateTask, clock)
    64  }
    65  
    66  func NewModuleWithConfigTask(client *http.Client, scope, endpoint string, maxEventCount int, maxByteSize, maxTime string, configTask ConfigUpdateTask, clock clock.Clock) (analytics.Module, error) {
    67  	glog.Infof("[pubstack] Initializing module scope=%s endpoint=%s\n", scope, endpoint)
    68  
    69  	// parse args
    70  	bufferCfg, err := newBufferConfig(maxEventCount, maxByteSize, maxTime)
    71  	if err != nil {
    72  		return nil, fmt.Errorf("fail to parse the module args, arg=analytics.pubstack.buffers, :%v", err)
    73  	}
    74  
    75  	defaultFeatures := map[string]bool{
    76  		auction:    false,
    77  		video:      false,
    78  		amp:        false,
    79  		cookieSync: false,
    80  		setUID:     false,
    81  	}
    82  
    83  	defaultConfig := &Configuration{
    84  		ScopeID:  scope,
    85  		Endpoint: endpoint,
    86  		Features: defaultFeatures,
    87  	}
    88  
    89  	pb := PubstackModule{
    90  		scope:         scope,
    91  		httpClient:    client,
    92  		cfg:           defaultConfig,
    93  		buffsCfg:      bufferCfg,
    94  		sigTermCh:     make(chan os.Signal),
    95  		stopCh:        make(chan struct{}),
    96  		eventChannels: make(map[string]*eventchannel.EventChannel),
    97  		muxConfig:     sync.RWMutex{},
    98  		clock:         clock,
    99  	}
   100  
   101  	signal.Notify(pb.sigTermCh, os.Interrupt, syscall.SIGTERM)
   102  
   103  	configChannel := configTask.Start(pb.stopCh)
   104  	go pb.start(configChannel)
   105  
   106  	glog.Info("[pubstack] Pubstack analytics configured and ready")
   107  	return &pb, nil
   108  }
   109  
   110  func (p *PubstackModule) LogAuctionObject(ao *analytics.AuctionObject) {
   111  	p.muxConfig.RLock()
   112  	defer p.muxConfig.RUnlock()
   113  
   114  	if !p.isFeatureEnable(auction) {
   115  		return
   116  	}
   117  
   118  	// serialize event
   119  	payload, err := helpers.JsonifyAuctionObject(ao, p.scope)
   120  	if err != nil {
   121  		glog.Warning("[pubstack] Cannot serialize auction")
   122  		return
   123  	}
   124  
   125  	p.eventChannels[auction].Push(payload)
   126  }
   127  
   128  func (p *PubstackModule) LogNotificationEventObject(ne *analytics.NotificationEvent) {
   129  }
   130  
   131  func (p *PubstackModule) LogVideoObject(vo *analytics.VideoObject) {
   132  	p.muxConfig.RLock()
   133  	defer p.muxConfig.RUnlock()
   134  
   135  	if !p.isFeatureEnable(video) {
   136  		return
   137  	}
   138  
   139  	// serialize event
   140  	payload, err := helpers.JsonifyVideoObject(vo, p.scope)
   141  	if err != nil {
   142  		glog.Warning("[pubstack] Cannot serialize video")
   143  		return
   144  	}
   145  
   146  	p.eventChannels[video].Push(payload)
   147  }
   148  
   149  func (p *PubstackModule) LogSetUIDObject(so *analytics.SetUIDObject) {
   150  	p.muxConfig.RLock()
   151  	defer p.muxConfig.RUnlock()
   152  
   153  	if !p.isFeatureEnable(setUID) {
   154  		return
   155  	}
   156  
   157  	// serialize event
   158  	payload, err := helpers.JsonifySetUIDObject(so, p.scope)
   159  	if err != nil {
   160  		glog.Warning("[pubstack] Cannot serialize video")
   161  		return
   162  	}
   163  
   164  	p.eventChannels[setUID].Push(payload)
   165  }
   166  
   167  func (p *PubstackModule) LogCookieSyncObject(cso *analytics.CookieSyncObject) {
   168  	p.muxConfig.RLock()
   169  	defer p.muxConfig.RUnlock()
   170  
   171  	if !p.isFeatureEnable(cookieSync) {
   172  		return
   173  	}
   174  
   175  	// serialize event
   176  	payload, err := helpers.JsonifyCookieSync(cso, p.scope)
   177  	if err != nil {
   178  		glog.Warning("[pubstack] Cannot serialize video")
   179  		return
   180  	}
   181  
   182  	p.eventChannels[cookieSync].Push(payload)
   183  }
   184  
   185  func (p *PubstackModule) LogAmpObject(ao *analytics.AmpObject) {
   186  	p.muxConfig.RLock()
   187  	defer p.muxConfig.RUnlock()
   188  
   189  	if !p.isFeatureEnable(amp) {
   190  		return
   191  	}
   192  
   193  	// serialize event
   194  	payload, err := helpers.JsonifyAmpObject(ao, p.scope)
   195  	if err != nil {
   196  		glog.Warning("[pubstack] Cannot serialize video")
   197  		return
   198  	}
   199  
   200  	p.eventChannels[amp].Push(payload)
   201  }
   202  
   203  func (p *PubstackModule) start(c <-chan *Configuration) {
   204  	for {
   205  		select {
   206  		case <-p.sigTermCh:
   207  			close(p.stopCh)
   208  			cfg := p.cfg.clone().disableAllFeatures()
   209  			p.updateConfig(cfg)
   210  			return
   211  		case config := <-c:
   212  			p.updateConfig(config)
   213  			glog.Infof("[pubstack] Updating config: %v", p.cfg)
   214  		}
   215  	}
   216  }
   217  
   218  func (p *PubstackModule) updateConfig(config *Configuration) {
   219  	p.muxConfig.Lock()
   220  	defer p.muxConfig.Unlock()
   221  
   222  	if p.cfg.isSameAs(config) {
   223  		return
   224  	}
   225  
   226  	p.cfg = config
   227  	p.closeAllEventChannels()
   228  
   229  	p.registerChannel(amp)
   230  	p.registerChannel(auction)
   231  	p.registerChannel(cookieSync)
   232  	p.registerChannel(video)
   233  	p.registerChannel(setUID)
   234  }
   235  
   236  func (p *PubstackModule) isFeatureEnable(feature string) bool {
   237  	val, ok := p.cfg.Features[feature]
   238  	return ok && val
   239  }
   240  
   241  func (p *PubstackModule) registerChannel(feature string) {
   242  	if p.isFeatureEnable(feature) {
   243  		sender := eventchannel.BuildEndpointSender(p.httpClient, p.cfg.Endpoint, feature)
   244  		p.eventChannels[feature] = eventchannel.NewEventChannel(sender, p.clock, p.buffsCfg.size, p.buffsCfg.count, p.buffsCfg.timeout)
   245  	}
   246  }
   247  
   248  func (p *PubstackModule) closeAllEventChannels() {
   249  	for key, ch := range p.eventChannels {
   250  		ch.Close()
   251  		delete(p.eventChannels, key)
   252  	}
   253  }