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

Add IHostBuilder support #1015

Closed
wants to merge 22 commits into from

Conversation

Vandersteen
Copy link

As discussed here: #1001

First quick and dirty commit

My first thought after beginning to add an implementation: would it be bad if it acted 100% like the IWebHostBuilder extension.

The user would have the choice between:

Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseSentry()
        webBuilder.UseStartup<Startup>();
    });

And

Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    })
    .UseSentry();

Only 'caveat' right now is that it would add a few classes that would never be used in a WorkerService environment:

  • IStartupFilter, SentryStartupFilter
  • IRequestPayloadExtractor, FormRequestPayloadExtractor
  • IRequestPayloadExtractor, DefaultRequestPayloadExtractor

However i'm not sure this is such an issue, thoughts ?

Also, I haven't taken #1001 (comment) into consideration

@Vandersteen Vandersteen requested a review from bruno-garcia as a code owner May 27, 2021 12:51
@codecov-commenter
Copy link

codecov-commenter commented May 27, 2021

Codecov Report

Merging #1015 (7c20f88) into main (f84c23b) will increase coverage by 0.01%.
The diff coverage is 91.11%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1015      +/-   ##
==========================================
+ Coverage   81.43%   81.45%   +0.01%     
==========================================
  Files         193      195       +2     
  Lines        6325     6348      +23     
  Branches     1526     1537      +11     
==========================================
+ Hits         5151     5171      +20     
- Misses        739      740       +1     
- Partials      435      437       +2     
Impacted Files Coverage Δ
.../Sentry.AspNetCore/SentryAspNetCoreOptionsSetup.cs 100.00% <ø> (ø)
...ry/Extensibility/DefaultRequestPayloadExtractor.cs 81.81% <0.00%> (ø)
...c/Sentry.AspNetCore/SentryHostBuilderExtensions.cs 81.25% <81.25%> (ø)
...DependencyInjection/ServiceCollectionExtensions.cs 100.00% <100.00%> (ø)
...entry.AspNetCore/SentryLoggingBuilderExtensions.cs 100.00% <100.00%> (ø)
...entry.AspNetCore/SentryWebHostBuilderExtensions.cs 81.25% <100.00%> (-7.22%) ⬇️
src/Sentry/SentryClient.cs 76.56% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f84c23b...7c20f88. Read the comment docs.

@bruno-garcia bruno-garcia requested a review from Tyrrrz May 28, 2021 20:45
@Tyrrrz
Copy link
Contributor

Tyrrrz commented May 31, 2021

Can we converge on just one way of using UseSentry() somehow?

@Vandersteen
Copy link
Author

Can we converge on just one way of using UseSentry() somehow?

What exactly do you mean ?

@Tyrrrz
Copy link
Contributor

Tyrrrz commented May 31, 2021

Can we converge on just one way of using UseSentry() somehow?

What exactly do you mean ?

Sorry. I was referring to how having both the option to put UseSentry() inside ConfigureWebHostDefaults(...) and outside of it seems a bit confusing from user perspective. I wonder if we can do anything about that.

@Vandersteen
Copy link
Author

Can we converge on just one way of using UseSentry() somehow?

What exactly do you mean ?

Sorry. I was referring to how having both the option to put UseSentry() inside ConfigureWebHostDefaults(...) and outside of it seems a bit confusing from user perspective. I wonder if we can do anything about that.

I'm not sure we can do anything about that.
IWebHostBuilder was the predecessor for IHostBuilder

I understand it could be confusing.

One way I suggested to 'tackle' that problem was explained in the original issue

  • UseSentryWeb() on IWebHostBuilder
  • UseSentryCore() on IHostBuilder

But that was also not desirable behaviour

@Tyrrrz
Copy link
Contributor

Tyrrrz commented May 31, 2021

Let's see what @bruno-garcia thinks about this

@bruno-garcia
Copy link
Member

Not a fan of the two methods either.
Can we phase out the one on IWebHostBuilder then? Can we do everything through IHostBuilder instead?

@Vandersteen
Copy link
Author

Vandersteen commented May 31, 2021

My 2 cents, so you can make an informed decision

Can we phase out the one on IWebHostBuilder then?

That could indeed be an option, deprecate IWebHostBuilder extensions.
However, do note that WebHost.CreateDefaultBuilder will no longer be supported then, forcing users to use Host.CreateDefaultBuilder

While both still seem acceptable ways to create a Host from asp.net core's standpoint. There does not seem to be plans to deprecate/remove WebHost

// no longer supported
WebHost.CreateDefaultBuilder()
    .UseSentry()

// required
Host.CreateDefaultBuilder()
   .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    })
    .UseSentry()

As far as I know, both of these are equivalent.
I don't see any signs that IWebHostBuilder will be deprecated as well (f.e. it is used in the callback of ConfigureWebHostDefaults)

Can we do everything through IHostBuilder instead?

I can't see anything that you cannot achieve through the IHostBuilder that you can with the IWebHostBuilder

But don't take my word for it :)

@bruno-garcia
Copy link
Member

Sounds like we do need to keep both. So one way is: We add the infrastructure for IHostBuilder and update our docs to show only the use of this extension going forward.

We could add a note that UseSentry exists on IWebHostBuilder but is not needed if the one in IHostBuilder was already called.

All of that assumes that we can make them equivalent. If we can add "more" features through IWebHostBuilder this wouldn't really work.

@Vandersteen
Copy link
Author

Exactly, at the time of writing, the current IHostBuilder extensions in this PR are a 'literal' copy of the IWebHostBuilder with IWebHostBuilder replaced with IHostBuilder and some namespace / include changes.

@bruno-garcia
Copy link
Member

Exactly, at the time of writing, the current IHostBuilder extensions in this PR are a 'literal' copy of the IWebHostBuilder with IWebHostBuilder replaced with IHostBuilder and some namespace / include changes.

Sounds like we could pull the code to a single place and forward the call from both places then?

Is the code reentrant already? We can expect that ppl will call it twice (from each interface).

@Vandersteen
Copy link
Author

Sounds like we could pull the code to a single place and forward the call from both places then?

I looked into that, but IHostBuilder and IWebHostBuilder are not the same interface, so code sharing is not easily done

Is the code reentrant already? We can expect that ppl will call it twice (from each interface).

No not yet, I will comment in code where I see issues


_ = logging.Services
.AddSingleton<IConfigureOptions<SentryAspNetCoreOptions>, SentryAspNetCoreOptionsSetup>();
_ = logging.Services.AddSingleton<ILoggerProvider, SentryAspNetCoreLoggerProvider>();
Copy link
Author

Choose a reason for hiding this comment

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

@bruno-garcia using TryAddSingleton here can cause issues, as it won't register it if let's say Serilog or other already exists, in this case you want the 'override' ability

Copy link
Member

Choose a reason for hiding this comment

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

That's true. Is there a way to check if the specific specification has been added? I believe it was possible to check the items in the collection so we can verify if specifically SentryAspNetCoreLoggerProvider was already added and make sure we only add it once.

.AddSingleton<IConfigureOptions<SentryAspNetCoreOptions>, SentryAspNetCoreOptionsSetup>();
_ = logging.Services.AddSingleton<ILoggerProvider, SentryAspNetCoreLoggerProvider>();

_ = logging.AddFilter<SentryAspNetCoreLoggerProvider>(
Copy link
Author

Choose a reason for hiding this comment

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

There is no TryAddFilter, i'm not sure it is an issue if this is added twice thought

Copy link
Member

Choose a reason for hiding this comment

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

Not sure either, I guess something we'd need to test? There's no way to look into the filters already added?

Copy link
Author

Choose a reason for hiding this comment

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

I will take a closer look at it

Copy link
Author

@Vandersteen Vandersteen Jun 1, 2021

configureSentry?.Invoke(context, sentryBuilder);
});

_ = builder.ConfigureServices((c, s) => _ = s.AddTransient<IStartupFilter, SentryStartupFilter>());
Copy link
Author

Choose a reason for hiding this comment

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

TryAddTransient is also an issue here, if any IStartupFilter was already added (which most likely it will) this will not be registered

Copy link
Member

Choose a reason for hiding this comment

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

Something lke:

            if (!s.Any(d =>  d.ServiceType == descriptor.ServiceType &&    d.GetImplementationType() == implementationType))

Copy link
Author

Choose a reason for hiding this comment

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

Oh nice, I didn't think of that.
Just ran a quick test and yes that would work.

@Vandersteen
Copy link
Author

I dropped some comments here and there (see above)

In some code bases, I worked around this with a private static bool that keeps track if it is already called or not.
I could proceed and resolve those that way, so that calling it on both interfaces does not give strange issues

@bruno-garcia
Copy link
Member

In some code bases, I worked around this with a private static bool that keeps track if it is already called or not.
I could proceed and resolve those that way, so that calling it on both interfaces does not give strange issues

This strategy won't work in the ASP.NET Core case because even on a normal run of it, 2 containers are created and the first time we'd add (before the static fields are set) would end up on a dead host, and the new one wouldn't have it.
See: https://github.com/bruno-garcia/repro-logger-provider-double-instantiation #103 (comment) aspnet/Hosting#1499

@Vandersteen
Copy link
Author

Took the comments into consideration, made AddSentry() idempotent on

  • IHostBuilder
  • IWebHostBuilder

@Vandersteen Vandersteen requested a review from bruno-garcia June 4, 2021 11:49
Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

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

I have just a nitpick but this already looks good to me!

Before we merge, It would be nice to have some coverage of this by some test, even an integration test of sorts.
I assume we have such test in the ASP.NET Core integration already then it could be copied over since it's the same thing.

It could also be used from one of the samples (Samples.ME.Logging or GenericHost actually)?
The samples make sure we have an easy way to run it and verify it works. And also to 'show ppl' some examples. And to have usage of the API to see what it looks like.

/// <returns></returns>
internal static IServiceCollection AddSentryStartupFilter(this IServiceCollection serviceCollection)
{
if(!serviceCollection.IsRegistered<IStartupFilter, SentryStartupFilter>())
Copy link
Member

Choose a reason for hiding this comment

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

nit: one day we'll have a linter that reformats and pushed to the branch :)

Could you please do this throughout the change?

Suggested change
if(!serviceCollection.IsRegistered<IStartupFilter, SentryStartupFilter>())
if (!serviceCollection.IsRegistered<IStartupFilter, SentryStartupFilter>())

@Vandersteen
Copy link
Author

Vandersteen commented Jun 14, 2021

Latest commit should've fixed failing tests

Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

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

Thanks for this feature! There are a few minor things to address.
I'd even say it's likely good enough to merge and we can adjust things on our end. What would you say @Tyrrrz ?

samples/Sentry.Samples.AspNetCore.Basic/Program.cs Outdated Show resolved Hide resolved
@@ -0,0 +1,70 @@
using System;
Copy link
Member

Choose a reason for hiding this comment

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

I would prefer we remove this file. The idea of Basic is to have the simplest possible example.
I'd prefer we continued to use a single file for this example. We can even refactor this to use top level statements.

We can remove the WebHost example and use only your new approach

Copy link
Author

Choose a reason for hiding this comment

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

What do you think of the current version of basic ?

l.AddSentry();
})
.ConfigureLogging(b => b.AddConsole())
.UseSentry()
Copy link
Member

Choose a reason for hiding this comment

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

Very nice!

@@ -12,15 +12,15 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="../../src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj" />
<ProjectReference Include="../../src/Sentry.AspNetCore/Sentry.AspNetCore.csproj" />
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to change the dependency to ASP.NET Core integration? The new types are all on the S.E.L, right?

Suggested change
<ProjectReference Include="../../src/Sentry.AspNetCore/Sentry.AspNetCore.csproj" />
<ProjectReference Include="../../src/Sentry.Extensions.Logging/Sentry.Extensions.Logging.csproj" />

Copy link
Author

Choose a reason for hiding this comment

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

New types are not on S.E.L. right now, see comment below

/// </summary>
/// <param name="builder">The builder.</param>
/// <returns></returns>
public static IHostBuilder UseSentry(this IHostBuilder builder)
Copy link
Member

Choose a reason for hiding this comment

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

IHostBuilder lives in Microsoft.Extensions.Hosting so this could be added to Sentry.Extensions.Logging.

Copy link
Author

Choose a reason for hiding this comment

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

I would've personally kept it in the asp.net core part, as it is the replacement of WebHostBuilder in the future.
Would you like me to move it ?

@Tyrrrz
Copy link
Contributor

Tyrrrz commented Jun 16, 2021

I would also prefer to avoid having a dependency on Sentry.AspNetCore when one needs a generic host integration. It seems counter-intuitive. Sentry.Extensions.Logging is in a weird place right now, in a sense that it should probably be called Sentry.Shared or something, but I think it's still a better place for the generic IHostBuilder stuff.

@Tyrrrz Tyrrrz changed the title Draft: Add IHostBuilder support Add IHostBuilder support Jun 16, 2021
@Vandersteen
Copy link
Author

Vandersteen commented Jun 16, 2021

I understand

How do we proceed with these dependencies:

  • Adding the Startupfilter / middleware
  • Calling the AddSentry() on servicecollection extensions (and it's depedencies on aspnetcore)
  • SentryAspNetCoreOptions
  • Microsoft.AspNetCore.Hosting

@Tyrrrz
Copy link
Contributor

Tyrrrz commented Jun 16, 2021

I'm also okay with merging as is since the PR has been up for a while, and then improving on it as we go along 😉

@Vandersteen
Copy link
Author

I'll leave that up to you guys

@bruno-garcia
Copy link
Member

bruno-garcia commented Jun 16, 2021

I understand

How do we proceed with these dependencies:

  • Adding the Startupfilter / middleware
  • Calling the AddSentry() on servicecollection extensions (and it's depedencies on aspnetcore)
  • SentryAspNetCoreOptions
  • Microsoft.AspNetCore.Hosting

Could we move all of that to Sentry.Extensions.Logging?
Oh I see, we'd need to add Microsoft.AspNetCore.Hosting as a dependency or:

  <ItemGroup Condition="'$(TargetFramework)' != 'netstandard2.0'">
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
  </ItemGroup>

Instead of AspNetCore.Hosting, isn't this Microsoft.Extensions.Hosting?

So it's either that or a new Sentry.Extensions.Hosting and have Sentry.AspNetCore depend on it?

@Vandersteen
Copy link
Author

I'll take a closer look tomorrow

@Vandersteen
Copy link
Author

Vandersteen commented Jun 16, 2021

Well I couldn't keep myself, took a quick gander.

Things like:

  • IApplicationBuilder
  • IStartupFilter
  • HttpContext

All require AspNetCore ref

I believe you won't be able to substitute IHostBuilder.UseSentry() for IWebHostBuilder.UseSentry() completely if you go that path

IHostBuilder.UseSentry() could contain the minimum requirements in a Sentry.Extensions.Hosting but will not be a replacement for IWebHostBuilder.UseSentry()

I don't see anything wrong with that, if it's clear from a usage point of view.

@Vandersteen
Copy link
Author

Vandersteen commented Jul 5, 2021

@bruno-garcia Going forward, I believe a new Sentry.Extensions.Hosting might be the best bet. Considering future .NET MAUI support

Do you wish for me to proceed on that path ?
Anything special I should know when setting up a new Project ?

@bruno-garcia
Copy link
Member

bruno-garcia commented Jul 10, 2021

@bruno-garcia Going forward, I believe a new Sentry.Extensions.Hosting might be the best bet. Considering future .NET MAUI support

Do you wish for me to proceed on that path ?

Yeah sounds like we'll need it particularly because we're looking at adding support to MAUI as soon as it's out so we'd really appreciate your help with this!

Anything special I should know when setting up a new Project ?

Please take a look at the other libraries like Sentry.Extensions.Logging in terms of structure, like the details in the csproj for the lib and the tests.

@Vandersteen
Copy link
Author

Ok, thank you for the feedback.

I might have some time this week to continue working on this

@Cyral
Copy link

Cyral commented Aug 27, 2021

I would also prefer to avoid having a dependency on Sentry.AspNetCore when one needs a generic host integration. It seems counter-intuitive. Sentry.Extensions.Logging is in a weird place right now, in a sense that it should probably be called Sentry.Shared or something, but I think it's still a better place for the generic IHostBuilder stuff.

I don't have anything to add about how this should be implemented but I just wanted to voice my support for adding this. The above describes my use case exactly, as I use IHostBuilder to build non-asp.net applications that make use of dependency injection. This would let all of my services use the same Sentry init logic instead of using the DI version in some places and the using wrapper in other places.

@mattjohnsonpint
Copy link
Contributor

Thanks again for the time spent here, but we've decided not to do this. See #190 (comment)

Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants