First Last

First Last

  • 1.4k
  • 596
  • 25.6k

An MVC custom error logger trying to call a web api using async

Oct 28 2020 12:56 PM
I have a custom error logger that I want to:
1. Write a text file to a folder.
2. Using a web api, it sends an alert email to a staff members to notify that an error was written to a folder.
3. At the, end show the appropiate error view.
#1 and #3 worked fine before I introduced into the 'OnException' method code to process the #2. To do so, I had to add 'async' to the method as I am calling a web api that has that feature.
Now when MVC detects a code bug, it goes to the custom error logger but fails as it does not like the 'async' on the method.
 
I get:
An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%@ Page Async="true" %>. This exception may also indicate an attempt to call an "async void" method, which is generally unsupported within ASP.NET request processing. Instead, the asynchronous method should return a Task, and the caller should await it.
How do I solve this as I want to call the web api to send that alert email?
I have in the FilterConfig.cs - the ErrorLoggerAttribute points to a custom error logger. 
  1. filters.Add(new ErrorLoggerAttribute());  
Here is the custom ErrorLoggerAttribute.cs: 
  1. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
  2. // An async override method.  
  3. // - Fired off on exception.  
  4. //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
  5. public async override void OnException(ExceptionContext filterContext)  
  6. {  
  7.     //#############################################################################################  
  8.     // Write a text file to the ClientErrorLog folder.  
  9.     // - It may append to the text file if it already exists.  
  10.     //#############################################################################################  
  11.   
  12.     string strLogText = "";  
  13.     Exception ex = filterContext.Exception;  
  14.     filterContext.ExceptionHandled = true;  
  15.     var objClass = filterContext;  
  16.   
  17.     // Set the text.  
  18.     strLogText += "Message ---\n{0}" + ex.Message;  
  19.   
  20.     // Check the error source.  
  21.     if (ex.Source == ".Net SqlClient Data Provider")  
  22.     {  
  23.         strLogText += Environment.NewLine + "SqlClient Error ---\n{0}" + "Check Sql Error";  
  24.     }  
  25.     else if (ex.Source == "System.Web.Mvc")  
  26.     {  
  27.         strLogText += Environment.NewLine + ".Net Error ---\n{0}" + "Check MVC Code For Error";  
  28.     }  
  29.     else if (filterContext.HttpContext.Request.IsAjaxRequest() == true)  
  30.     {  
  31.         strLogText += Environment.NewLine + ".Net Error ---\n{0}" + "Check MVC Ajax Code For Error";  
  32.     }  
  33.   
  34.     strLogText += Environment.NewLine + "Source ---\n{0}" + ex.Source;  
  35.     strLogText += Environment.NewLine + "StackTrace ---\n{0}" + ex.StackTrace;  
  36.     strLogText += Environment.NewLine + "TargetSite ---\n{0}" + ex.TargetSite;  
  37.   
  38.     if (ex.InnerException != null)  
  39.     {  
  40.         // Error prone.  
  41.         strLogText += Environment.NewLine + "Inner Exception is {0}" + ex.InnerException;  
  42.     }  
  43.     if (ex.HelpLink != null)  
  44.     {  
  45.         // Error prone.  
  46.         strLogText += Environment.NewLine + "HelpLink ---\n{0}" + ex.HelpLink;  
  47.     }  
  48.   
  49.     // The Textwriter for writing characters to a stream.  
  50.     StreamWriter log;  
  51.   
  52.     // Set the date.   
  53.     // - For instance: 27-October-2020  
  54.     string timestamp = DateTime.Now.ToString("d-MMMM-yyyy"new CultureInfo("en-GB"));  
  55.   
  56.     // Read the web.config and get the error log path to write an 'error log' text file to.  
  57.     string error_folder = ConfigurationManager.AppSettings["ClientErrorLogPath"].ToString();  
  58.   
  59.     // Check if the error folder exists.  
  60.     if (!System.IO.Directory.Exists(error_folder))  
  61.     {  
  62.         System.IO.Directory.CreateDirectory(error_folder);  
  63.     }  
  64.   
  65.     // Check if the text file exists.  
  66.     // - For instance, looks for: ClientErrorLog\Log_27-October-2020.txt  
  67.     if (!File.Exists(String.Format(@"{0}\Log_{1}.txt", error_folder, timestamp)))  
  68.     {  
  69.         // Create the text file.  
  70.         log = new StreamWriter(String.Format(@"{0}\Log_{1}.txt", error_folder, timestamp));  
  71.     }  
  72.     else  
  73.     {  
  74.         // Set for an add(append) to the text file.  
  75.         log = File.AppendText(String.Format(@"{0}\Log_{1}.txt", error_folder, timestamp));  
  76.     }  
  77.   
  78.     var controllerName = (string)filterContext.RouteData.Values["controller"];  
  79.     var actionName = (string)filterContext.RouteData.Values["action"];  
  80.   
  81.     // Start writing to the 'error log' text file.  
  82.     // - First, write the timestamp to the file.  
  83.     log.WriteLine(Environment.NewLine + DateTime.Now);  
  84.   
  85.     log.WriteLine("------------------------------------------------------------------------------------------------");  
  86.     log.WriteLine("Controller Name :- " + controllerName);  
  87.     log.WriteLine("Action Method Name :- " + actionName);  
  88.     log.WriteLine("------------------------------------------------------------------------------------------------");  
  89.   
  90.     // Write the filter context.  
  91.     log.WriteLine(objClass);  
  92.   
  93.     // Write the text.  
  94.     log.WriteLine(strLogText);  
  95.   
  96.     // Write an empty line.  
  97.     log.WriteLine();  
  98.   
  99.     //#############################################################################################  
  100.     // Using the web api, send a client error alert email to a staff member (an Admin) as to notify  
  101.     // that an error was written to the ClientErrorLog folder.  
  102.     //#############################################################################################  
  103.   
  104.     string errorMessage = "";  
  105.     string exceptionMessage = "";  
  106.   
  107.     try  
  108.     {  
  109.         // Instantiate the ControlResult for getting data from the web api.  
  110.         ControlResult controlResult = new ControlResult();  
  111.   
  112.         // -----------------------------------------------------------------------------------------------------------------------------------------------  
  113.         // 1st: There is no 'user name' available here - can't reference the session variable.  
  114.         //        A 'user name' is needed so that the process functions properly - need to access the web api with a valid 'user name'.  
  115.         // -----------------------------------------------------------------------------------------------------------------------------------------------  
  116.   
  117.         // Get the control control 'user name'.  
  118.         // - It calls a model class to execute a method.  
  119.         controlResult = await GetControl();  
  120.   
  121.         // Check if an error occurred in the web api.  
  122.         if (controlResult.ApiErrorMessage == null)  
  123.         {  
  124.             // Got the control 'user name' in order to access the web api'.  
  125.   
  126.             try  
  127.             {  
  128.                 // -----------------------------------------------------------------------------------------------------------------------------------------------  
  129.                 // 2nd: Send a client error alert email to a staff member (an Admin) as to notify that an error was written to the   
  130.                 //         ClientErrorLog folder.  
  131.                 // -----------------------------------------------------------------------------------------------------------------------------------------------  
  132.   
  133.                 // Instantiate the ClientErrorResult for getting data from the web api.  
  134.                 ClientErrorResult clientErrorResult = new ClientErrorResult();  
  135.   
  136.                 // Instantiate the BLL_Client class so that the functions can be referenced.   
  137.                 BLL_ClientError bll_ClientError = new BLL_ClientError();  
  138.   
  139.                 // Call the web api to process the error.  
  140.                 // - It calls a model class to execute a method.  
  141.                 clientErrorResult = await bll_ClientError.ProcessClientErrorNoLog(controlResult.UserName);  
  142.   
  143.                 // Check if an error occurred in the web api.  
  144.                 if (clientErrorResult.ApiErrorMessage != null)  
  145.                 {  
  146.                     // Set the error message with the web api error message. The ReasonPhrase set in BLL_ClientError.  
  147.                     errorMessage = controlResult.ApiErrorMessage;  
  148.                 }  
  149.             }  
  150.             catch (Exception ex3)  
  151.             {  
  152.                 // Friendly user message.  
  153.                 errorMessage = "Server error on sending a client error alert. Exception error: " + ex3.Message;  
  154.             }  
  155.         }  
  156.         else  
  157.         {  
  158.             // Set the error message with the web api error message. The ReasonPhrase set in BLL_Control.  
  159.             errorMessage = controlResult.ApiErrorMessage;  
  160.         }  
  161.     }  
  162.     catch (Exception ex1)  
  163.     {  
  164.         // Friendly user message.  
  165.         exceptionMessage = "Server error on getting. Exception error: " + ex1.Message;  
  166.   
  167.         try  
  168.         {  
  169.             // Instantiate the ClientErrorResult for getting data from the web api.  
  170.             ClientErrorResult clientErrorResult = new ClientErrorResult();  
  171.   
  172.             // Call the web api to process the error.  
  173.             // Note: the 'user name' being passed. Using 'Control' as a 'user name' is not available.  
  174.             // - It calls a model class to execute a method.  
  175.             clientErrorResult = await ProcessClientError("Control", ex1.Message, "Server error on getting the control field. Method: OnException");  
  176.   
  177.             // Check if an error occurred in the web api.  
  178.             if (clientErrorResult.ApiErrorMessage == null)  
  179.             {  
  180.                 // All good. Just show the friendly user message set above.  
  181.                 // Pass data from the controller to the corresponding view.  
  182.                 errorMessage = exceptionMessage;  
  183.             }  
  184.             else  
  185.             {  
  186.                 // Set the error message with the web api error message. The ReasonPhrase set in BLL_ClientError.  
  187.                 errorMessage = clientErrorResult.ApiErrorMessage;  
  188.             }  
  189.         }  
  190.         catch (Exception ex2)  
  191.         {  
  192.             // Pass data from the controller to the corresponding view.  
  193.             errorMessage = "Failure in ProcessClientError. Exception error: " + ex2.Message + ". Original error: " + exceptionMessage;  
  194.         }  
  195.     }  
  196.   
  197.     // Check if there was an error.  
  198.     if (errorMessage == "")  
  199.     {  
  200.         // Close the stream.  
  201.         log.Close();  
  202.   
  203.         // Cancel the current session.  
  204.         filterContext.HttpContext.Session.Abandon();  
  205.   
  206.         // Set the action result.  
  207.         filterContext.Result = new ViewResult()  
  208.         {  
  209.             // Set the name of the view to render - the Views/Shared/Error.cshtml.  
  210.             ViewName = "Error"  
  211.         };  
  212.     }  
  213.     else  
  214.     {  
  215.         // Write the error to the text file.  
  216.         // - It may append to the text file if it already exists.  
  217.   
  218.         // Write to the 'error log' text file.  
  219.         // First, write the timestamp to the file.  
  220.         log.WriteLine(Environment.NewLine + DateTime.Now);  
  221.   
  222.         // Set the text with a header statement.  
  223.         strLogText = "Attmpted to send a client error alert email to a staff member (an Admin) - but got an error. See it below.";  
  224.   
  225.         // Write the text.  
  226.         log.WriteLine(strLogText);  
  227.   
  228.         // Write an empty line.  
  229.         log.WriteLine();  
  230.   
  231.         // Clear the previous.  
  232.         strLogText = "";  
  233.   
  234.         // Set the text with the error message.  
  235.         strLogText += "Message ---\n{0}" + errorMessage;  
  236.   
  237.         // Write the text.  
  238.         log.WriteLine(strLogText);  
  239.   
  240.         // Write an empty line.  
  241.         log.WriteLine();  
  242.   
  243.         // Close the stream.  
  244.         log.Close();  
  245.   
  246.         // Cancel the current session.  
  247.         filterContext.HttpContext.Session.Abandon();  
  248.   
  249.         // Set the action result.  
  250.         filterContext.Result = new ViewResult()  
  251.         {  
  252.             // Set the name of the view to render - the Views/Shared/ErrorSendingError.cshtml.  
  253.             ViewName = "ErrorSendingError"  
  254.         };  
  255.     }  
  256. }  
  257.   
  258. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
  259. // async Method.  
  260. // Returns a model.  
  261. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
  262. public async Task<ControlResult> GetControl()  
  263. {  
  264.     BLL_Control bll_Control = new BLL_Control();  
  265.   
  266.     ControlResult controlResult = new ControlResult();  
  267.   
  268.     try  
  269.     {  
  270.         // Call the model class to get the control field.  
  271.         controlResult = await bll_Control.GetControl();  
  272.     }  
  273.     catch (Exception)  
  274.     {  
  275.         throw;  
  276.     }  
  277.   
  278.     return controlResult;  
  279. }  
  280.   
  281. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
  282. // async Method.  
  283. // Returns a model.  
  284. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  
  285. public async Task<ClientErrorResult> ProcessClientError(string userName, string errorMessage, string additionalInfo)  
  286. {  
  287.     BLL_ClientError bll_ClientError = new BLL_ClientError();  
  288.   
  289.     ClientErrorResult clientErrorResult = new ClientErrorResult();  
  290.   
  291.     try  
  292.     {  
  293.         // Call the model class to process the client error.  
  294.         clientErrorResult = await bll_ClientError.ProcessClientError(userName, errorMessage, additionalInfo);  
  295.     }  
  296.     catch (Exception)  
  297.     {  
  298.         throw;  
  299.     }  
  300.   
  301.     return clientErrorResult;  
  302. }  
 
 
 

Answers (2)