elmah / Elmah

Error Logging Modules & Handlers for ASP.NET
https://elmah.github.io/
Apache License 2.0
306 stars 65 forks source link

How to log errors for referenced assembly in Azure functions? #429

Closed shyamal890 closed 6 years ago

shyamal890 commented 6 years ago

Azure functions app references a asp.net data service project assembly. This assembly has ELMAH setup to log manual errors like:

public static void LogError(Exception ex, string contextualMessage = null)
        {
            // log error to Elmah
            if (HttpContext.Current == null)
            {
                //Incase there is no HttpContext available
                ErrorLog.GetDefault(null).Log(new Error(ex));
            }
            else if (contextualMessage != null)
            {
                // log exception with contextual information that's visible when 
                // clicking on the error in the Elmah log
                var annotatedException = new Exception(contextualMessage, ex);
                ErrorSignal.FromCurrentContext().Raise(annotatedException);
            }
            else
            {
                ErrorSignal.FromCurrentContext().Raise(ex);
            }
        }

Moreover, all service calls are wrapped with try,catch block. So whenever azure functions app calls a function from the reference project, incase there's an error it would be caught by ELMAH and logged in the database.

However, I am not able to see any new entry in elmah's table.

Example of service function:

public static string GenerateSomething(SomeModel model)
{

    using (var dbContextTransaction = db.Database.BeginTransaction())       //Commit-RollBack
    {
        try
        {
            object o2 = null;
            int i2 = (int)o2;   // Error

            return "Success";
        }
        catch (Exception ex)
        {
            ElmahCustomErrorLog.LogError(ex);

            return "Error: " + ex.ToString();
        }
    }
}

Also tried installing elmah.bootstrapper nuget package instead of elmah. That too didn't work

atifaziz commented 6 years ago

Is your error log configured correctly? What ErrorLog implementation does ErrorLog.GetDefault(null) return?

shyamal890 commented 6 years ago

ErrorLog is a inbuilt method of Elmah comes when you download Elmah.bootstrapper or Elmah library. ErrorLog.GetDefault(null) is utilized to set HttpContext to null as call from Azure functions app may not have a HttpContext

atifaziz commented 6 years ago

ErrorLog is an abstract base class, not a method. You have to use/configure a concrete implementation to be used to log to some database. That's why I asked, what does ErrorLog.GetDefault(null) return. What's your back-end database where are hoping it to log to?

shyamal890 commented 6 years ago

Followed the guide in https://github.com/elmah/Bootstrapper to setup settings. When one adds elmah Bootstrapper it creates a Elmah.Ahtz.config file in the solution folder. Which I haven't modified.

The only setting I added is to log errors in database. Here is the connectionString added to Web.Config file.

<add name="elmah:sql" connectionString="Data Source=SHYAMALPC;Initial Catalog=elmah;Integrated Security=SSPI;"

ErrorLog.GetDefault(null) though creates an In-memory Log, wonder how to set it up to log to database.

atifaziz commented 6 years ago

From the sound of it, looks like you did the right setup but obviously it's still not working so I wonder if it's got to do without your environment.

ErrorLog.GetDefault(null) though creates an In-memory Log, wonder how to set it up to log to database.

This is what I suspected.

Seems to me that Bootstrapper's start-up code is either not getting called or it's not picking up your configuration. Any chance you can debug through that piece to see if it's getting called?

Also, what's the version of ASP.NET you are targeting?

shyamal890 commented 6 years ago

@atifaziz Targeting .Net Framework 4.6 . Don't know how to debug into a DLL.

atifaziz commented 6 years ago

I suggest you create the simplest, even empty, ASP.NET 4.6 Web Application project, add elmah.bootstrapper package and use the following configuration (replacing the YOUR_* bits):

<appSettings>
    <add key="elmah:sql:applicationName" value="YOUR_APP_NAME"/>
</appSettings>
<connectionStrings>
    <add name="elmah:sql" connectionString="Server=YOUR_SERVER_NAME;Database=YOUR_DB_NAME;Trusted_Connection=True;"/>
</connectionStrings>

Do the errors get logged?

shyamal890 commented 6 years ago

So the referenced project which had elmah boostrapper setup is referenced by another parent webApi project where ErrorLog.GetDefault(null) points correctly to the database with applicationName. While when the project is referened in azure functions, ErrorLog.GetDefault(null) fails to correctly point to the sql database.

Does the referenced project look for settings in the parent project?

Also tried creating new project as suggested and referencing it in azure functions, same issue: ErrorLog.GetDefault(null) fails to correctly point to the sql database.

shyamal890 commented 6 years ago

@atifaziz Any thoughts?

atifaziz commented 6 years ago

ELMAH & its Bootstrapper are primarily designed to be used in ASP.NET web applications. If you're using it for Azure Functions then you're on your own, I'm afraid, and what you're observing seems normal. You'll have to do your own setup (pretty similar to how Bootstrapper does it for you) to have ErrorLog.GetDefault(null) return the right ErrorLog implementation. This can be done with ServiceCenter at run-time. For more information, see my reply in the “How to set the SqlErrorLog ConnectionString at runtime” thread over at the ELMAH Discussion Group.

Does the referenced project look for settings in the parent project?

There's no concept of a project at run-time so no.

shyamal890 commented 6 years ago

Thanks that worked!

public static void Config()
{
    var currentServiceCenter = Elmah.ServiceCenter.Current;
    Elmah.ServiceCenter.Current = context =>
    {
        var container = new ServiceContainer(currentServiceCenter(context));
        var connectionString = ConfigurationManager.ConnectionStrings["ErrorLog"].ToString();
        var log = new SqlErrorLog(connectionString)
        {
            ApplicationName = "appName",
        };
        container.AddService(typeof(ErrorLog), log);
        return container;
    };
}