Skip to content

Betterize Hosting Log #10102

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

Merged
merged 4 commits into from
Aug 20, 2019
Merged

Betterize Hosting Log #10102

merged 4 commits into from
Aug 20, 2019

Conversation

benaadams
Copy link
Member

@benaadams benaadams commented May 9, 2019

Output something more useful for HostingRequestFinishedLog (which can get intermixed when multiple parallel requests); so it becomes the more interesting event given context; since its kinda junky currently unless you are also outputting full scopes (even then you need to do matching).

Before

info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://ben-laptop/
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 31.180400000000002ms 404
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://ben-laptop/index.html
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 1.6021ms 200 text/html
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://ben-laptop/js/aoa.avatar.js
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 2.6724ms 200 application/octet-stream
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://ben-laptop:26347/textures/spacedrop2048_right1.dds
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://ben-laptop:26347/textures/spacedrop2048_left2.dds
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://ben-laptop:26347/textures/spacedrop2048_top3.dds
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://ben-laptop:26347/textures/spacedrop2048_bottom4.dds
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://ben-laptop:26347/textures/spacedrop2048_back6.dds
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://ben-laptop:26347/textures/spacedrop2048_front5.dds
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 46.6755ms 200 application/octet-stream
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 2.5938000000000003ms 200 application/octet-stream
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 2.7852ms 200 application/octet-stream
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 246.4214ms 200 application/octet-stream
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 280.962ms 200 application/octet-stream
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished in 2.2681ms 200 application/octet-stream

After

info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://ben-laptop/index.html - -
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/1.1 GET http://ben-laptop/index.html - - - 200 2348 text/html 76.8050ms
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 GET http://ben-laptop/js/aoa.avatar.js - -
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/1.1 GET http://ben-laptop/js/aoa.avatar.js - - - 200 39852 application/octet-stream 8.6488ms
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/1.1 POST http://ben-laptop/form application/x-www-form-urlencoded 19
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
      Request finished HTTP/1.1 POST http://ben-laptop/form application/x-www-form-urlencoded 19 - 404 0 - 8.1129ms

Also improve the more detailed semantic logged output

Before
image

After
image

Resolves #10097
Resolves #10556
Resolves #10557

/cc @davidfowl

@benaadams
Copy link
Member Author

benaadams commented May 9, 2019

Also improve the more detailed semantic logged output

Before

image

After

image

@Tratcher
Copy link
Member

Tratcher commented May 9, 2019

@aspnet-hello
Copy link

This comment was made automatically. If there is a problem contact aspnetcore-build@microsoft.com.

I've triaged the above build. I've created/commented on the following issue(s)
https://github.com/aspnet/AspNetCore-Internal/issues/2451

}

// Get or create the _loggingContext
private LogContext LoggingContext => _loggingContext ??= new LogContext();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

??= @pranavkm How many points for that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥇

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My remaining question is how often a Context is constructed without setting any of these properties to a non-default value.

This does seem like a ton of work to turn six properties into one lazy-allocated field with six properties.

Also, does this need to be thread safe? I hope not because new LogContext() could run multiple times if LoggingContext is accessed and initialized twice in a parallel.

Copy link
Member Author

@benaadams benaadams May 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My remaining question is how often a Context is constructed without setting any of these properties to a non-default value.

If you don't want an Activity (152 bytes), 4 ExecutionContexts (160 bytes), 2 strings (76+66 bytes), 4 AsyncLocalMaps (160 bytes) and 3 Logging scope objects (144 bytes); => 758 bytes per request; when you aren't outputting any of it because you aren't at Information level; you might set "Microsoft.AspNetCore.Hosting.Diagnostics": "None" which will do it.

e.g.

"Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.AspNetCore.Hosting.Diagnostics": "None"
    },
    "EventLog": {
      "LogLevel": {
        "Microsoft.AspNetCore.Hosting.Diagnostics": "None"
      }
    }
},

And if you are picking up that extra 758 bytes per request, what's an extra object ;)

Also if you have no logging as per the TE benchmarks; which also benefits from the struct being 64 bytes rather than 82 bytes when passed.

Also, does this need to be thread safe?

No, its contained to before and after the request in hosting and not exposed to the user code in the application

Copy link
Member Author

@benaadams benaadams May 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g.
image

DateTimeFormat (string, byte[], HeaderValues) and ConcurrentDictionary (enumerator) are both heartbeat rather than request allocations so should be subtracted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely forgot that the Context itself was a struct. Too bad it's public. It would have been sooo much easier to turn the Context into a class, but that's breaking.

Copy link
Member Author

@benaadams benaadams May 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if Logging is off for Hosting then the Activity and the LogContext will never be allocated as they are guarded by the Log Enabled check

https://github.com/aspnet/AspNetCore/blob/b744814f06aa145c77db1db496a356d8f9808a4e/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L47-L53

Copy link
Member Author

@benaadams benaadams May 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely forgot that the Context itself was a struct. Too bad it's public. It would have been sooo much easier to turn the Context into a class, but that's breaking.

Well LogContext is now a class if you can work out where to hang it rather than allocating it per request? (when logging or diagnostics is enabled)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if Logging is off for Hosting then the Activity and the LogContext will never be allocated as they are guarded by the Log Enabled check

I figured, but that's unrelated to the LogContext.

Well LogContext is now a class if you can work out where to hang it rather than allocating it per request? (when logging or diagnostics is enabled)

Pooling is an interesting idea. If Context itself was a class, could we pool that instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HttpContext is "pooled" via IHttpContextFactory (lives on connection); but the HostingApplication.Context is side-car'd over it and avoids allocation by living in the stack and being passed around by value.


namespace Microsoft.AspNetCore.Hosting.Internal
{
using static HostingRequestStartingLog;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More @pranavkm points 😄 I see this was introduced "way back" in C# 6, but I haven't seen this used much.

public IDisposable Scope
{
// Get set value or return null
get => _loggingContext?.Scope;
Copy link
Member Author

@benaadams benaadams May 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to make this readonly get but the version of C# AspNetCore uses is too old.

I think updating it will make Code Check unhappy as every auto-property getter gets the [Readonly] attribute

@halter73
Copy link
Member

halter73 commented May 11, 2019

Actually, would it really be too breaking to make HostingApplication.Context a class? Context seems to have turned into an overly-long, overly-complicated class anyway with almost everything being stored via LogContext reference. This PR changes the struct layout anyway which is why the reference assembly needed updating.

@davidfowl

@benaadams
Copy link
Member Author

Actually, would it really be too breaking to make HostingApplication.Context a class?

Would need to introduce a service that the server implemented to not allocated it per request (as per HttpContext); so would be a lot more motion? And since it also contains the HttpContext would you want to bypass its pooling (rather than pooling both)?

Could go with a double interface; if server implements HostContext pooling use that, otherwise use only HttpContext pooling and allocate the HostContext?

@benaadams
Copy link
Member Author

This PR changes the struct layout anyway which is why the reference assembly needed updating.

Only removes the attributes [System.Runtime.CompilerServices.CompilerGeneratedAttribute] which I'm not sure why Code Check cares about; and the dummy int field to indicate it also has non-objects for the C# compiler.

@benaadams
Copy link
Member Author

Could go with a double interface; if server implements HostContext pooling use that, otherwise use only HttpContext pooling and allocate the HostContext?

Something like:

public interface IHostingContextContainer
{
    HostingContext HostingContext { get; }
}

@benaadams
Copy link
Member Author

Though... that might be part of a bigger issue. Like are some routing structures connection orientated rather than request; can scopes be reset and reused etc..?

@benaadams
Copy link
Member Author

With pooling the HostContext #10180

@analogrelay
Copy link
Contributor

@halter73 @Tratcher Good to merge? Everything seems nice and green :)

@davidfowl
Copy link
Member

I'd like to review this before merging. I haven't kept up to date with the changes.

@benaadams benaadams changed the title Betterize Hosting Log [WIP] Betterize Hosting Log May 27, 2019
@benaadams
Copy link
Member Author

Added WIP as it adds a cost; so should be rebased on (merged after) #10319 which removes that cost.

Also raised the issues #10556 and #10557 for the race conditions and incorrect reporting it addresses.

@analogrelay
Copy link
Contributor

@davidfowl @halter73 what's the situation here? Are we ready to merge? Unfortunately it'll probably need another rebase :(.

@halter73
Copy link
Member

I'm fine with merging this. We'll want to keep a close eye on the benchmarks.

@analogrelay
Copy link
Contributor

With master targeting 5.0 now, I'm totally fine with that. I'll re-kick the build to force it to run against the latest master. If that works, I'm OK with merging without a rebase (since we'll rebase as it's merged).

@analogrelay
Copy link
Contributor

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@davidfowl
Copy link
Member

Imagine if we could run benchmarks before committing 😬

@analogrelay
Copy link
Contributor

Someday... someday our dreams will come true :).

@halter73 the rebuild looks to be fine, merge when you're ready.

@benaadams
Copy link
Member Author

Rebased

@halter73
Copy link
Member

/azp run AspNetCore-ci

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@benaadams
Copy link
Member Author

Didn't seem to restart the AspNetCore-helix-test (Tests: Helix x64) test which failed in
Microsoft.AspNetCore.SignalR.StackExchangeRedis.Tests
So I'd assume unrelated?

@halter73
Copy link
Member

Command '/usr/bin/docker logs redisTestContainer' failed with exit code '-1'

Doesn't look to be related to this change. The failure isn't related to the contents of the logs, but the docker logs command exiting with a non-zero code.

@benaadams
Copy link
Member Author

/azp run

@azure-pipelines
Copy link

Commenter does not have sufficient privileges for PR 10102 in repo aspnet/AspNetCore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-hosting Includes Hosting
Projects
None yet
8 participants