I am trying to have Custom Error messages for any any Exceptions thrown by Bot Framework Composer Bot. As the custom runtime of Composer project does not have bot.cs file, it is not possible to implement OnturnError. I have tried to add it in Startup.cs but it is not working. Is there a way I can implement this ?
I have also tried adding a Error handling class inheriting from cloudAdapter and adding it as a service in Startup.cs.
public class AdapterWithErrorHandler : CloudAdapter
{
public AdapterWithErrorHandler(BotFrameworkAuthentication auth, ILogger<IBotFrameworkHttpAdapter> logger, ConversationState conversationState = default)
: base(auth, logger)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
// Send a message to the user
var errorMessageText = "The bot encountered an error or bug.";
var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
await turnContext.SendActivityAsync(errorMessage);
errorMessageText = "To continue to run this bot, please fix the bot source code.";
errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
await turnContext.SendActivityAsync(errorMessage);
if (conversationState != null)
{
try
{
// Delete the conversationState for the current conversation to prevent the
// bot from getting stuck in a error-loop caused by being in a bad state.
// ConversationState should be thought of as similar to "cookie-state" in a Web pages.
await conversationState.DeleteAsync(turnContext);
}
catch (Exception e)
{
logger.LogError(e, $"Exception caught on attempting to Delete ConversationState : {e.Message}");
}
}
// Send a trace activity, which will be displayed in the Bot Framework Emulator
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.Message, "https://www.botframework.com/schemas/error", "TurnError");
};
}
}
Startup .cs
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
You can use "Error occurred event" to handle errors and custom error messages, steps on how to handle exceptions in BotComposer
Related
There is my error in console:
2022-01-17T08:43:15.669Z] Executed 'AutoImport' (Failed,
Id=cfafc70c-40f4-4b42-9af5-76ca76daa53d, Duration=652ms)
[2022-01-17T08:43:15.670Z] System.Private.CoreLib: Exception while
executing function: AutoImport. TeamsAllocationManager.Integrations:
An unexpected error occurred calling external API.
service:FutureDatabase; code:NotFound; message (optional):Not Found.
And there is a code of function:
[FunctionName("AutoImport")]
public async Task AutoImport([TimerTrigger("*/15 * * * * *")] TimerInfo timer, ILogger logger)
{
using (logger.BeginScope(new Dictionary<string, object>
{
["AppVersion"] = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "-",
["Action"] = "AutoImport",
}))
{
try
{
logger.LogInformation($"Auto Import action started");
var command = new ImportProjectsAndEmployeesCommand();
await _dispatcher.DispatchAsync<ImportProjectsAndEmployeesCommand, ImportReportDto>(command);
}
catch (Exception ex)
{
LogException(logger, ex);
throw;
}
}
}
Error triggers in handler by line:
ICollection<User>? users = await _futureDatabaseApiClient.GetUsersAsync();
Im not sure why it occurs, it happen only in dev, in azure i didnt noticed this error. I had a clue about nugets but idk how to figure out which package is missing ( i tried install few or update some and error still occurs).
Sombody know the solution?
The FutureDatabaseApiClient you're using is calling an API that doesn't exist. The error message clearly states:
An unexpected error occurred calling external API. service:FutureDatabase; code:NotFound.
This might be because of a (base) URL not set correctly, or it being a relative URL.
I am using a Kestrel based server application with ASP.net core 2.1. I have a custom error handling middleware like this:
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context /* other dependencies */)
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
Log.Warning(exception,"Exception occurred: {exception}",exception);
var code = HttpStatusCode.InternalServerError; // 500 if unexpected
var result = JsonConvert.SerializeObject(new { error = exception.Message });
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
}
It seems to work in 99% of the cases, but every now, and then the server process stops, and I see some exception as last logged entry. Unfortunately, I haven't been able to reproduce this on my development machine, it only appears on the production system. In my understanding this should not happen in any case.
Are there any known errors I could make to make the server stop? Is there anything I could enable for diagnostics?
The stacktraces of the logged exceptions usually indicate some issue with the input or things which I would like to report using the ErrorHandlingMiddleware.
Are you using Windows or Liunx? If using Windows you should be able to capture a crash dump on process crash using WER (Windows Error Reporting) https://michaelscodingspot.com/how-to-create-use-and-debug-net-application-crash-dumps-in-2019/#Automatically-create-dump-on-Crash.
On Linux you can do this https://www.cyberciti.biz/tips/linux-core-dumps.html
That should let you collect a crash dump and you can analyze it to see where the crash is coming from.
Generally we catch all exceptions that happen during requests. Crashing the process usually means:
An exception thrown from:
an async void method in your code
A background thread
A stackoverflow exception
I have a plugin which fires on Update of the Incident entity. It creates a new record in another table (new_documentaccess). This new_documentaccess record needs to have the same Owner as that of the Incident entity.
Now, I understand that I cannot set the Owner field like any other field on the entity by doing a simple assignment.
So, I wrote in the following.
public void CreateDocumentAccess(Incident incident)
{
new_documentsaccess documentAccess = new new_documentsaccess();
documentAccess.new_CaseId = incident.ToEntityReference();
documentAccess.new_name = incident.OwnerId.Name;
Guid recordId = crmService.Create(documentAccess);
var request = new AssignRequest{
Assignee = new EntityReference(SystemUser.EntityLogicalName, incident.OwnerId.Id),
Target = new EntityReference(new_documentsaccess.EntityLogicalName, recordId)};
crmService.Execute(request);
}
However, I got the following error during execution when I was debugging with
Break when an exception is Thrown: enabled for Common Language Runtime Exceptions.
thrown at the line
var request = new AssignRequest{
Assignee = new EntityReference(SystemUser.EntityLogicalName, incident.OwnerId.Id),
Target = new EntityReference(new_documentsaccess.EntityLogicalName, recordId)};
Principal user (Id=e9e3a98d-a93e-e411-80bc-000c2908bc67, type=8) is
missing prvAssignnew_documentsaccess privilege
(Id=7ecaf3da-77c8-4ee3-9b29-e5a4455cd952)"}
My Plugin code is as follows
try
{
CreateDocumentAccess(Incident incident);
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occurred while creating the document access.", ex);
}
catch (Exception ex)
{
TracingService.Trace("ExecutePreIncidentUpdate: {0}", ex.ToString());
throw;
}
If I just run it as User using the front end, I get the following error.
Exiting PluginPreIncidentUpdate.Execute(), Correlation Id: 4e7e4c3c-3cef-46ab-8d08-a6d0dbca34c7, Initiating User: be179876-9b39-e411-80bb-000c2908bc67
Questions
What code changes should I be making so that even if it errors out
on the above mentioned error, the ErrorDetails.txt captures the
error Principal user (Id=e9e3a98d-a93e-e411-80bc-000c2908bc67, type=8) is missing prvAssignnew_documentsaccess privilege
(Id=7ecaf3da-77c8-4ee3-9b29-e5a4455cd952)?
Why is it not happening already?
Firstly: To answer your questions:
Trace logs are only included with InvalidPluginExecutionException. Your second catch is being used and is throwing the caught exception not an "InvalidPluginExecutionException"
Changing this:
catch (Exception ex)
{
TracingService.Trace("ExecutePreIncidentUpdate: {0}", ex.ToString());
throw;
}
To something like this should pull your trace logs through into the ErrorDetails attachment.
catch (Exception ex)
{
TracingService.Trace("ExecutePreIncidentUpdate: {0}", ex.ToString());
throw new InvalidPluginExecutionException("An error has occurred");
}
Secondly: The cause
The error you are getting is indicating that the user does not have privileges to assign a new_documentaccess record.
When you register a plugin and add a new step; you can choose which user's context to run it in.
By default this is set to "Calling User".
Can you confirm that the calling user is in a security role that has the assign privilege for new_documentaccess records?
I'm trying to create an availability page which checks all the services that a site uses, wrapping each check in a try/catch and then displaying any failures to the users. One of those services is ELMAH, so I am calling that to double check that we can log errors there successfully.
Controller:
var a = new AvailabilityModel();
try {
a.ElmahConnectionString = ConfigurationManager.ConnectionStrings["elmah-sqlserver"].ConnectionString;
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Elmah availability test"));
a.ElmahSuccess = true;
} catch (Exception ex) {
a.ElmahSuccess = false;
a.ElmahException = ex;
Response.StatusCode = 503;
}
return View(a);
When ELMAH succeeds, all is well. When it throws any kind of error (DB permissions, etc.), I get an error which is not captured by the try/catch OR by any of the normal error-capturing pieces: ASP.NET MVC HandleError, customErrors redirects, or even httpErrors in system.webServer. The display is not the normal IIS generic message, instead I see a single line saying "The service is unavailable."
Response:
LTSB-W34511 C:\s\d\build % curl -i http://server/test/availability
HTTP/1.1 503 Service Unavailable
Cache-Control: public, max-age=14400, s-maxage=0
Content-Type: text/html
Server: Microsoft-IIS/7.5 X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 06 Aug 2014 15:46:55 GMT
Content-Length: 27
The service is unavailable.
And that's it. At least I know my availability is not working, but I want to at least display to the user that it's ELMAH causing the problem, and show the connection string it's trying to use. So, I need to capture this exception somehow.
I've tried tweaking my web.config a number of different ways, but I suspect there's something about the way ELMAH inserts itself into the module pipeline which stops me from handling the issue.
Edit:
To clarify, this is a simplified example. I am not planning to expose this information to end users. This availability page will only be available to internal users who are troubleshooting future issues.
ELMAH is only one of the services/databases used by the application in question, and I want to give administrators a quick dashboard-like view of what is up and down. I can't do that if ELMAH errors lead to this insta-503.
Ok, basically this is not possible without any code. The Raise method in Elmah will not let you see any error except if you trace it:
// ErrorLogModule.LogException
try
{
Error error = new Error(e, context);
ErrorLog errorLog = this.GetErrorLog(context);
error.ApplicationName = errorLog.ApplicationName;
string id = errorLog.Log(error);
errorLogEntry = new ErrorLogEntry(errorLog, id, error);
}
catch (Exception value)
{
Trace.WriteLine(value);
}
However when the event is successfully logged the ErrorLogModule will call the logged event in order to let potential listeners know that the logging was a success. So let's quickly write a custom class that will override some methods from the ErrorLogModule and will allow us to notice that the event was not logged:
public class CustomErrorLogModule: Elmah.ErrorLogModule
{
public Boolean SomethingWasLogged { get; set; }
protected override void OnLogged(Elmah.ErrorLoggedEventArgs args)
{
SomethingWasLogged = true;
base.OnLogged(args);
}
protected override void LogException(Exception e, HttpContext context)
{
SomethingWasLogged = false;
base.LogException(e, context);
if (!SomethingWasLogged)
{
throw new InvalidOperationException("An error was not logged");
}
}
}
Swap the ErrorLogModule with the CustomErrorLogModule in your configuration file and Elmah will complain when something wrong is happening; calling Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("test")); in a test page lets the InvalidOperationException("An error was not logged") be thrown out of the call.
If you want to get back the exact exception that occured when trying to log the exception, you can use the fact that the ErrorLogModule traces the exception when it occurs. Create a listener class:
public class ExceptionInterceptor : DefaultTraceListener
{
public Exception TracedException { get; set; }
public override void WriteLine(object o)
{
var exception = o as Exception;
if (exception != null)
{
TracedException = exception;
}
}
}
Then your LogException method becomes
protected override void LogException(Exception e, HttpContext context)
{
var exceptionListener = new ExceptionInterceptor();
Trace.Listeners.Add(exceptionListener);
try
{
SomethingWasLogged = false;
base.LogException(e, context);
if (!SomethingWasLogged)
{
throw exceptionListener.TracedException;
}
}
finally
{
Trace.Listeners.Remove(exceptionListener);
}
}
EDIT: or even if you want to be as terse as possible
public class ExceptionInterceptor : DefaultTraceListener
{
public override void WriteLine(object o)
{
var exception = o as Exception;
if (exception != null)
{
throw exception;
}
}
}
// snip... LogException in your CustomErrorLogModule
protected override void LogException(Exception e, HttpContext context)
{
var exceptionListener = new ExceptionInterceptor();
Trace.Listeners.Add(exceptionListener);
try
{
base.LogException(e, context);
}
finally
{
Trace.Listeners.Remove(exceptionListener);
}
}
One final word: There is a smell in this way of checking for service availability, and you are going to pepper your error database with test exceptions which may not be the desired behavior. I understand that you aim to check the whole logging chain but perhaps there could be some other way to do it; I don't really know your context so I won't comment any further but don't hesitate to think on it.
Anyway, these changes should let you receive the exception you will need.
important edit: very important point: you may want to add a trigger to your CustomErrorLogModule so it doesn't throw when you are not testing. The resilience you are observing in Elmah is generally a good thing because you don't want a diagnostic platform to cause problems that may necessitate other diagnostics. That's why Elmah or logging frameworks don't throw, and that's why you should make the exception rethrowing mechanism triggerable so your program doesn't have to watch its step when raising exceptions in Elmah.
No, no no! Never display the connection string to the user and never tell them what the problem is. Doing so is a serious security vulnerability. Simply put, don't do it. Fix your underlying problems with Elmah.
Problems in your error handling pipeline are very bad, because it'll cause it to try to handle the new error that's generated, which basically causes a loop. The ASP.NET engine recognizes that something serious has gone wrong, so it gives a generic "the service is unavailable" message. Check the event logs on your server to find out the underlying Elmah error and correct it.
I have a website on appharbor.
I know there is logentries for logs on demand.
But, how can I see all unhandled server-side exceptions?
I have found this snippet (to put int Global.ascx), but couldn't find what are all the required dlls to reference.
protected void Application_Error(object sender, EventArgs e)
{
try
{
var exception = Server.GetLastError();
var logEntry = new LogEntry
{
Date = DateTime.Now,
Message = exception.Message,
StackTrace = exception.StackTrace,
};
var datacontext = new LogDBDataContext();
datacontext.LogEntries.InsertOnSubmit(logEntry);
datacontext.SubmitChanges();
}
catch (Exception)
{
// failed to record exception
}
}
AppHarbor already logs application errors, you can inspect them by clicking "Errors" in the menu to the left of the application dashboard.
Another great option is to add Elmah to your AppHarbor application, but remember to lock down Elmah view.
Should you still want to use the snippet above, then take a look at the full sample to see what other dependencies are required.