Skip to content

Commit

Permalink
React to runtime EventSource changes (#2268)
Browse files Browse the repository at this point in the history
  • Loading branch information
MihaZupan authored Oct 3, 2023
1 parent 26fc382 commit 87bb512
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 9 deletions.
125 changes: 119 additions & 6 deletions src/TelemetryConsumption/Http/HttpEventListenerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ internal sealed class HttpEventListenerService : EventListenerService<HttpEventL
{
protected override string EventSourceName => "System.Net.Http";

#if NET8_0_OR_GREATER
protected override int NumberOfMetrics => 11;
#else
protected override int NumberOfMetrics => 9;
#endif

public HttpEventListenerService(ILogger<HttpEventListenerService> logger, IEnumerable<IHttpTelemetryConsumer> telemetryConsumers, IEnumerable<IMetricsConsumer<HttpMetrics>> metricsConsumers)
: base(logger, telemetryConsumers, metricsConsumers)
Expand Down Expand Up @@ -48,39 +52,83 @@ protected override void OnEvent(IHttpTelemetryConsumer[] consumers, EventWritten
break;

case 2:
Debug.Assert(eventData.EventName == "RequestStop" /* && payload.Count == 0 */);
Debug.Assert(eventData.EventName == "RequestStop" && payload.Count == (eventData.Version == 0 ? 0 : 1));
{
#if NET8_0_OR_GREATER
var statusCode = (int)payload[0];
foreach (var consumer in consumers)
{
consumer.OnRequestStop(eventData.TimeStamp, statusCode);
}
#else
foreach (var consumer in consumers)
{
consumer.OnRequestStop(eventData.TimeStamp);
}
#endif
}
break;

case 3:
Debug.Assert(eventData.EventName == "RequestFailed" /* && payload.Count == 0 */);
Debug.Assert(eventData.EventName == "RequestFailed" && payload.Count == (eventData.Version == 0 ? 0 : 1));
{
#if NET8_0_OR_GREATER
var exceptionMessage = (string)payload[0];
foreach (var consumer in consumers)
{
consumer.OnRequestFailed(eventData.TimeStamp, exceptionMessage);
}
#else
foreach (var consumer in consumers)
{
consumer.OnRequestFailed(eventData.TimeStamp);
}
#endif
}
break;

case 4:
Debug.Assert(eventData.EventName == "ConnectionEstablished" && payload.Count == 2);
Debug.Assert(eventData.EventName == "ConnectionEstablished" && payload.Count == (eventData.Version == 0 ? 2 : 7));
{
var versionMajor = (int)(byte)payload[0];
var versionMinor = (int)(byte)payload[1];
#if NET8_0_OR_GREATER
var connectionId = (long)payload[2];
var scheme = (string)payload[3];
var host = (string)payload[4];
var port = (int)payload[5];
var remoteAddress = (string?)payload[6];
foreach (var consumer in consumers)
{
consumer.OnConnectionEstablished(eventData.TimeStamp, versionMajor, versionMinor, connectionId, scheme, host, port, remoteAddress);
}
#else
foreach (var consumer in consumers)
{
consumer.OnConnectionEstablished(eventData.TimeStamp, versionMajor, versionMinor);
}
#endif
}
break;

case 5:
Debug.Assert(eventData.EventName == "ConnectionClosed" && payload.Count == 2);
Debug.Assert(eventData.EventName == "ConnectionClosed" && payload.Count == (eventData.Version == 0 ? 2 : 3));
{
var versionMajor = (int)(byte)payload[0];
var versionMinor = (int)(byte)payload[1];
#if NET8_0_OR_GREATER
var connectionId = (long)payload[2];
foreach (var consumer in consumers)
{
consumer.OnConnectionClosed(eventData.TimeStamp, versionMajor, versionMinor, connectionId);
}
#else
foreach (var consumer in consumers)
{
consumer.OnConnectionClosed(eventData.TimeStamp, versionMajor, versionMinor);
}
#endif
}
break;

case 6:
Expand All @@ -97,12 +145,20 @@ protected override void OnEvent(IHttpTelemetryConsumer[] consumers, EventWritten
break;

case 7:
Debug.Assert(eventData.EventName == "RequestHeadersStart" && payload.Count == 0);
Debug.Assert(eventData.EventName == "RequestHeadersStart" && payload.Count == (eventData.Version == 0 ? 0 : 1));
{
#if NET8_0_OR_GREATER
var connectionId = (long)payload[0];
foreach (var consumer in consumers)
{
consumer.OnRequestHeadersStart(eventData.TimeStamp, connectionId);
}
#else
foreach (var consumer in consumers)
{
consumer.OnRequestHeadersStart(eventData.TimeStamp);
}
#endif
}
break;

Expand Down Expand Up @@ -148,14 +204,61 @@ protected override void OnEvent(IHttpTelemetryConsumer[] consumers, EventWritten
break;

case 12:
Debug.Assert(eventData.EventName == "ResponseHeadersStop" /* && payload.Count == 0 */);
Debug.Assert(eventData.EventName == "ResponseHeadersStop" && payload.Count == (eventData.Version == 0 ? 0 : 1));
{
#if NET8_0_OR_GREATER
var statusCode = (int)payload[0];
foreach (var consumer in consumers)
{
consumer.OnResponseHeadersStop(eventData.TimeStamp, statusCode);
}
#else
foreach (var consumer in consumers)
{
consumer.OnResponseHeadersStop(eventData.TimeStamp);
}
#endif
}
break;

case 13:
Debug.Assert(eventData.EventName == "ResponseContentStart" && payload.Count == 0);
{
foreach (var consumer in consumers)
{
consumer.OnResponseContentStart(eventData.TimeStamp);
}
}
break;

case 14:
Debug.Assert(eventData.EventName == "ResponseContentStop" && payload.Count == 0);
{
foreach (var consumer in consumers)
{
consumer.OnResponseContentStop(eventData.TimeStamp);
}
}
break;

case 15:
Debug.Assert(eventData.EventName == "RequestFailedDetailed" && payload.Count == 1);
// This event is more expensive to collect and requires an opt-in keyword.
Debug.Fail("We shouldn't be seeing this event as the base EventListenerService always uses EventKeywords.None.");
break;

case 16:
Debug.Assert(eventData.EventName == "Redirect" && payload.Count == 1);
#if NET8_0_OR_GREATER
{
var redirectUri = (string)payload[0];
foreach (var consumer in consumers)
{
consumer.OnRedirect(eventData.TimeStamp, redirectUri);
}
}
#endif
break;
}
}

Expand Down Expand Up @@ -199,6 +302,16 @@ protected override bool TrySaveMetric(HttpMetrics metrics, string name, double v
metrics.Http20RequestsQueueDuration = TimeSpan.FromMilliseconds(value);
break;

#if NET8_0_OR_GREATER
case "http30-connections-current-total":
metrics.CurrentHttp30Connections = (long)value;
break;

case "http30-requests-queue-duration":
metrics.Http30RequestsQueueDuration = TimeSpan.FromMilliseconds(value);
break;
#endif

default:
return false;
}
Expand Down
12 changes: 12 additions & 0 deletions src/TelemetryConsumption/Http/HttpMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,16 @@ public sealed class HttpMetrics
/// Average time spent on queue for HTTP 2.0 requests that hit the MAX_CONCURRENT_STREAMS limit on the connection in the last metrics interval.
/// </summary>
public TimeSpan Http20RequestsQueueDuration { get; internal set; }

#if NET8_0_OR_GREATER
/// <summary>
/// Number of currently open HTTP 3.0 connections.
/// </summary>
public long CurrentHttp30Connections { get; internal set; }

/// <summary>
/// Average time spent on queue for HTTP 3.0 requests in the last metrics interval.
/// </summary>
public TimeSpan Http30RequestsQueueDuration { get; internal set; }
#endif
}
89 changes: 89 additions & 0 deletions src/TelemetryConsumption/Http/IHttpTelemetryConsumer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ void OnRequestFailed(DateTime timestamp) { }
/// <param name="versionMinor">Minor component of the connection's HTTP version.</param>
void OnConnectionEstablished(DateTime timestamp, int versionMajor, int versionMinor) { }

/// <summary>
/// Called when a new HTTP connection is closed.
/// </summary>
/// <param name="timestamp">Timestamp when the event was fired.</param>
/// <param name="versionMajor">Major component of the connection's HTTP version.</param>
/// <param name="versionMinor">Minor component of the connection's HTTP version.</param>
void OnConnectionClosed(DateTime timestamp, int versionMajor, int versionMinor) { }

/// <summary>
/// Called when a request that hit the MaxConnectionsPerServer or MAX_CONCURRENT_STREAMS limit leaves the queue.
/// </summary>
Expand Down Expand Up @@ -89,4 +97,85 @@ void OnResponseHeadersStart(DateTime timestamp) { }
/// </summary>
/// <param name="timestamp">Timestamp when the event was fired.</param>
void OnResponseHeadersStop(DateTime timestamp) { }

/// <summary>
/// Called when <see cref="HttpClient"/> starts buffering the response content.
/// This event WILL NOT be called for requests made by YARP, as they are not buffered.
/// </summary>
/// <param name="timestamp">Timestamp when the event was fired.</param>
void OnResponseContentStart(DateTime timestamp) { }

/// <summary>
/// Called when <see cref="HttpClient"/> stops buffering the response content.
/// This event WILL NOT be called for requests made by YARP, as they are not buffered.
/// </summary>
/// <param name="timestamp">Timestamp when the event was fired.</param>
void OnResponseContentStop(DateTime timestamp) { }

// Some events were augmented in .NET 8 with more parameters.
// For backwards compatibility, they are implemented as DIMs that forward to older methods with fewer parameters.
#if NET8_0_OR_GREATER
/// <summary>
/// Called after an HTTP request.
/// </summary>
/// <param name="timestamp">Timestamp when the event was fired.</param>
/// <param name="statusCode">The status code returned by the server. -1 if no response was received.</param>
void OnRequestStop(DateTime timestamp, int statusCode) =>
OnRequestStop(timestamp);

/// <summary>
/// Called before <see cref="OnRequestStop(DateTime)"/> if the request failed.
/// </summary>
/// <param name="timestamp">Timestamp when the event was fired.</param>
/// <param name="exceptionMessage">A message that describes the exception associated with this request failure.</param>
void OnRequestFailed(DateTime timestamp, string exceptionMessage) =>
OnRequestFailed(timestamp);

/// <summary>
/// Called when a new HTTP connection is established.
/// </summary>
/// <param name="timestamp">Timestamp when the event was fired.</param>
/// <param name="versionMajor">Major component of the connection's HTTP version.</param>
/// <param name="versionMinor">Minor component of the connection's HTTP version.</param>
/// <param name="connectionId">ID of the connection that was established, unique for this process.</param>
/// <param name="scheme">Scheme the connection was established with.</param>
/// <param name="host">Host the connection was established to.</param>
/// <param name="port">Port the connection was established to.</param>
/// <param name="remoteAddress">The remote address this connection was established to, if available.</param>
void OnConnectionEstablished(DateTime timestamp, int versionMajor, int versionMinor, long connectionId, string scheme, string host, int port, string? remoteAddress) =>
OnConnectionEstablished(timestamp, versionMajor, versionMinor);

/// <summary>
/// Called when a new HTTP connection is closed.
/// </summary>
/// <param name="timestamp">Timestamp when the event was fired.</param>
/// <param name="versionMajor">Major component of the connection's HTTP version.</param>
/// <param name="versionMinor">Minor component of the connection's HTTP version.</param>
/// <param name="connectionId">ID of the connection that was closed.</param>
void OnConnectionClosed(DateTime timestamp, int versionMajor, int versionMinor, long connectionId) =>
OnConnectionClosed(timestamp, versionMajor, versionMinor);

/// <summary>
/// Called before sending the request headers.
/// </summary>
/// <param name="timestamp">Timestamp when the event was fired.</param>
/// <param name="connectionId">ID of the connection we are sending this request on.</param>
void OnRequestHeadersStart(DateTime timestamp, long connectionId) =>
OnRequestHeadersStart(timestamp);

/// <summary>
/// Called after reading all response headers.
/// </summary>
/// <param name="timestamp">Timestamp when the event was fired.</param>
/// <param name="statusCode">The status code returned by the server.</param>
void OnResponseHeadersStop(DateTime timestamp, int statusCode) =>
OnResponseHeadersStop(timestamp);

/// <summary>
/// Called before a request is redirected if <see cref="SocketsHttpHandler.AllowAutoRedirect"/> is enabled.
/// </summary>
/// <param name="timestamp">Timestamp when the event was fired.</param>
/// <param name="redirectUri">The uri the request is being redirected to.</param>
void OnRedirect(DateTime timestamp, string redirectUri) { }
#endif
}
10 changes: 10 additions & 0 deletions src/TelemetryConsumption/Sockets/SocketsEventListenerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ internal sealed class SocketsEventListenerService : EventListenerService<Sockets
{
protected override string EventSourceName => "System.Net.Sockets";

#if NET8_0_OR_GREATER
protected override int NumberOfMetrics => 7;
#else
protected override int NumberOfMetrics => 6;
#endif

public SocketsEventListenerService(ILogger<SocketsEventListenerService> logger, IEnumerable<ISocketsTelemetryConsumer> telemetryConsumers, IEnumerable<IMetricsConsumer<SocketsMetrics>> metricsConsumers)
: base(logger, telemetryConsumers, metricsConsumers)
Expand Down Expand Up @@ -94,6 +98,12 @@ protected override bool TrySaveMetric(SocketsMetrics metrics, string name, doubl
metrics.DatagramsSent = longValue;
break;

#if NET8_0_OR_GREATER
case "current-outgoing-connect-attempts":
metrics.CurrentOutgoingConnectAttempts = longValue;
break;
#endif

default:
return false;
}
Expand Down
7 changes: 7 additions & 0 deletions src/TelemetryConsumption/Sockets/SocketsMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,11 @@ public sealed class SocketsMetrics
/// Number of datagrams sent since telemetry was enabled.
/// </summary>
public long DatagramsSent { get; internal set; }

#if NET8_0_OR_GREATER
/// <summary>
/// Number of outgoing (Connect) Socket connection attempts that are currently in progress.
/// </summary>
public long CurrentOutgoingConnectAttempts { get; internal set; }
#endif
}
Loading

0 comments on commit 87bb512

Please sign in to comment.