Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React to runtime EventSource changes #2268

Merged
merged 1 commit into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading