github.com/xmidt-org/webpa-common@v1.11.9/service/monitor/listener.go (about)

     1  package monitor
     2  
     3  import (
     4  	"sync/atomic"
     5  	"time"
     6  
     7  	"github.com/go-kit/kit/log"
     8  	"github.com/go-kit/kit/log/level"
     9  	"github.com/go-kit/kit/metrics/provider"
    10  	"github.com/go-kit/kit/sd"
    11  	"github.com/xmidt-org/webpa-common/logging"
    12  	"github.com/xmidt-org/webpa-common/service"
    13  )
    14  
    15  // Event carries the same information as go-kit's sd.Event, but with the extra Key that identifies
    16  // which filtered service or path was updated.
    17  type Event struct {
    18  	// Key is the in-process unique identifier for the sd.Instancer that produced this event.
    19  	// For consul, this value does not equal the name of the service (as it includes tags, datacenters, etc.).
    20  	// For that purpose, use the Service field.
    21  	Key string
    22  
    23  	// Service, unlike Key, specifically identifies the service of the sd.Instancer that produced this event.
    24  	// This value is used by listeners to update metric labels.
    25  	Service string
    26  
    27  	// Instancer is the go-kit sd.Instancer which sent this event.  This instance can be used to enrich
    28  	// logging via logging.Enrich.
    29  	Instancer sd.Instancer
    30  
    31  	// EventCount is the postive, ascending integer identifying this event's sequence, e.g. 1 refers to the first
    32  	// service discovery event.  Useful for logging and certain types of logic, such as ignoring the initial instances from a monitor.
    33  	EventCount int
    34  
    35  	// Instances are the filtered instances that came from the sd.Instancer.  If this is set,
    36  	// Err will be nil.
    37  	Instances []string
    38  
    39  	// Err is any service discovery error that occurred.  If this is set, Instances will be empty.
    40  	Err error
    41  
    42  	// Stopped is set to true if and only if this event is being sent to indicate the monitoring goroutine
    43  	// has exited, either because of being explicitly stopped or because the environment was closed.
    44  	Stopped bool
    45  }
    46  
    47  type Listener interface {
    48  	MonitorEvent(Event)
    49  }
    50  
    51  type ListenerFunc func(Event)
    52  
    53  func (lf ListenerFunc) MonitorEvent(e Event) {
    54  	lf(e)
    55  }
    56  
    57  type Listeners []Listener
    58  
    59  func (ls Listeners) MonitorEvent(e Event) {
    60  	for _, v := range ls {
    61  		v.MonitorEvent(e)
    62  	}
    63  }
    64  
    65  // NewMetricsListener produces a monitor Listener that gathers metrics related to service discovery.
    66  func NewMetricsListener(p provider.Provider) Listener {
    67  	var (
    68  		errorCount    = p.NewCounter(service.ErrorCount)
    69  		lastError     = p.NewGauge(service.LastErrorTimestamp)
    70  		updateCount   = p.NewCounter(service.UpdateCount)
    71  		lastUpdate    = p.NewGauge(service.LastUpdateTimestamp)
    72  		instanceCount = p.NewGauge(service.InstanceCount)
    73  	)
    74  
    75  	return ListenerFunc(func(e Event) {
    76  		timestamp := float64(time.Now().Unix())
    77  
    78  		if e.Err != nil {
    79  			errorCount.With(service.ServiceLabel, e.Service, service.EventKeyLabel, e.Key).Add(1.0)
    80  			lastError.With(service.ServiceLabel, e.Service, service.EventKeyLabel, e.Key).Set(timestamp)
    81  		} else {
    82  			updateCount.With(service.ServiceLabel, e.Service, service.EventKeyLabel, e.Key).Add(1.0)
    83  			lastUpdate.With(service.ServiceLabel, e.Service, service.EventKeyLabel, e.Key).Set(timestamp)
    84  		}
    85  
    86  		instanceCount.With(service.ServiceLabel, e.Service, service.EventKeyLabel, e.Key).Set(float64(len(e.Instances)))
    87  	})
    88  }
    89  
    90  // NewAccessorListener creates a service discovery Listener that dispatches accessor instances to a nested closure.
    91  // Any error received from the event results in a nil Accessor together with that error being passed to the next closure.
    92  // If the AccessorFactory is nil, DefaultAccessorFactory is used.  If the next closure is nil, this function panics.
    93  //
    94  // An UpdatableAccessor may directly be used to receive events by passing Update as the next closure:
    95  //    ua := new(UpdatableAccessor)
    96  //    l := NewAccessorListener(f, ua.Update)
    97  func NewAccessorListener(f service.AccessorFactory, next func(service.Accessor, error)) Listener {
    98  	if next == nil {
    99  		panic("A next closure is required to receive Accessors")
   100  	}
   101  
   102  	if f == nil {
   103  		f = service.DefaultAccessorFactory
   104  	}
   105  
   106  	return ListenerFunc(func(e Event) {
   107  		switch {
   108  		case e.Err != nil:
   109  			next(nil, e.Err)
   110  
   111  		case len(e.Instances) > 0:
   112  			next(f(e.Instances), nil)
   113  
   114  		default:
   115  			next(service.EmptyAccessor(), nil)
   116  		}
   117  	})
   118  }
   119  
   120  func NewKeyAccessorListener(f service.AccessorFactory, key string, next func(string, service.Accessor, error)) Listener {
   121  	if next == nil {
   122  		panic("A next closure is required to receive Accessors")
   123  	}
   124  
   125  	if f == nil {
   126  		f = service.DefaultAccessorFactory
   127  	}
   128  
   129  	return ListenerFunc(func(e Event) {
   130  		switch {
   131  		case e.Err != nil:
   132  			next(key, nil, e.Err)
   133  
   134  		case len(e.Instances) > 0:
   135  			next(key, f(e.Instances), nil)
   136  
   137  		default:
   138  			next(key, service.EmptyAccessor(), nil)
   139  		}
   140  	})
   141  }
   142  
   143  const (
   144  	stateDeregistered uint32 = 0
   145  	stateRegistered   uint32 = 1
   146  )
   147  
   148  // NewRegistrarListener binds service registration to the lifecycle of a service discovery watch.
   149  // Upon the first successful update, or on any successful update following one or more errors, the given
   150  // registrar is registered.  Any error that follows a successful update, or on the first error, results
   151  // in deregistration.
   152  func NewRegistrarListener(logger log.Logger, r sd.Registrar, initiallyRegistered bool) Listener {
   153  	if logger == nil {
   154  		logger = logging.DefaultLogger()
   155  	}
   156  
   157  	if r == nil {
   158  		panic("A registrar is required")
   159  	}
   160  
   161  	var state uint32 = stateDeregistered
   162  	if initiallyRegistered {
   163  		state = stateRegistered
   164  	}
   165  
   166  	return ListenerFunc(func(e Event) {
   167  		var message string
   168  		if e.Err != nil {
   169  			message = "deregistering on service discovery error"
   170  		} else if e.Stopped {
   171  			message = "deregistering due to monitor being stopped"
   172  		} else {
   173  			if atomic.CompareAndSwapUint32(&state, stateDeregistered, stateRegistered) {
   174  				logger.Log(level.Key(), level.InfoValue(), logging.MessageKey(), "registering on service discovery update")
   175  				r.Register()
   176  			}
   177  
   178  			return
   179  		}
   180  
   181  		if atomic.CompareAndSwapUint32(&state, stateRegistered, stateDeregistered) {
   182  			logger.Log(level.Key(), level.ErrorValue(), logging.MessageKey(), message, logging.ErrorKey(), e.Err, "stopped", e.Stopped)
   183  			r.Deregister()
   184  		}
   185  	})
   186  }