2

I've got the a service with an async method that calls an external dependency (here with a fake implementation, just for demonstration purposes):

public class RandomNumberService : IRandomNumberService
{
    private readonly IHttpClientFactory _httpClientFactory;

    public RandomNumberService(IHttpClientFactory httpClientFactory) => _httpClientFactory = httpClientFactory;

    public async Task<int> GetRandomNumber()
    {
        HttpClient client = _httpClientFactory.CreateClient();
        HttpResponseMessage response = await client.GetAsync(@"http://www.randomnumberapi.com/api/v1.0/random?min=1&max=10&count=1");
        int[]? result = await response.Content.ReadFromJsonAsync<int[]>();

        if (!response.IsSuccessStatusCode || result == null || !result.Any())
        {
            var stackTrace = new StackTrace().ToString();
            // log error with the stack trace

            return 42;
        }

        int number = result.First();

        return number;
    }
}

This service can be called from several places, so when an error occurs I'd like to log it with an useful stack trace, but in my stackTrace variable, I get the following horror:

   at AsyncStackTraces.Services.RandomNumberService.GetRandomNumber()
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.ExecutionContextCallback(Object s)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext(Thread threadPoolThread)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.AsyncStateMachineBox`1.MoveNext()
   ... and 125 more lines of System stuff

The problem is that nowhere in that stack trace I see the caller of my service, which would be useful to debug the error.

EDIT: I've seen this similar question but Ben.Demystifier doesn't meet my needs, since I'm not working with an exception, and EnhancedStackTrace.Current() doesn't provide a complete stack trace (it doesn't contain the name of the service nor its caller):

Is there any way to get that complete (and clean) stack trace to log?

Tao Gómez Gil
  • 2,228
  • 20
  • 36
  • 1
    Does this answer your question? [Is it possible to get a good stack trace with .NET async methods?](https://stackoverflow.com/questions/15410661/is-it-possible-to-get-a-good-stack-trace-with-net-async-methods) – Heretic Monkey Jul 12 '21 at 13:01
  • I recommend Ben.Demystifier. – Stephen Cleary Jul 12 '21 at 13:51
  • Ben.Demystifier doesn't meet my needs :( I'm not working with an exception, and EnhancedStackTrace.Current() doesn't provide a complete stack trace (it doesn't contain the name of the service nor its caller) – Tao Gómez Gil Jul 12 '21 at 14:01
  • @TaoGómezGil but your code *is* doing what an exception would do. Except it pays the cost without any of the benefits. Exceptions were created to fix the myriad of problems caused by return values. Using the same type to return both results and "error codes" is a very bad idea. – Panagiotis Kanavos Jul 12 '21 at 14:07
  • @TaoGómezGil trying to read the response even though the query failed can lead to exceptions anyway. This code can lose any error details returned by the HTTP service. What are you trying to do? Why generate a costly stack trace and still return a very specific number that can *easily* be ignored? – Panagiotis Kanavos Jul 12 '21 at 14:08
  • @PanagiotisKanavos, an error can happen during the call to an external services because of external reasons (i.e. service is down) but also because there is an error in that call. I SUSPECT THAT I'M IN THAT SITUATION, AND I WANT TO DEBUG IT. In any case, the method should return a default value, but I want to log an error with a complete stack trace, because it will help me to find errors in the code calling this service. If I don't know the caller, I don't know where to look for those errors. – Tao Gómez Gil Jul 12 '21 at 14:21
  • 1
    @TaoGómezGil if an external error occurred you'd get an exception or error response. Your code is trying to generate a stack trace, though, not debug anything. `If I don't know the caller, I don't know where to look for those errors.` if you throw an exception because, frankly, that method *did fault*, it's the *caller's* job to log and handle the exception. In any case, it's *that caller* that controls your log files, if you use `ILogger` or any other logging library – Panagiotis Kanavos Jul 12 '21 at 14:33
  • And once again, hiding the error and returning a magic number is a very, *VERY* bad idea. Developers *hate* this because everyone had to stay until midnight to troubleshoot a problem caused by ignored "magic" numbers that somehow ended up in production. Maybe it was a `0` exchange rate. Or `-1` dividend. Or worse, reused passwords, certificates, repeating tokens etc caused by hard-coded PRNG values. – Panagiotis Kanavos Jul 12 '21 at 14:37
  • I get your point, but take into account that the code here is a simplified example, I won't be returning any magic number in my actual code. However, if an error occurs during that call to the external service, the **default value should be the same**, regardless which code called my service. Throwing an exception will force each caller to have the exact same "recovering logic" so that's why I'd like to put it inside my service itself. – Tao Gómez Gil Jul 12 '21 at 14:41
  • Anyway, this is going a bit off-topic, my initial question remains unanswered: is there a way to obtain a complete stacktrace inside async code, that can be used for debugging purposes? – Tao Gómez Gil Jul 12 '21 at 14:42
  • How do the clients look? Are those simply `var result = await GetRandomNumber();`? – ThomasArdal Jul 15 '21 at 05:50
  • @ThomasArdal, yes, well, more like `var result = await _randomNumberService.GetRandomNumber();` – Tao Gómez Gil Jul 19 '21 at 10:17

0 Answers0