Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpContext and TelemetryInitializer

I want to attach the user's "client_id" claim as a property to every request sent to Application Insights.

From what I've read, I should be implementing ITelemetryInitializer but I need the HttpContext for the request in order to retrieve "client_id". See my initialiser:

public class ClaimTelemetryInitializer : ITelemetryInitializer
{
    public HttpContext HttpContext { get; set; }

    public void Initialize(ITelemetry telemetry)
    {
        this.AddTelemetryContextPropertFromClaims(telemetry, "client_id");
    }

    private void AddTelemetryContextPropertFromClaims(ITelemetry telemetry, string claimName)
    {
        if (HttpContext != null)
        {
            var requestTelemetry = telemetry as RequestTelemetry;

            var claim = HttpContext.User.Claims.SingleOrDefault(x => x.Type.Equals(claimName, StringComparison.InvariantCultureIgnoreCase));

            if (claim != null)
            {
                telemetry.Context.Properties[claimName] = claim.Value;
            }
        }
    }
}

I could create an action filter to set the context each time, but this feels awful:

public class TrackClaimsAttribute : ActionFilterAttribute
{
    public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var initialiser = TelemetryConfiguration.Active.TelemetryInitializers.OfType<ClaimTelemetryInitializer>().Single();

        initialiser.HttpContext = context.HttpContext;

        return base.OnActionExecutionAsync(context, next);
    }
}

Is there a better way to achieve what I want to do?

like image 569
Dave New Avatar asked Jul 16 '16 16:07

Dave New


People also ask

What is ITelemetryInitializer?

Add/modify properties: ITelemetryInitializer. Use telemetry initializers to enrich telemetry with additional information or to override telemetry properties set by the standard telemetry modules. For example, Application Insights for a web package collects telemetry about HTTP requests.

What is AddApplicationInsightsTelemetry?

AddApplicationInsightsTelemetry prioritizes configuration from appsettings. json , irrespective of the order in which providers are added. Use the services. AddApplicationInsightsTelemetry(IConfiguration) method to read configuration from IConfiguration without this preferential treatment for appsettings.

How do I add application insights to Web API?

Add Application Insights automatically From within your ASP.NET web app project in Visual Studio: Select Project > Add Application Insights Telemetry > Application Insights Sdk (local) > Next > Finish > Close.

How do I enable application insight on Azure?

Enable monitoringSelect Application Insights in the left pane for your app service. Then select Enable. Create a new resource or select an existing Application Insights resource for this application. When you select OK to create a new resource, you're prompted to Apply monitoring settings.


3 Answers

You should implement the WebTelemetryInitializerBase which provides you the HttpContext.

Your code should look like:

public class ClaimTelemetryInitializer : WebTelemetryInitializerBase
{
    protected override void OnInitializeTelemetry(
            HttpContext platformContext,
            RequestTelemetry rootRequestTelemetry, 
            ITelemetry telemetry) {

            var claim = HttpContext.User.Claims.SingleOrDefault(x => x.Type.Equals(claimName, StringComparison.InvariantCultureIgnoreCase));

            if (claim != null)
            {
                telemetry.Context.Properties[claimName] = claim.Value;
            }
    }
}
like image 173
yonisha Avatar answered Sep 26 '22 17:09

yonisha


I would suggest to inject an HttpContextAccessor instance in the ClaimTelemetryInitializer class's constructor, and then you could use it to extract values from the HttpContext. Or, even better, create a base class for your TelemetryInitializer, and use it's constructor to inject the HttpContextAccessor instance.

For example:

    protected ClaimTelemetryInitializer(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    public void Initialize(ITelemetry telemetry)
    {
        var context = this.httpContextAccessor.HttpContext;
        if (context == null)
        {
            return;
        }

        var claim = context.User.Claims.SingleOrDefault(x => x.Type.Equals(claimName, StringComparison.InvariantCultureIgnoreCase));
        //Do logic here...
    }
like image 27
garam93 Avatar answered Sep 24 '22 17:09

garam93


I wish this were designed into AppInsights but you can directly use the static HttpContext.Current. You can use it's per-request Items dictionary as a short term (near stateless) storage space to deliver your custom values to the custom telemetry handler.

So try

class AppInsightCustomProps : ITelemetryInitializer
{
    public void Initialize(ITelemetry telemetry)
    {
        var requestTelemetry = telemetry as RequestTelemetry;
        // Is this a TrackRequest() ?
        if (requestTelemetry == null) return;

        var httpCtx = HttpContext.Current;
        if (httpCtx != null)
        {
            var customPropVal = (string)httpCtx.Items["PerRequestMyCustomProp"];
            if (!string.IsNullOrWhiteSpace(customPropVal))
            {
                requestTelemetry.Properties["MyCustomProp"] = customPropVal;
            }
        }
    }
}

And to program the desired custom property, anywhere in your request pipeline have something like

if (HttpContext.Current != null)
{
    HttpContext.Current.Items["PerRequestMyCustomProp"] = myCustomPropValue;
}
like image 36
DeepSpace101 Avatar answered Sep 23 '22 17:09

DeepSpace101