question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Don't use static Agent API on APM middleware registration

See original GitHub issue

UseElasticApm function relies on static Agent.Setup(config), which throws if registering the middleware was already done. But this is not considering the scenario where more than one IHost or IWebHost instances can be running on the same process.

I think the common practice is registering a new IApmAgent instance as singleton to IServiceCollection and injecting it into the middleware. AutoMapper had the same issue previously and they fixed it following this approach and they just removed the static API for good in version 9.

That said, having IApmAgent registered in DI allows injecting into controllers/services instead of relying on statics as documentation recommends.

Furthermore, current configuration approach only allows configuring the Agent form IConfiguration and would be flexible to have an Options Builder approach like EF Core has.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:17
  • Comments:10 (2 by maintainers)

github_iconTop GitHub Comments

4reactions
gregkalaposcommented, Oct 8, 2020

We released the Elastic.Apm.Extensions.Hosting package which I believe will make this better - it’s currently beta, in the next release we can remove the beta flag. That package has an extension method for IHostBuilder and you don’t need to register a middleware anymore, it basically creates the agent and adds its components to IServiceCollection.

It’s also part of the NetCoreAll package that most of the people use.

Just do this in when you create your hostbuilder

public static IHostBuilder CreateHostBuilder(string[] args) =>
	Host.CreateDefaultBuilder(args)
		.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); })
		.UseAllElasticApm(); //<- this is from `Elastic.Apm.NetCoreAll` and uses the Elastic.Apm.Extensions.Hosting package

Once you do that, you’ll be able to dependency inject a tracer - for example:

public class HomeController : Controller
{
	private readonly ITracer _tracer;

	public HomeController(ITracer tracer) => _tracer = tracer; //you get the tracer from DI

	public IActionResult SomeMethod()
	{
		//now you can use the non static API
		var span = _tracer.CurrentSpan?.StartSpan("MySpan", "SampleSpanType");

		try
		{
			//...app code
		}
		finally
		{
			span?.End();
		}

		return View();
	}
}

Sometimes I see people wanting the whole agent instance - I don’t understand why people want that (happy to hear what you want to do with it), nevertheless, you can also inject an IApmAgent instance into your class. I believe most people want to manually create spans and transactions and for that I’d suggest injecting an ITracer - the tracer is the one that let’s you create those.

Now, in the latest beta release of this package I believe we don’t have ASP.NET Core monitoring (which is a huge missing thing), but that’s already on master, so in the next release that’ll be there and you can try it already on master.

Also, I’d like to point out that there is an Agent.IsConfigured property which lets you avoid the double initialization problem.

On the “static Agent API” part: I understand everyone’s concerns, I hope the approach above satisfies peoples needs - about the reasons why we can’t drop it I wrote here and maybe this here is also related.

Let me know if this would help with everyone’s problem here. Also maybe give a try to this new package.

3reactions
dasMullicommented, Oct 14, 2020

That package has an extension method for IHostBuilder and you don’t need to register a middleware anymore

@gregkalapos I just transitioned my current project to it, this resolves any double initialization problems (race conditions… one in 4 startups is lucky) I’ve had with multiple IHostedServices next to the ASP.NET Core stack but then no HTTP traces are created.

After playing around, this is the only working single configuration I could come up with that works under all cirumcstances:

    public static class HostBuilderExtensions
    {
        public static IHostBuilder UseApm(this IHostBuilder hostBuilder)
        {
            hostBuilder
                .UseElasticApm(
                    new HttpDiagnosticsSubscriber(),
                    new EfCoreDiagnosticsSubscriber(),
                    new SqlClientDiagnosticSubscriber(),
                    new ElasticsearchDiagnosticsSubscriber(),
                    new AspNetCoreDiagnosticsSubscriber()) // not included when calling hostBuilder.UseAllElasticApm()
                .ConfigureServices(svc =>
                {
                    // custom project-specific helpers and extensions for working with transactions & spans and custom log context enrichment
                    svc.AddSingleton<IApmCorrelationHelper, ApmCorrelationHelper>();
                    svc.AddSingleton<MvcActionObservabilityFilter>();
                    svc.Configure<MvcOptions>(
                        mvcOptions => mvcOptions.Filters.Add(
                            new ServiceFilterAttribute(typeof(MvcActionObservabilityFilter)) { IsReusable = true }));

                    // fix missing HTTP transactions when using Elastic.Apm.Extensions.Hosting APIs
                    svc.AddTransient<IStartupFilter, ApmStartupFilter>();
                    if (Type.GetType("Elastic.Apm.Api.Tracer, Elastic.Apm") is { } tracerType
                        && Type.GetType("Elastic.Apm.ApmAgent, Elastic.Apm") is { } agentType)
                    {
                        svc.AddSingleton(tracerType, sp => sp.GetRequiredService<ITracer>());
                        svc.AddSingleton(agentType, sp => sp.GetRequiredService<IApmAgent>());
                    }
                });
            return hostBuilder;
        }

        private class ApmStartupFilter : IStartupFilter
        {
            public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
            {
                return builder =>
                {
                    if (Type.GetType("Elastic.Apm.AspNetCore.ApmMiddleware, Elastic.Apm.AspNetCore") is { } apmMiddleware)
                    {
                        builder.UseMiddleware(apmMiddleware);
                    }

                    next(builder);
                };
            }
        }
    }

Am I missing some configuration option that provides a single configuration and allows for ASP.NET Core + IHostedService usage without startup race issues? (plus I really don’t want to miss some transactions that happen at the app start in these IHostedServices)

Since this needs reflection to access internal types, I’d be happy for a version that works with public API only.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Configuration | APM Python Agent Reference [6.x]
To adapt the Elastic APM agent to your needs, configure it using environment variables or framework-specific configuration. You can either configure the agent...
Read more >
Troubleshoot APM Java Agent Deployment
This error message indicates that the installer tried to issue a request to the OMC server or gateway, but did not receive any...
Read more >
Sending Traces to Datadog
The Agent then sends the traces to the Datadog backend to be displayed in the UI. The APM pipeline. Depending on the programming...
Read more >
App Server Agents Supported Environments
AppDynamics supports the use of the Java Agent to instrument any application component running on a supported JVM, irrespective of how that component...
Read more >
ElasticApm (co.elastic.apm:apm-agent-api 1.7.0 API)
This class is the main entry point of the public API for the Elastic APM agent. The tracer gives you access to the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found