// when receiving DiagnosticCounter events we may have buffered them to wait for // duplicate instrument events. If we've waited long enough then we should remove // them from the buffer and render them. private void HandleBufferedEvents() { DateTime now = DateTime.Now; lock (this) { while (_bufferedEvents.Count != 0) { CounterPayload payload = _bufferedEvents.Peek(); ProviderEventState providerEventState = _providerEventStates[payload.ProviderName]; if (providerEventState.InstrumentEventObserved) { _bufferedEvents.Dequeue(); } else if (providerEventState.FirstReceiveTimestamp + TimeSpan.FromSeconds(BufferDelaySecs) < now) { _bufferedEvents.Dequeue(); _renderer.CounterPayloadReceived(payload, _pauseCmdSet); } else { // technically an event that is eligible to be unbuffered earlier could be waiting behind a // buffered event that will wait longer, but we don't expect this variation to matter for // our scenarios. At worst an event might wait up to 2*BufferDelaySecs. If there is a scenario // where it matters we could scan the entire queue rather than just the front of it. break; } } } }
private void HandleDiagnosticCounter(TraceEvent obj) { IDictionary <string, object> payloadVal = (IDictionary <string, object>)(obj.PayloadValue(0)); IDictionary <string, object> payloadFields = (IDictionary <string, object>)(payloadVal["Payload"]); // If it's not a counter we asked for, ignore it. string name = payloadFields["Name"].ToString(); if (!_counterList.Contains(obj.ProviderName, name)) { return; } // init providerEventState if this is the first time we've seen an event from this provider if (!_providerEventStates.TryGetValue(obj.ProviderName, out ProviderEventState providerState)) { providerState = new ProviderEventState() { FirstReceiveTimestamp = obj.TimeStamp }; _providerEventStates.Add(obj.ProviderName, providerState); } // we give precedence to instrument events over diagnostic counter events. If we are seeing // both then drop this one. if (providerState.InstrumentEventObserved) { return; } CounterPayload payload = null; if (payloadFields["CounterType"].Equals("Sum")) { payload = new RatePayload( obj.ProviderName, name, payloadFields["DisplayName"].ToString(), payloadFields["DisplayUnits"].ToString(), null, (double)payloadFields["Increment"], _interval, obj.TimeStamp); } else { payload = new GaugePayload( obj.ProviderName, name, payloadFields["DisplayName"].ToString(), payloadFields["DisplayUnits"].ToString(), null, (double)payloadFields["Mean"], obj.TimeStamp); } // If we saw the first event for this provider recently then a duplicate instrument event may still be // coming. We'll buffer this event for a while and then render it if it remains unduplicated for // a while. // This is all best effort, if we do show the DiagnosticCounter event and then an instrument event shows up // later the renderer may obsserve some odd behavior like changes in the counter metadata, oddly timed reporting // intervals, or counters that stop reporting. // I'm gambling this is good enough that the behavior will never be seen in practice, but if it is we could // either adjust the time delay or try to improve how the renderers handle it. if (providerState.FirstReceiveTimestamp + TimeSpan.FromSeconds(BufferDelaySecs) >= obj.TimeStamp) { _bufferedEvents.Enqueue(payload); } else { _renderer.CounterPayloadReceived(payload, _pauseCmdSet); } }