Quite straighforward. This is generic code from some sandbox experiments. ExcludeFromCodeCoverage seems to be working properly for synchronhous methods. No runsettings or any configuration related to the coverage are in place.
[ExcludeFromCodeCoverage] // Still included in coverage report.
private async Task ExecuteRetryable(Func<Task> function)
{
try
{
await retryPolicyAsync.ExecuteAsync(function);
}
catch (Exception exception)
{
LogException(exception);
}
}
[ExcludeFromCodeCoverage] // Not included in coverage report as expected.
private void LogException(Exception exception)
{
if (TelemetryClient == null)
return;
var telemetry = new ExceptionTelemetry(exception);
telemetry.Properties.Add("typeCode", "ExceptionFromTheSandbox");
TelemetryClient.TrackException(telemetry);
}
}
Could this be a bug or am I missing something?
Related
I've created a simple webapi .net core 3.1 app.
I want to catch all unhandled exceptions.So I put this code according to the docs :
app.UseExceptionHandler(c => c.Run(async context =>
{
var exception = context.Features
.Get<IExceptionHandlerPathFeature>()
.Error;
var response = new { error = exception.Message };
log.LogDebug(exception.Message);
}));
This is my action:
[HttpGet]
public IActionResult Get()
{
throw new Exception("this is a test");
}
When this code runs, I do see that UseExceptionHandler is working.
But when my code in the action is :
[HttpGet]
public IActionResult Get()
{
Task.Run(async () =>
{
await Task.Delay(4000);
throw new Exception("this is a test");
});
return Ok();
}
Then UseExceptionHandler is NOT working.
However - the following code does catch the task's exception :
AppDomain.CurrentDomain.FirstChanceException += (sender, eventArgs) =>
{
Debug.WriteLine(eventArgs.Exception.ToString());
};
Question:
Why does the task exception isn't recognized by UseExceptionHandler?
How can I catch ALL types of exceptions? Should I rely only on AppDomain.CurrentDomain.FirstChanceException?
nb , I did disabled app.UseDeveloperExceptionPage();
To answer your questions.
Why does the task exception isn't recognized by UseExceptionHandler?
As already suggested in the comments, you cannot use UseExceptionHandler to catch exceptions initiated inside non-awaited tasks. UseExceptionHandler wraps your request in ASP.NET Core middleware. Once the action returns OK to the client, the middleware is no longer able to catch any exceptions happening in tasks started from within the action.
How can I catch ALL types of exceptions? Should I rely only on AppDomain.CurrentDomain.FirstChanceException?
You can catch exceptions globally and log them this way if you'd like. But I wouldn't recommend you to do it this way. The only reason you need to implement this event, is that you are starting tasks/threads inside your web requests. You have no way of knowing if these tasks are kept running (application restart, recycle, etc.). If you are looking to launch background tasks with ASP.NET Core, you should use Worker Services which is the intended way of doing this:
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<MyWorker>();
});
public class MyWorker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
// Do work
}
catch (Exception e)
{
// Log it?
}
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
}
}
}
The cause of this particular symptom is that Get is starting a fire-and-forget task that the server knows nothing about. The request will complete before the task even has a chance to execute, so the UseExceptionHandler middleware will never see any exceptions. This is no longer a fire-and-forget task.
The real problem though, is executing a long running task in the background. The built-in way to do this is using a Background Service. The docs show how to create timed and queued background service, that act as job queues.
It's equally easy, if not easier, to publish messages with the desired data from, eg a controller to the background service using, eg Channels. No need to create our own queue, when the BCL already has an asynchronous one.
The service could look like this :
public class MyService: BackgroundService
{
private readonly ChannelReader<T> _reader;
public QueuedBspService(MessageQueue<T> queue)
{
_reader = queue.Reader;
}
protected internal async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
await foreach (var msg in _reader.ReadAllAsync(stoppingToken))
{
try
{
//Process the message here
}
catch (Exception exc)
{
//Handle message-specific errors
}
}
}
catch (Exception exc)
{
//Handle cancellations and other critical errors
}
}
}
The MessageQueue<T> wraps the Channel, making it easier to inject it to both the BackgroundService and any publishers like eg, a Controller action:
public class MessageQueue<T>
{
private readonly Channel<T> _channel;
public ChannelReader<T> Reader => _channel;
public ChannelWriter<T> Writer => _channel;
public MessageChannel()
{
_channel = Channel.CreateBounded<T>(1);
}
}
I adjusted this code from a service that only allows a single operation at a time. That's a quick&dirty way of preventing controllers from making requests that can't be handled.
On the contolle side, this action will post a request to the queue if possible, and return a Busy response otherwise :
public class MyController
{
private readonly ChannelWriter<T> _writer;
public MyController(MessaggeQueue<T> queue)
{
_writer = queue.Writer;
}
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<ActionResult> Post(....)
{
var jobName="SomeJob";
var id=Guid.NewGuid();
var jobMsg=CreateMessage(id,...);
try
{
if (_writer.TryWrite(msg))
{
return CreatedAtAction("GetItem","Jobs",new {id});
}
else
{
return Problem(statusCode:(int) HttpStatusCode.ServiceUnavailable,detail:"Jobs in progress",title:"Busy");
}
}
catch (Exception exc)
{
_logger.LogError(exc,"Queueing {job} failed",jobName);
throw;
}
}
}
The Post action first checks if it can even post a job message. If it succeeds, it returns a 201 - Created response with a URL that could be checked eg to check the status of the jobs. return Created() could be used instead, but once you create a long running job, you also want to check its status.
If the channel is at capacity, the core returns 503 with an explanation
I'm wondering how I can let this code fall in the catch of PassThrough?
using System;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
try
{
await PassThrough(Test());
} catch (Exception) {
Console.WriteLine("caught at invocation");
}
Console.ReadLine();
}
public static async Task PassThrough(Task<bool> test)
{
try
{
var result = await test.ConfigureAwait(false);
// still need to do something with result here...
}
catch
{
Console.WriteLine("never caught... :(");
}
}
/// external code!
public static Task<bool> Test()
{
throw new Exception("something bad");
// do other async stuff here
// ...
return Task.FromResult(true);
}
}
fiddle
The external code should return handle the error path and return Task.FromException? Pass a Func<Task<bool>>?
My recommendation would be to change your PassThrough method to take a Func<Task<bool>> instead of a Task<bool>. This way, you can capture exceptions arising both from the synchronous part of your Test method, as well as the asynchronous task it launches. An added advantage is that asynchronous methods (defined using async and await) can be directly cast to Func<Task> or Func<Task<TResult>>.
using System;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
try
{
await PassThrough(Test);
// Note that we are now passing in a function delegate for Test,
// equivalent to () => Test(), not its result.
}
catch (Exception)
{
Console.WriteLine("caught at invocation");
}
Console.ReadLine();
}
public static async Task PassThrough(Func<Task<bool>> test)
{
try
{
var task = test(); // exception thrown here
var result = await task.ConfigureAwait(false);
// still need to do something with result here...
}
catch
{
Console.WriteLine("caught in PassThrough");
}
}
/// external code!
public static Task<bool> Test()
{
throw new Exception("something bad");
// do other async stuff here
// ...
return Task.FromResult(true);
}
}
Adding to Douglas's answer.
Only catch exceptions if you are able to do something meaningful with them and you can manage them at that level.
Task.FromException basically just places the exception on a task which you would usually return. However, in this case the Async Await Pattern already does this for you. i.e If you just let it fail, the exception will get placed on the task anyway, so there seems no real reason from your code to catch anything.
The only pertinent place you have to think about catching exceptions is in async void as they run unobserved and can cause issues when an exception is thrown
In the following line you are awaiting the PassThrough, not the Test.
await PassThrough(Test());
You could await both if you wanted:
await PassThrough(await Test()); // also need to change the signature of PassThrough from Task<bool> to bool.
...but in both cases the Test will be invoked first. And since it throws an exception, the PassThrough will never be invoked. This is the reason you don't see the "caught in PassThrough" message. The execution never enters this method.
I am still learning to use MSTest and Moq for automated unit testing in my application. I have successfully mocked the code and run it. It is showing that the tests are passed , but the code coverage is 0%. This is my code below.What needs to be changed so that code coverage becomes 100%.
I know this question has been asked a couple of times before, but nothing seems to help me.So can anyone suggest me what am I doing wrong.
Any help is highly appreciated.Thanks.
PS: I'm using Sonarcube for knowing the code coverage.
using Moq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Threading.Tasks;
using System.Diagnostics.CodeAnalysis;
namespace MyNameSpace
{
[TestClass]
public class ApplicationTest
{
readonly Helper moqHelper = new Helper();
[TestMethod()]
public void GetDataFromDataBaseMoq()
{
Task<bool> returnValue;
Mock<Application> mockType = new Mock<Application>();
mockType.CallBase = true;
mockType.Setup(x => x.GetDataFromDataBase()).Returns(returnValue = moqHelper.GetDataFromDataBaseMoq());
if (returnValue.Result)
{
Assert.IsTrue(true);
}
else
{
Assert.Fail();
}
}
}
[ExcludeFromCodeCoverage]
class Helper
{
internal async Task<bool> GetDataFromDataBaseMoq()
{
bool returnValue = true;
return returnValue;
}
}
public class Application : IApplication
{
public virtual async Task<bool> GetDataFromDataBase()
{
//if data retrive successfull, return true, else false
return true;
}
}
public interface IApplication
{
Task<bool> GetDataFromDataBase();
}
}
You're not testing your application code, you're testing your mock. You could've seen this by setting a breakpoint in Application.GetDataFromDataBase() and debugging your test; you'd see it won't be hit.
You need to only mock dependencies, if any. So rewrite your test to actually call into your code:
[TestMethod]
public async Task GetDataFromDataBase_Returns_True()
{
// Arrange
IApplication classUnderTest = new Application();
// Act
var result = await classUnderTest.GetDataFromDataBase();
// Assert
Assert.IsTrue(result);
}
And you'll see the need for all the mocks and helpers goes away.
I have a customer's DNN site with a custom module that's having issues with module error logging. The site was upgraded to version 7.4 from 6.2 and now to version 9.0. Module exceptions no longer appear in Admin / Host Events since the upgrade to 7.4. It appears module exception logging was changed in DNN 7.4 as explained here. This is the code that worked before but now nothing gets logged;
Test object:
public class foo
{
public int id { get; set; }
public string bar { get; set; }
}
Test webapi Controller:
[DnnAuthorize]
public class MyCustomModuleController : DnnApiController
{
private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(MyCustomModuleController));
[HttpGet]
public HttpResponseMessage GetFoo(int id)
{
try
{
foo test = null;
var bar = test.bar; //will throw null exception
...
}
catch (Exception ex)
{
Logger.Error(ex); //no log entries in db since ver. 7.4
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Server error");
}
}
Is there a setting that I should enable or is there a new way of logging events?
It is my understanding that DotNetNuke.Instrumentation.GetLogger() returns the logging object for doing the Log4Net logging to file appender logs stored in: /Portals/_default/Logs. You can also view them from Host > Host Settings > Logs.
Normally, you would add to your class by setting it up in a constructor and calling the .Error()/.ErrorFormat(), .Warn()/.WarnFormat(), etc functions to append an error or information message to the logging file.
public class MyCustomModuleController : DnnApiController
{
private DotNetNuke.Instrumentation.ILog Logger { get; set; }
public MyCustomModuleController()
{
Logger = DotNetNuke.Instrumentation.LoggerSource.Instance.GetLogger(this.GetType());
}
public HttpResponseMessage GetFoo(int id)
{
try
{
...
}
catch (Exception ex)
{
Logger.Error(ex);
}
}
}
The other logging technique available is using the DotNetNuke.Services.Exceptions to log the exception to the database. These errors get added to the EventLog and Exceptions tables.
public class MyCustomModuleController : DnnApiController
{
public HttpResponseMessage GetFoo(int id)
{
try
{
...
}
catch (Exception ex)
{
DotNetNuke.Services.Exceptions.Exceptions.LogException(ex);
}
}
}
It is possible that DNN disconnected the Log4Net process with the EventLog. I can't remember how it worked back in version 6.
I'm writing integration tests using the MSTest framework. The tests and code under test all have significant logging built into them.
I'm trying to figure out a way to hook into the Assert's output so I can write it to the log files along with the rest of log.
For example, if I have a test method like
[TestMethod]
SomeRandomIntegrationTest()
{
//Code to actually run the test, and includes logging.
Assert.AreEqual(true, false, "This error message should also appear in the log");
}
I would get
Message: Assert.AreEqual failed. Expected true. Got false. This error message should also appear in the log.
in my log file.
I tried doing
private StringBuilder testOutputBuilder;
private StringWriter testOutputWriter;
private TextWriter originalWriter;
[TestInitialize]
public virtual void Initialize()
{
//Redirect the test output into the log files
testOutputBuilder = new StringBuilder();
testOutputWriter = new StringWriter(testOutputBuilder);
originalWriter = Console.Out;
Console.SetOut(testOutputWriter);
}
[TestCleanup]
public virtual void TestCleanup()
{
if (TestContext.CurrentTestOutcome != UnitTestOutcome.Passed)
{
//File logging happens here using the testOutputBuilder
}
Console.SetOut(originalWriter);
testOutputWriter.Dispose();
}
but the testOutputBuilder returns an empty string.
How can I grab the string outputs from the assert methods in MSTest?
I wrote a workaround using separate function:
public string WriteErrorToFile(TextWriter textwriter, string errorMessage)
{
textwriter.WriteLine(errorMessage);
return errorMessage;
}
and code inside test should be modified like:
Assert.AreEqual(true, false, WriteErrorToFile(originalWriter, "This error message should also appear in the log"));
If you have only one log file then you can remove first parameter to the function.
Hope this helps
I did this:
public void OutputAssert(Action func)
{
try
{
func();
}
catch (Exception ex)
{
OutputToFile(ex.Message);
throw ex;
}
}
And then in the test:
[TestMethod]
public void TestAssertOutput()
{
OutputAssert(() => Assert.AreEqual(false, true, "test message"));
}
The output being:
Assert.AreEqual failed. Expected:<False>. Actual:<True>. test message