Preventing specific exceptions from being logged to App Insights - c#

This is a long shot and there doesnt seem to be anything close on Google, but I was wondering if it possible to prevent certain exceptions from being logged to App Insights?
There are a lot of places in our code base where exceptions were raised for user actions that are not really exceptions that we want to show in App Insights.
However, changing all the areas where this occurs would take use a long time and would require a lot of testing as we would need to make sure that the flow of logic is not changed
At the moment we raise exceptions of type LocationDomainException, Im thinking that if I come across somewhere where we dont want to log the exception, we can change to LocationDomainUserError
I would need to stop this exception, or any exception inheriting from it from being logged
Im using .NET Core 3.1.
I appreciate this is a bit of an anti pattern, but has anyone ever tried it?

Yes, you can use a Telemetry Processor:
public class CustomTelemetryFilter : ITelemetryProcessor
{
private readonly ITelemetryProcessor _next;
public CustomTelemetryFilter(ITelemetryProcessor next)
{
_next = next;
}
public void Process(ITelemetry item)
{
// Example: process all exceptions except LocationDomainException
var isSomeException = item is ExceptionTelemetry ex && ex.Exception is LocationDomainException;
if (!isSomeException)
_next.Process(item); // Process the item
else
{
// Item is dropped here
}
}
}
Register the processor using services.AddApplicationInsightsTelemetryProcessor<CustomTelemetryFilter>();

Related

Why is my scoped service being called as a new instance every time?

This is a practice ASP.NET project I'm using to better understand a few techniques, and while I've got Dependency Injection working, its not working quite as I want it to. I have a class that I want to use to store a history, so every time the user hits a submit button, it displays a result, and after the second time it starts displaying the history. Anyway I added the history to the DI as a scoped service, thinking that would mean it would be created and then remain the same instance for the duration of the session for that user. However according to the debugger it looks like the list never gets bigger than one, and thats at the point of adding the item to the list. So the code.
The object
{
public class RollHistory : IRollHistory
{
public List<IRollMessage> Entries { get; set; } = new List<IRollMessage>();
}
}
The DI
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddTransient<IDiceTray, DiceTray>();
services.AddTransient<IRollMessage, RollMessage>();
services.AddScoped<IRollHistory, RollHistory>();
}
The Controller constructor
public HomeController(ILogger<HomeController> logger, IDiceTray diceTray, IRollMessage rollMessage, IRollHistory rollHistory)
{
_logger = logger;
_diceTray = diceTray;
_rollMessage = rollMessage;
_rollHistory = rollHistory;
}
And the code for when the button gets clicked
[HttpPost]
public IActionResult Index(DiceRollModel diceRoll)
{
_diceTray.DiceRoll(diceRoll.DiceType, diceRoll.DiceCount, diceRoll.Bonus, diceRoll.VantageType);
_rollMessage.RollMessages(_diceTray);
diceRoll.RollResult = _rollMessage;
_rollHistory.Entries.Add(_rollMessage);
diceRoll.History = _rollHistory.Entries;
return View(diceRoll);
}
It's worth noting I've tried to code this at least 4 different ways with and without DI, the only way it works is if I use AddSingleton, while this might not be an issue because this app is unlikely to ever be live, its a poor excuse not to do it right.
I believe “scope” is by default per request which would explain that each submit gets is own service.
“Doing stuff right” is of course to some extend a matter of opinion. But my opinion would clearly be that I would avoid server-side session to avoid problems with scaling to more than one instance. There are also ways to support shared state, but this is difficult. To me singletons are not a code smell either, but they have their own problems.
Your problem might be solved by storing whatever state you need in the browser either in a cookie or localStorage. Your service would then have request scope, but it would read user state from browser causing “user scope” for the data. (But don’t rely on browser state to persist and remember it is modifiable to the user.)

How to get actual request execution time

Given the following middleware:
public class RequestDurationMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestDurationMiddleware> _logger;
public RequestDurationMiddleware(RequestDelegate next, ILogger<RequestDurationMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
var watch = Stopwatch.StartNew();
await _next.Invoke(context);
watch.Stop();
_logger.LogTrace("{duration}ms", watch.ElapsedMilliseconds);
}
}
Because of the pipeline, it occurs before the end of pipeline and logs different times:
WebApi.Middlewares.RequestDurationMiddleware 2018-01-10 15:00:16.372 -02:00 [Verbose] 382ms
Microsoft.AspNetCore.Server.Kestrel 2018-01-10 15:00:16.374 -02:00 [Debug] Connection id ""0HLAO9CRJUV0C"" completed keep alive response.
Microsoft.AspNetCore.Hosting.Internal.WebHost 2018-01-10 15:00:16.391 -02:00 [Information] "Request finished in 405.1196ms 400 application/json; charset=utf-8"
How can I capture the actual request execution time from WebHost (405.1196ms in the example) value in this case? I want to store this value in database or use it elsewhere.
I thought this question was really interesting, so I looked into this for a bit to figure out how the WebHost is actually measuring and displaying that request time. Bottom line is: There is neither a good nor an easy nor a pretty way to get this information, and everything feels like a hack. But follow along if you’re still interested.
When the application is started, the WebHostBuilder constructs the WebHost which in turn creates the HostingApplication. That’s basically the root component that is responsible to respond to incoming requests. It is the component that will invoke the middleware pipeline when a request comes in.
It is also the component that will create HostingApplicationDiagnostics which allows to collect diagnostics about the request handling. At the beginning of the request, the HostingApplication will call HostingApplicationDiagnostics.BeginRequest, and at the end of the request, it will call HostingApplicationDiagnostics.RequestEnd.
Not that surprisingly, HostingApplicationDiagnostics is the thing that will measure the request duration and also log that message for the WebHost that you have been seeing. So this is the class that we have to inspect more closely to figure out how to get the information.
There are two things the diagnostics object uses to report diagnostics information: A logger, and a DiagnosticListener.
Diagnostic listener
The DiagnosticListener is an interesting thing: It is basically a general event sink that you can just raise events on. And other objects can then subscribe to it to listen to these events. So this almost sounds perfect for our purpose!
The DiagnosticListener object that the HostingApplicationDiagnostics uses is passed on by the WebHost and it actually gets resolved from dependency injection. Since it is registered by the WebHostBuilder as a singleton, we can actually just resolve the listener from dependency injection and subscribe to its events. So let’s just do that in our Startup:
public void ConfigureServices(IServiceCollection services)
{
// …
// register our observer
services.AddSingleton<DiagnosticObserver>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
// we inject both the DiagnosticListener and our DiagnosticObserver here
DiagnosticListener diagnosticListenerSource, DiagnosticObserver diagnosticObserver)
{
// subscribe to the listener
diagnosticListenerSource.Subscribe(diagnosticObserver);
// …
}
That’s already enough to get our DiagnosticObserver running. Our observer needs to implement IObserver<KeyValuePair<string, object>>. When an event occurs, we will get a key-value-pair where the key is an identifier for the event, and the value is a custom object that is passed by the HostingApplicationDiagnostics.
But before we implement our observer, we should actually look at what kind of events HostingApplicationDiagnostics actually raises.
Unfortunately, when the request ends, the event that is raised on the diagnostic lister just gets passed the end timestamp, so we would also need to listen to the event that is raised at the beginning of the request to read the start timestamp. But that would introduce state into our observer which is something we want to avoid here. In addition, the actual event name constants are prefixed with Deprecated which might be an indicator that we should avoid using these.
The preferred way is to use activities which are also closely related to the diagnostic observer. Activities are apparently states that track, well, activities as they appear in the application. They are started and stopped at some point, and also already record how long they run on their own. So we can just make our observer listen to the stop event for the activity to get notified when its done:
public class DiagnosticObserver : IObserver<KeyValuePair<string, object>>
{
private readonly ILogger<DiagnosticObserver> _logger;
public DiagnosticObserver(ILogger<DiagnosticObserver> logger)
{
_logger = logger;
}
public void OnCompleted() { }
public void OnError(Exception error) { }
public void OnNext(KeyValuePair<string, object> value)
{
if (value.Key == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop")
{
var httpContext = value.Value.GetType().GetProperty("HttpContext")?.GetValue(value.Value) as HttpContext;
var activity = Activity.Current;
_logger.LogWarning("Request ended for {RequestPath} in {Duration} ms",
httpContext.Request.Path, activity.Duration.TotalMilliseconds);
}
}
}
Unfortunately there is just no solution without downsides… I found this solution to be very inaccurate for parallel requests (e.g. when opening a page that has also images or scripts which are requested in parallel). This is likely due to the fact that we are using a static Activity.Current to get the activity. However there does not really seem to be a way to get just the activity for a single request, e.g. from the key value pair that was passed.
So I went back and tried my original idea again, using those deprecated events. The way I understood it is btw. that they are just deprecated because using activities is recommended, not because they will be removed soon (of course we are working with implementation details and an internal class here, so these things could change at any time). To avoid problems with concurrency, we need to make sure we store the state inside of the HTTP context (instead of a class field):
private const string StartTimestampKey = "DiagnosticObserver_StartTimestamp";
public void OnNext(KeyValuePair<string, object> value)
{
if (value.Key == "Microsoft.AspNetCore.Hosting.BeginRequest")
{
var httpContext = (HttpContext)value.Value.GetType().GetProperty("httpContext").GetValue(value.Value);
httpContext.Items[StartTimestampKey] = (long)value.Value.GetType().GetProperty("timestamp").GetValue(value.Value);
}
else if (value.Key == "Microsoft.AspNetCore.Hosting.EndRequest")
{
var httpContext = (HttpContext)value.Value.GetType().GetProperty("httpContext").GetValue(value.Value);
var endTimestamp = (long)value.Value.GetType().GetProperty("timestamp").GetValue(value.Value);
var startTimestamp = (long)httpContext.Items[StartTimestampKey];
var duration = new TimeSpan((long)((endTimestamp - startTimestamp) * TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency));
_logger.LogWarning("Request ended for {RequestPath} in {Duration} ms",
httpContext.Request.Path, duration.TotalMilliseconds);
}
}
When running this, we do actually get accurate results and we also have access to the HttpContext which we can use to identify the request. Of course, the overhead that’s involved here is very apparent: Reflection to access property values, having to store information in HttpContext.Items, the whole observer thing in general… that’s probably not a very performant way to do this.
Futher reading on diagnostic source and activities: DiagnosticSource Users Guid and Activity User Guide.
Logging
Somewhere above I mentioned that the HostingApplicationDiagnostics also reports the information to the logging facilities. Of course: This is what we are seeing in the console after all. And if we look at the implementation, we can see that this already calculates the proper duration here. And since this is structured logging, we could use this to grab that information.
So let’s attempt to write a custom logger that checks for that exact state object and see what we can do:
public class RequestDurationLogger : ILogger, ILoggerProvider
{
public ILogger CreateLogger(string categoryName) => this;
public void Dispose() { }
public IDisposable BeginScope<TState>(TState state) => NullDisposable.Instance;
public bool IsEnabled(LogLevel logLevel) => true;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (state.GetType().FullName == "Microsoft.AspNetCore.Hosting.Internal.HostingRequestFinishedLog" &&
state is IReadOnlyList<KeyValuePair<string, object>> values &&
values.FirstOrDefault(kv => kv.Key == "ElapsedMilliseconds").Value is double milliseconds)
{
Console.WriteLine($"Request took {milliseconds} ms");
}
}
private class NullDisposable : IDisposable
{
public static readonly NullDisposable Instance = new NullDisposable();
public void Dispose() { }
}
}
Unfortunately (you probably love this word by now, right?), the state class HostingRequestFinishedLog is internal, so we cannot use it directly. So we have to use reflection to identify it. But we just need its name, then we can extract the value from the read-only list.
Now all we need to do is register that logger (provider) with the web host:
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
logging.AddProvider(new RequestDurationLogger());
})
.UseStartup<Startup>()
.Build();
And that’s actually all we need to be able to access the exact same information that the standard logging also has.
However, there are two problems: We don’t have a HttpContext here, so we cannot get information about which request this duration actually belongs to. And as you can see in the HostingApplicationDiagnostics, this logging call is actually only made when the log level is at least Information.
We could get the HttpContext by reading the private field _httpContext using reflection but there is just nothing we can do about the log level. And of course, the fact that we are creating a logger to grab information from one specific logging call is a super hack and probably not a good idea anyway.
Conclusion
So, this is all terrible. There simply is no clean way to retrieve this information from the HostingApplicationDiagnostics. And we also have to keep in mind that the diagnostics stuff actually only runs when it’s enabled. And performance critical applications will likely disable it at one point or another. In any way, using this information for anything outside of diagnostics would be a bad idea since it’s just too fragile in general.
So what is the better solution? A solution that works outsid of a diagnostics context? A simple middleware that runs early; just like you have already used. Yes, this is likely not as accurate as it will leave out a few paths from the outer request handling pipeline but it will still be an accurate measurement for the actual application code. After all, if we wanted to measure framework performance, we would have to measure it from the outside anyway: as a client, making requests (just like the benchmarks work).
And btw. this is also how Stack Overflow’s own MiniProfiler works. You just register the middleware early and that’s it.

Restart IIS AppDomain on type initializer/static constructor exception

I've got an ASP.NET that relies on some code that uses static constructors. The code in these type initializers sometimes fails. Let's say, for sake of argument, that the code is:
public static readonly string Thing = SomeSpecialCallThatRarelyFails();
Perhaps that's vile, but it cannot be changed. And this kinda code is in every controller, so ASP.NET can't create the controller and just sits there broken until someone comes along to restart it.
I understand this is the way it should be, because the problem may very well be non-transient and auto-restarting would create a loop. Or perhaps only one controller fails, so the app is still sort of alive. So I get the default behaviour to just keep returning the error. But in this particular case, let's pretend the best thing is to notice this failure and restart.
How can I automate the detection of this scenario and trigger a restart or recycle of the IIS app pool/AppDomain?
I've noticed that if I cause an exception on Application_Start, then the app will auto-restart. So one way is for me to iterate over all my types and try accessing them. If they have .cctor failures, then I'll crash Application_Start and ASP.NET will restart. But that's pretty hacky, plus it won't help if the actual request code references another type that I don't know about which throws on .cctor.
Is there a better way? Should I write a Web API filter and look for TypeInitializerException or something?
Just a thought. Is the 'rare failure' deterministic? Could it be solved by adding retry logic?
public static readonly string Thing = RetrySpecialCall();
private static string RetrySpecialCall()
{
while (true)
{
try
{
return SomeSpecialCallThatRarelyFails();
}
catch (Exception) {}
}
}
So here's a way to handle it in Web API 1:
In Application_Start, iterate over your controller types, calling System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor to force all your known type constructors to run. If it Application_Start fails, ASP.NET seems to restart.
Add an exception filter that looks for TypeInitializationExceptions. Then call HttpRuntime.UnloadAppDomain().
The two parts are needed as a controller failing to construct will not hit the exception filters.
With Web API 2 it seems like you could do it in one go by implementing System.Web.Http.ExceptionHandling.IExceptionLogger and registering it as a global service. Same logic: check for TypeInitializationException and UnloadAppDomain if so.

SimpleMembershipProvider intermittently returning wrong user

I am administrator of a small practice project web application, AngularJS front-end pulling its back-end data from a C#/.NET WebAPI, and I'm handling security using the SimpleMembershipProvider.
I suspect that the way I implemented said security is not the best (I'm told ASP.NET Identity is now the way to go?) but that's another question altogether.
The issue that I'm very bewilderingly running into is that I get occasional reports that on a given page load to display a particular user's data, it returns somebody else's. Reloading the page fixes the issue (evidently) and I haven't been able to duplicate the scenario myself, or figure out anything particularly consistent in the users to which this happens.
None of the information being displayed is at all sensitive in nature (the app's just a friendly front end for an already public third-party API) so I'm not in panic mode about this, but I am both concerned and confused and want it fixed.
Here is what one of my API controller endpoints looks like:
[Authorize]
public class UserController : ApiController
{
private static int _userId;
private readonly IUserProfileRepository _userProfileRepository;
public UserController()
{
_userProfileRepository = new UserProfileRepository(new DatabaseContext());
_userId = WebSecurity.GetUserId(User.Identity.Name);
}
public UserProfileDto Get()
{
return _userProfileRepository.GetUserProfileById(_userId).ToDto();
}
}
Any feedback on where I might be going wrong here or what might be causing the intermittant inconsistency would be very much appreciated. (Laughter also acceptable if the way I handled this is just really bad. :P )
Static class fields are shared by all instances/threads of the same AppDomain (in your case - process). Different http requests are processed by threads running in parallel. Any two threads running [almost] at the same time may (will) change the value of _userId. You are assigning _userId in the constructor of your controller, and a new instance of this controller is created for each http request that is to be responded to by UserController. Therefore, this assignment will happen multiple times.
You will have hard time replicating this problem, since you are a single user testing the code, hence there are no overlapping request threads.
Remove static specifier from the _userId field declaration of the controller class.
Note: make sure that DatabaseContext is disposed of. One place that can be used for this is the overriden Controller.Dispose.
Change the Get to retrieve the user id rather than from a static variable:
public UserProfileDto Get()
{
return _userProfileRepository.GetUserProfileById(WebSecurity.GetUserId(User.Identity.Name)).ToDto();
}

Exceptions and testing - how to unit test something for specific exception when I catch them all?

I'm writing MVC4 web application. Generally I try to put "try{}catch{}" block inside every controller method that returns ActionResult to the user. I do it in order to catch all Exceptions and display appropriate message, so user will never see something like:
"Reference not set to an instance of an object"
My controllers usually looks like this:
try
{
}
catch(MyFirstCustomException ex)
{
//set some message for the user and do some cleaning etc.
return ActionResult();
}
catch(MySecondCustomException ex) (and so on...)
{
//set some message for the user and do some cleaning etc.
return ActionResult();
}
catch(Exception ex)
{
//set some message for the user and do some cleaning etc.
return ActionResult();
}
However now I got the following situation: I have AccountController and a LogIn method, I want to write a unit test (using Microsoft Unit Testing Framework), that will assert that user which haven't activated his account, won't be able to log in. I have a special Exception named UserNotActivatedException that is thrown, when such attempt is detected. Problem is - since I catch all my exceptions within a controller, my test will never actually see this exception itself - thus the test will always fail. I managed to bypass the problem by creating special status enum for my model which looks like this:
public enum LoginViewModelStatus
{
NotLoggedIn = 0,
LoginSuccessfull = 1,
LoginFailed = 2,
UserNotActivatedException = 3,
UnknownErrorException = 100
}
and by setting it to a certain value when something is happening (so when I catch my special UserNotActivatedException - I set loginModelStatus to UserNotActivatedException and so on)
My questions:
Are there any nicer alternatives to this?
I'm thinking of using this design in other controllers as well, are there any downfalls here?
Is it good design to use a lot of custom exceptions for displaying messages for users, or would it be better to use more mini if(someCondition){return false;} tests?
You could wrap the code inside the try part in order to be able to unit test this part.
Here, the unit testable part is simply "wrapped" inside the MyUnitTestableMethod method :
try
{
MyUnitTestableMethod();
}
catch(MyFirstCustomException ex)
{
// ...
}
catch(MySecondCustomException ex) (and so on...)
{
// ...
}
catch(Exception ex)
{
// ...
}
KISS : Keep It Sanely Simple (or Keep It Simple and Stupid) :)
You should test that code returns expected results in all cases and more or less ignore how method does its work.
I.e. in your case Controller converts multiple exceptions into different view - test that when you feed data that causes exception scenario the Controller returns view you expect.
If lower levels of methods used by controller may throw exception - test them too, but this time for throwing particular exceptions.
It is up to you how many exceptions is enough. Good logging of exceptions is probably more important than variety. In most cases you should not show information from exception to a user anyway, but rather something like "Catastrophic error. If need assistance the error was logged with id AB455". All "expected exception" cases should be handled and presented to user as normal flow.
Note that it is ok to throw exceptions from actions as long as you have code that handles all exceptions. Action filter like HandleErrorAttribute can be used to configure exception policy for particular action/whole application.
It seems as you have your code "too stable". That is, your logic can never generate errors. It is good from a stability point of view but not very testable.
I would in this case have a class to handle the custom logic catch all exceptions generated from that class before returning ActionResult to separate the logic.
class ActionClass
{
public bool HandleLogin(...)
{
...
}
}
and use the class like this:
try
{
ActionClass action = new ActionClass();
action.HandleLogin(...)
}
// Catchblock here
This will allow you to test the logic.

Categories