-
Notifications
You must be signed in to change notification settings - Fork 524
Implement IHttpRequestIdentifierFeature on Frame #1578
Conversation
|
||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure | ||
{ | ||
internal static class CorrelationIdGenerator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Depending on the outcome of dotnet/extensions#197, this may move to Microsoft.Extensions.Primitives
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not leave it in HttpAbstractions? Kestrel and Hosting would have access to it there. Or does something else need it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll refer you to dotnet/extensions#197 on that one. @muratg is looking into it.
Sample log when application request has an error:
|
} | ||
|
||
bool IHttpResponseFeature.HasStarted | ||
{ | ||
get { return HasResponseStarted; } | ||
get => HasResponseStarted; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These can be reduced to bool IHttpResponseFeature.HasStarted => HasResponseStarted;
right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Will update.
@@ -95,6 +96,8 @@ public Frame(IHttpApplication<TContext> application, FrameContext frameContext) | |||
{ | |||
try | |||
{ | |||
KestrelEventSource.Log.RequestStart(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we need these events. Hosting does the exact same thing what value do we add here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hosting only logs method and path. This logs connectionId and requestId to make it possible to correlate connections and requests.
var stop = Assert.Single(events, e => e.EventName == "ConnectionStop"); | ||
Assert.All(new[] { "connectionId" }, p => Assert.Contains(p, stop.PayloadNames)); | ||
Assert.Same(KestrelEventSource.Log, stop.EventSource); | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the scopes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
avoiding copy+paste errors. Because the asserts for each event type looks so similar.
var id = Encoding.ASCII.GetBytes(context.TraceIdentifier); | ||
Assert.Equal(IdentifierLength, id.Length); | ||
context.Response.ContentLength = id.Length; | ||
context.Response.Body.Write(id, 0, id.Length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WriteAsync
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returns chunked encoding, which made it more difficult to pull the response body via StreamReader.
})) | ||
{ | ||
var usedIds = new HashSet<string>(); | ||
var usedIdsLock = new object(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not ConcurrentBag
instead of set + lock?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, i'll use that. will update
}); | ||
|
||
// requests on same connection | ||
using (var conn = server.CreateConnection()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: call it connection
since that's we call it in all other tests
// requests on same connection | ||
using (var conn = server.CreateConnection()) | ||
{ | ||
var buff = new char[IdentifierLength]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Buff is a funny name, call it either buf
or buffer
😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ᕙ༼ຈل͜ຈ༽ᕗ
Assert.Equal(id, _frame.TraceIdentifier); | ||
|
||
_frame.TraceIdentifier = null; | ||
Assert.NotEqual(id, _frame.TraceIdentifier); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assert.NotNull
again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's implicit as we already asserted id != null.
@@ -45,6 +45,6 @@ public interface IKestrelTrace : ILogger | |||
|
|||
void NotAllConnectionsAborted(); | |||
|
|||
void ApplicationError(string connectionId, Exception ex); | |||
void ApplicationError(string connectionId, string traceIdentifier, Exception ex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
Would it make sense to give connection and request id's unique prefixes to make them easier to distinguish in logs? |
{ | ||
public static class EventSourceExtensions | ||
{ | ||
public static void ConnectionStart(this KestrelEventSource eventSource, Connection connection) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you are moving KestrelEventSource back to the Core project, can this change also move the connection start/stop logging to core? It would be nice if this tracing could be shared between transports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change was required to decouple Connection
which is in Transport.Libuv and KestrelEventSource
which is now in Core. ConnectionStop/Start are still on KestrelEventSource which is in Kestrel.Core, but the public methods now take string arguments directly instead of Connection
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think this is worth doing now. Rather than partially moving KestrelEventSource to Core, let's move it completely. I think this might require adding some sort of OnConnectionClosed() method to IConnectionContext, but it should still be a pretty quick/simple change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
@shirhatti thoughts? Another idea I played with making each request ID contain the connection id so correlation is even easier. // pseudocode
var count = _connectionRequests++;
var requestId = $"{_connectionId}:{count}"; |
🆙 📅 |
{ | ||
await connection.Send("GET / HTTP/1.1", | ||
"Content-Length: 0", | ||
"Host: localhost", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to send Content-Length
and Host
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
|
||
await connection.Receive($"HTTP/1.1 200 OK", | ||
$"Date: {server.Context.DateHeaderValue}", | ||
"Content-Length: " + IdentifierLength, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: use $""
YES please! @glennc Thoughts? |
@shirhatti I will follow up via email. Not everyone checks PR comments. |
@@ -12,6 +12,7 @@ public interface IConnectionContext | |||
string ConnectionId { get; } | |||
IPipeWriter Input { get; } | |||
IPipeReader Output { get; } | |||
void OnConnectionClosed(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Move below "// TODO: Remove these (Use Pipes instead?)"
There is a task that will be added to Pipes that should let us track when SocketOutputConsumer complete's it's pipe reader which will be right after disposing the socket.
This feature generates a unique ID per request. This unique ID can be used in event source and logging. Also, this change improves KestrelEventSource by moving it back into the Kestrel.Core assembly and de-coupling from the Libuv transport. This adds two new events, RequestStart and RequestStop, which can be used to identify the correlation between connection ID and request trace identifier.
c05230e
to
8395888
Compare
This feature generates a unique ID per request. This unique ID can be used in event source and logging.
Also, this change improves KestrelEventSource by moving it back into the Kestrel.Core assembly and de-coupling from the Libuv transport. This adds two new events, RequestStart and RequestStop, which can be used to identify the correlation between connection ID and request trace
identifier.
Resolves #1367
Benchmark
-n Plaintext --kestrelThreadCount 2
dev
RPS: 1344136.86
RPS: 1374674.53
log-request-id
RPS: 1341543.62
RPS: 1360439.85