diff --git a/telemetry/collection.go b/telemetry/collection.go index d4b1e501..3e7e2fc5 100644 --- a/telemetry/collection.go +++ b/telemetry/collection.go @@ -104,7 +104,7 @@ func Reset() { // go keyword after the call to SendHello so it // doesn't block crucial code. func Set(key string, val interface{}) { - if !enabled { + if !enabled || isDisabled(key) { return } bufferMu.Lock() @@ -123,10 +123,8 @@ func Set(key string, val interface{}) { // If key is new, a new list will be created. // If key maps to a type that is not a list, // a panic is logged, and this is a no-op. -// -// TODO: is this function needed/useful? func Append(key string, value interface{}) { - if !enabled { + if !enabled || isDisabled(key) { return } bufferMu.Lock() @@ -161,7 +159,7 @@ func Append(key string, value interface{}) { // that is not a counting set, a panic is logged, // and this is a no-op. func AppendUnique(key string, value interface{}) { - if !enabled { + if !enabled || isDisabled(key) { return } bufferMu.Lock() @@ -204,7 +202,7 @@ func Increment(key string) { // atomicAdd adds amount (negative to subtract) // to key. func atomicAdd(key string, amount int) { - if !enabled { + if !enabled || isDisabled(key) { return } bufferMu.Lock() @@ -225,3 +223,14 @@ func atomicAdd(key string, amount int) { buffer[key] = intVal + amount bufferMu.Unlock() } + +// isDisabled returns whether key is +// a disabled metric key. ALL collection +// functions should call this and not +// save the value if this returns true. +func isDisabled(key string) bool { + disabledMetricsMu.RLock() + _, ok := disabledMetrics[key] + disabledMetricsMu.RUnlock() + return ok +} diff --git a/telemetry/telemetry.go b/telemetry/telemetry.go index 0e831fad..bc193e31 100644 --- a/telemetry/telemetry.go +++ b/telemetry/telemetry.go @@ -155,6 +155,18 @@ func emit(final bool) error { continue } + // update the list of enabled/disabled keys, if any + for _, key := range reply.EnableKeys { + disabledMetricsMu.Lock() + delete(disabledMetrics, key) + disabledMetricsMu.Unlock() + } + for _, key := range reply.DisableKeys { + disabledMetricsMu.Lock() + disabledMetrics[key] = struct{}{} + disabledMetricsMu.Unlock() + } + // make sure we didn't send the update too soon; if so, // just wait and try again -- this is a special case of // error that we handle differently, as you can see @@ -259,6 +271,18 @@ type Response struct { // Error will be populated with an error message, if any. // This field should be empty if the status code is < 400. Error string `json:"error,omitempty"` + + // DisableKeys will contain a list of keys/metrics that + // should NOT be sent until further notice. The client + // must NOT store these items in its buffer or send them + // to the telemetry server while they are disabled. If + // this list and EnableKeys have the same value (which is + // not supposed to happen), this field should dominate. + DisableKeys []string `json:"disable_keys,omitempty"` + + // EnableKeys will contain a list of keys/metrics that + // MAY be sent until further notice. + EnableKeys []string `json:"enable_keys,omitempty"` } // Payload is the data that gets sent to the telemetry server. @@ -335,6 +359,12 @@ var ( updateTimer *time.Timer updateTimerMu sync.Mutex + // disabledMetrics is a list of metric keys + // that should NOT be saved to the buffer + // or sent to the telemetry server. + disabledMetrics = make(map[string]struct{}) + disabledMetricsMu sync.RWMutex + // instanceUUID is the ID of the current instance. // This MUST be set to emit telemetry. // This MUST NOT be openly exposed to clients, for privacy.