The Elastic APM .NET agent is surprisingly adept at instrumenting IIS applications, but its true power lies in its ability to weave application-level insights directly into the fabric of your web server’s request lifecycle.
Let’s see it in action. Imagine a typical ASP.NET MVC application hosted on IIS. When a request hits the IIS worker process, the APM agent, injected via a .config file, intercepts it before the application’s routing logic even begins.
<configuration>
<system.webServer>
<modules>
<add name="ElasticAPMModule" type="Elastic.Apm.Net4.ElasticAPMModule, Elastic.Apm.Net4" preCondition="managedRuntime" />
</modules>
</system.webServer>
<appSettings>
<add key="ELASTIC_APM_SERVER_URLS" value="http://localhost:8200" />
<add key="ELASTIC_APM_SERVICE_NAME" value="MyIISApp" />
<add key="ELASTIC_APM_ENVIRONMENT" value="production" />
</appSettings>
</configuration>
This ElasticAPMModule is a native IIS module that hooks into the IHttpModule pipeline. It starts a new APM transaction for each incoming HTTP request. As the request traverses the pipeline – through authentication, authorization, and finally to your application’s controllers – the agent automatically instruments common framework calls: database queries (Entity Framework, Dapper), outgoing HTTP requests, and even custom code marked with APM attributes.
When the response is sent back, the transaction is completed, and a detailed trace, including all captured spans, is sent to the configured APM Server. This provides a granular view of request duration, identifying bottlenecks within your application code, external dependencies, or database interactions. You can see exactly which controller action took the longest, which SQL query was slow, or which external API call introduced latency.
The core problem this solves is the "black box" nature of traditional web server logging. You might know a request took 5 seconds, but why? The APM agent transforms that 5-second mystery into a detailed timeline. It gives you the "what" (a slow request), the "where" (a specific controller action), and the "how" (a slow database query or external call).
The exact levers you control are primarily through the appSettings in your web.config (or appsettings.json for newer .NET Core apps). ELASTIC_APM_SERVER_URLS is critical – it tells the agent where to send the data. ELASTIC_APM_SERVICE_NAME is how your application will be identified in the APM UI. ELASTIC_APM_ENVIRONMENT allows you to distinguish between development, staging, and production environments. Beyond these, you can fine-tune sampling rates, disable specific instrumentation, and configure custom labels for deeper context.
What most people don’t realize is that the agent’s automatic instrumentation extends beyond just the most common scenarios. It has built-in support for a wide array of .NET technologies, including WCF services hosted on IIS, and even older ASP.NET Web Forms applications. The agent doesn’t just passively observe; it actively participates in the request lifecycle, ensuring that even deeply nested operations are captured as distinct spans within your overall transaction trace.
The next logical step after getting your IIS application instrumented is to explore distributed tracing, which connects requests across multiple microservices.