Problems adding insights to Owin based project - c#

I have been trying to add Azure application insights to a few projects. The whole experience was seamless with a .net core app. However, when I tried to update the Cloud role name property, that is where I could not find a lot for an OWIN based app. I want the name of the bubble in Insights Application Map to appear what I set in this property (My API for example) but it keeps resorting to the resource name that I have for this resource in Azure (my-azure-api). After scouring through most online resources, I was able to do the following which does not work.
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.Extensibility;
namespace MyApp.Insights
{
public class RoleNameInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
// set role name correctly here.
telemetry.Context.Cloud.RoleName = "My API";
}
}
}
Also added the following in the applicationinsights.config
<Add Type="MyApp.Insights.RoleNameInitializer, MyApp"/>
Added the following to the startup class too (Just as a precaution)
using IntegratedTeleHealthPlatformApi.Insights;
using Microsoft.ApplicationInsights.Extensibility;
using Owin;
namespace MyApp
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
TelemetryConfiguration
.Active
.TelemetryInitializers
.Add(new RoleNameInitializer());
ConfigureAuth(app);
ApplyDatabaseMigrations();
}
}
}

I just setup a simple owin based asp.net project(asp.net web application, then in nuget install Microsoft.Owin.Host.SystemWeb).
After the setup, in visual studio -> Project -> Add Application Insights Telemetry:
My custom TelemetryInitializer as below:
Then just add the initializer to the applicationinsights.config:
And after execution, the role name is the one which I set in the initializer:
Please have a try if it's ok at your side. And to make sure your RoleNameInitializer is called, you can set breakpoint there to see if it's called or not.

Related

Has anyone had any luck getting a .NET 6 REST API project to work in an AWS Lambda?

I have a very simple ASP.NET 6.0 Web API application, with a Home controller with one Get method returning text:
[ApiController]
[Route("[controller]")]
public class HomeController : Controller
{
// GET
[HttpGet]
public IActionResult Get()
{
return Ok(new { message = "Hello, World!" });
}
}
I've been able to get ASP.NET projects < 6.0 to work, but with .NET 6 I'm running into issues. There's no longer a Startup class; that functionality moved to the implicit Program class. So in my LambdaEntryPoint class (which inherits from APIGatewayProxyFunction) I'm using Program as the Startup:
protected override void Init(IWebHostBuilder builder)
{
builder.UseStartup<Program>();
}
I'm getting an error when manually testing from the AWS console: Amazon.Lambda.RuntimeSupport.ExceptionHandling.LambdaValidationException: Unable to load assembly. I believe that my naming is correct: MyAssembly::MyAssembly.LambdaEntryPoint::FunctionHandlerAsync
The only thing different about my Program class is that I had to add public partial class Program { } at the bottom so the unit tests would could find Program and run.
My Test event looks like this:
{
"resource": "/Home",
"path": "/Home",
"httpMethod": "GET",
"isBase64Encoded": true
}
It is a cut down version of the default Amazon API Gateway AWS Proxy
The fact that you don't have a Startup class doesn't mean you can't make one, I made a Startup class for my API in .NET 6 and it works fine
You do not need to add a Startup.cs class.
The only thing you have to do if you are working with NET6 is in your Program.cs file you have to add this line of code.
This is if you are using a rest API or API GATEWAY
builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);
This is if you are using your lambda with a url
builder.Services.AddAWSLambdaHosting(LambdaEventSource.HttpApi);
..and in aws you just have to change the handler with the name of your project and that's it.
MyAssembly
See the example here in aws

Application Insights is not including properties set by custom ITelemetryInitializer

(Added UPDATE 1 below which I think answers this question)
In a fairly simple ASP.NET Core 2 web app I have initialized in Program like this:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseApplicationInsights()
.Build();
I haven't configured an instrumentation key in appSettings.json yet because for now I'm running locally. When I run my app and force an exception on load of my home page I can see the exception logged in Visual Studio's telemetry search for Application Insights.
I now want to capture some details about the request in every logged event. I've followed some guidance and created an implementation of ITelemetryInitializer:
public class CustomTelemetryInitializer : ITelemetryInitializer
{
private const string UserIdKey = "UserId";
private readonly IUserService _userService;
public CustomTelemetryInitializer(IUserService userService)
{
_userService = userService;
}
public void Initialize(ITelemetry telemetry)
{
if (!(telemetry is RequestTelemetry requestTelemetry)) return;
var props = requestTelemetry.Properties;
if (!props.ContainsKey(UserIdKey))
{
var user = _userService.GetCurrentUser();
if (user != null)
{
props.Add(UserIdKey, user.UserId);
}
}
}
}
This is largely following this guide. IUserService is just a service which uses IHttpContextAccessor to get the current ClaimsPrincipal. You can see I'm trying to add the user's ID to the custom telemetry properties.
I've registered this ITelemetryInitializer in Startup like this:
services.AddSingleton<ITelemetryInitializer, CustomTelemetryInitializer>();
When I run my app again, I can see the debugger running through CustomTelemetryInitializer and setting the property correctly. However, when I review the events logged in app insights, the custom property is not included. They look identical to the screenshot above.
I've tried moving the app insights initialization out of Program and instead initializing it in Startup after registering the ITelemetryInitializer using services.AddApplicationInsightsTelemetry(), but this makes no difference.
Anyone know what I'm doing wrong?
UPDATE 1
I've realised I made a mistake. My custom properties are being included for Request events after all. But not the Exception events. But I've now realised that these Exception events take a different implementation of ITelemetry in Initialize i.e. TraceTelemetry. So I hadn't realised I was excluding these events from my custom properties.
Glad you figured it out.
All ITelemetry implementations in the SDK implements, ISupportProperties which gives it Properties collection. If you want to attach properties to every telemetry, you can cast to ISupportProperties and set props.

How to integrate server side Application Insights telemetry into my Umbraco / Articulate web site ideally?

I have created an Umbraco web site by doing the following
Open Visual Studio
Create a new empty MVC project
Added the current Umbraco nuget package
Hosted the project in Azure
Successfully executed Umbraco installer
Installed the current Articulate package
Now I would like to integrate Application Insights. The client side part is very easy, I just need to add some JavaScript code into the master view.
For the server side part I need to add this code:
using System;
using System.Web.Mvc;
using Microsoft.ApplicationInsights;
namespace MVC2App.Controllers
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AiHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext != null && filterContext.HttpContext != null && filterContext.Exception != null)
{
//If customError is Off, then AI HTTPModule will report the exception
if (filterContext.HttpContext.IsCustomErrorEnabled)
{
// Note: A single instance of telemetry client is sufficient to track multiple telemetry items.
var ai = new TelemetryClient();
ai.TrackException(filterContext.Exception);
}
}
base.OnException(filterContext);
}
}
}
// then register AiHandleErrorAttribute in FilterConfig:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AiHandleErrorAttribute());
}
}
My question in detail is the following:
How do I integrate this code ideally touching as less as possible in the Umbraco core to make updates in the future as easy as possible? What is the best approach to integrate the code?
Do I need to touch the Umbraco core or is it possible just to change the Articulate code? Or even better: Can I create my own Umbraco package which can add Application Insights feaature to my Umbraco instance (maybe both client and server side part)?
You shouldn't need to touch the Umbraco Core for this. You could quite easily do it as your own custom code, which you could then turn into a plugin. The attribute you could just add as a class, and to hook it in, rather than the FilterConfig event, you can use the Umbraco Startup Handler: https://our.umbraco.org/documentation/reference/events/application-startup
You could register your filter in the ApplicationStarted event handler of the startup handler, that should work.

Path not found error

I installed SignalR 2.0-rc1, and:
1: Created a hub:
public class Socials : Hub
{
public void PublicChat(string message)
{
Clients.All.PublicChat(new { message });
}
}
2: Created a startup class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
3: Registered it in web.config:
<add key="owin:AppStartup" value="Scyk.Startup, Scyk"/> //Scyk is my main namespace, also a project name, I placed Startup class in there.
Now, https://myhost.com/signalr/hubs is generating javascript file properly, but when I open developer console in my browser, I see that it has not connected, but:
There is an asp error saying that path /signalr/connect was not found (why is it trying to access /signalr/connect? Is that normal? If so, then this must be purely routing problem, how do I solve it?)
In my console, I see that there is a EventSource's response has a MIME type ("text/html") that is not "text/event-stream". Aborting the connection. error. I am not sure if this is related, but it started to show up today, wasn't there before.
What am I doing wrong?
Any path beginning with /signalr should be routed through OWIN so signalr can handle the request.
It is normal for the client to try to access /signalr/connect after accessing /signalr/negotiate. /signalr/connect is the endpoint where SignalR establishes its WebSockets/Server-Sent Events/Forever Frame/Long Polling connections.

TraceListener in OWIN Self Hosting

I am using Microsoft.Owin.Hosting to host the following, very simple web app.
Here is the call to start it:
WebApp.Start<PushServerStartup>("http://localhost:8080/events");
Here is the startup class I am using:
public class PushServerStartup
{
public void Configuration(IAppBuilder app)
{
app.MapHubs();
}
}
I am running this inside a console application that does a lot of other things including routing trace writing to certain files etc. But all of a sudden (when activating the OWIN hosting) I am seeing trace messages written to the console that are normally routed somewhere else.
Obviously there are some trace listeners active in the OWIN hosting framework. How can I switch them off?
I had the same issue, I was self hosting 4 instances in one process and for each request was getting 4 lots of messages traced to console.
I simply removed the TraceListener instance
Trace.Listeners.Remove("HostingTraceListener")
"HostingTraceListener" is defined in the owin source code so I guess could change
- http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Hosting/Engine/HostingEngine.cs
I did this after
WebApp.Start(...
An alternative to the answer from meilke that works with latest Katana self-host (2.1.0):
StartOptions options = new StartOptions("http://localhost:8080/events");
// disable built-in owin tracing by using a null traceoutput
options.Settings.Add(
typeof(Microsoft.Owin.Hosting.Tracing.ITraceOutputFactory).FullName,
typeof(NullTraceOutputFactory).AssemblyQualifiedName);
using (WebApp.Start<PushServerStartup>(options))
NullTraceOutputFactory is similar to DummyFactory but using StreamWriter.Null instead of StringWriter:
public class NullTraceOutputFactory : ITraceOutputFactory
{
public TextWriter Create(string outputFile)
{
return StreamWriter.Null;
}
}
I found a solution myself. After studying the Katana source code it seems like you need to register your own ITraceOutputFactory instance to overrule the default trace listener (which is writing to the console).
Here is the new start call:
var dummyFactory = new DummyFactory();
var provider = ServicesFactory.Create(
defaultServiceProvider => defaultServiceProvider.AddInstance<ITraceOutputFactory>(dummyFactory));
using (WebApp.Start<Startup>(provider, new StartOptions("http://localhost:8090")))
{
Console.ReadLine();
}
And here is a dummy trace factory (maybe not the best solution but you can replace it with something serving your purpose a little better):
public class DummyFactory : ITraceOutputFactory
{
public TextWriter Create(string outputFile)
{
return TextWriter.Null;
}
}

Categories

Resources