Не удается добавить протоколирование при использовании TPL с асинхронным ожиданием - PullRequest
0 голосов
/ 08 апреля 2019

Я безуспешно искал ответы на этот вопрос, поэтому стараюсь здесь.Я пытаюсь заставить регистрацию работать в моем коде, внутри Задачи.Я попытался максимально упростить мой пример.

    public void ExecuteRequestOnNewTask(string clientRequestContent)
    {
        WriteToTextFile("just entered ExecuteRequestOnNewTask");
        try
        {
            string response = string.Empty;
            Task.Factory.StartNew(
                async () =>
                {
                    WriteToTextFile("About to call ExecuteRequest");
                    response = await ExecuteRequest(clientRequestContent);
                    WriteToTextFile("Response received:" + response);
                });
        }
        catch (Exception ex)
        {
            WriteToTextFile("Exception from task:" + ex);
        }
    }

    public async Task<string> ExecuteRequest(string clientRequestContent)
    {
        // long running stuff here
    }

    public void WriteToTextFile(string text)
    {
        string textWithCurrentDateTime = "\r\n" + DateTime.UtcNow.AddHours(1) + ": " + text;
        string path = @"C:\Log.txt";
        if (!File.Exists(path))
        {
            File.Create(path);
            TextWriter tw = new StreamWriter(path);
            tw.WriteLine(textWithCurrentDateTime);
            tw.Close();
        }
        else if (File.Exists(path))
        {
            TextWriter tw = new StreamWriter(path, true);
            tw.WriteLine(textWithCurrentDateTime);
            tw.Close();
        }
    }

Единственное, что я получаю в качестве вывода в моем файле журнала, это:

"только что вошел в ExecuteRequestOnNewTask" "О вызове ExecuteRequest"

Я не записал ни ответ, ни что-либо в методе длительного запуска.

Ответы [ 2 ]

1 голос
/ 09 апреля 2019

Если вы хотите регистрировать исключения для задач запуска и забывания, вот хороший метод расширения, который вы можете использовать:

public static Task OnExceptionLogError(this Task task, string message)
{
    task.ContinueWith(t =>
    {
        var exception = t.Exception;
        var innerExceptions = exception.Flatten().InnerExceptions;
        var lines = innerExceptions.Select(ex => $"{ex.GetType().FullName}: {ex.Message}");
        string literal = innerExceptions.Count == 1 ? "an exception" : $"{innerExceptions.Count} exceptions";
        WriteToTextFile($"{message}, {literal} occured on task #{task.Id}:\r\n  " + String.Join("\r\n  ", lines));
    }, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
    return task;
}

Пример использования:

Task.Factory.StartNew(
    async () =>
    {
        WriteToTextFile("About to call ExecuteRequest");
        response = await ExecuteRequest(clientRequestContent);
        WriteToTextFile("Response received:" + response);
    }).OnExceptionLogError("ClientRequestContent"); // <--- give a title for the error

В нем регистрируются все возможные вложенные ошибки, которые могли быть сгруппированы, например, путем вызова Task.WhenAll.


Вот еще один способ регистрации исключений из задач запуска и забывания. Он имеет преимущество в том, что он является глобальным обработчиком в одном месте, и недостатком в том, что он не может регистрировать больше информации об ошибке, кроме самого исключения.

static void Main(string[] args)
{
    TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
    // ...
}

private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
    // Log the e.Exception, which is an AggregateException
    WriteToTextFile("Exception from task:" + e.Exception);
}

Обновление: Предостережения:

1) Событие TaskScheduler.UnobservedTaskException возникает, когда задача собирается сборщиком мусора, и это может произойти намного позже или не произойти вовсе!

2) Это событие не вызывается в сборках отладки, только в сборках выпуска !

1 голос
/ 09 апреля 2019

Есть несколько проблем с кодом:

  • Task.Factory.StartNew - очень низкоуровневый, опасный метод .Он имеет неочевидный планировщик задач по умолчанию и не понимает async делегатов.Если вы хотите запустить код в потоке пула потоков, используйте вместо него Task.Run.
  • Задача, возвращаемая из StartNew, игнорируется.Таким образом, блок catch никогда ничего не сделает.Если вы хотите перехватывать и регистрировать исключения в сценарии «забей и забудь», это нужно сделать внутри делегата.Иными словами, вы не можете перехватывать исключения из чего-то, что было запущено и забыто именно потому, что оно было забыто!

Кроме того, WriteToTextFile не является поточно-ориентированным, чтоопределенно проблема здесь.Но наиболее вероятная причина для усеченного журнала - запуск и забывание.Если ваше приложение закрывается, задачи запуска и забывания завершаются - без исключений, без уведомления.Это по замыслу - в конце концов, они включают и забывают .

...