Tip: Auditing from inside Extensions

Tip: Auditing from inside Extensions

  
It is possible to write Audits to the application log from within your extensions, much like it can be done from your eSpace.

To achieve this, Hub Edition provides you with the function Audit(), inside the GenericExtendedActions namespace of its runtime. Anywhere inside your C# Extension, when you want to log an audit simply add the line

GenericExtendedActions.Audit( AppInfo.GetAppInfo().OsContext, <Message>, <ModuleName> );


and provide it with a suitable Message and ModuleName. These parameters have the exact same meaning as the equivalent parameters in the Audit Built-in Action in Service Studio. Please refer to Service Studio’s help documentation if you need more information on how the Audit functionality works.

Regards,


Miguel
Is it applicable to Java Version also ? 
Hi Vasanth,

It is surely possible. Make sure you include the package outsystems.hubedition.runtimeplatform.
After this all you need is the following:

GenericExtendedActions.audit(AppInfo.getAppInfo().getOsContext(), "Module", "Message")
And that's it.

Cheers.

How do you log errors (which you may want to catch, but log in the error log)

The exceptions thrown inside extensions are already logged by the platform (although there's a cap to prevent the lgo from flooding)...
Here's the answer you want, in C#. What it does is give you a "LogError" method that will send stuff to the ERROR LOG (not the audit log!) so that it ends up where you expect it to. That lets you handle errors inside a try/catch block, log the error the the log, and set an error message or otherwise gracefully handle the error... because I *hate* getting exceptions from Extensions, and therefore causing the transaction to be aborted... ugh.

Also, you'll note that my version handles the 2,000 character cap (just be careful what you feed this!) nicely, and just produces multiple log messages of 2,000 characters each.

At the top of your extension:

using OutSystems.HubEdition.RuntimePlatform.Log;

Then in your extension (replace "Mandrill" with the name of the extension!):
 

        private void LogError(WebException e)

        {

            var fullMessage = "MESSAGE: " + e.Message;

 

            using (var streamReader = new StreamReader(e.Response.GetResponseStream()))

            {

                fullMessage += "\nRESPONSE: " + streamReader.ReadToEnd();

            }

 

            fullMessage += "\nDATA:";

 

            foreach (var key in e.Data.Keys)

            {

                fullMessage += "\n" + key + ": " + e.Data[key];

            }

 

            LogError(fullMessage);

        }

 

        private void LogError(Exception e)

        {

            ErrorLog.StaticWrite(DateTime.Now, AppInfo.GetAppInfo().OsContext.Session.SessionID,

            AppInfo.GetAppInfo().eSpaceId, AppInfo.GetAppInfo().Tenant.Id,

            AppInfo.GetAppInfo().OsContext.Session.UserId, e.Message, e.ToString(), "Mandrill");

        }

 

        private void LogError(string message)

        {

            if (message.Length <= 2000)

            {

                ErrorLog.StaticWrite(DateTime.Now, AppInfo.GetAppInfo().OsContext.Session.SessionID,

                AppInfo.GetAppInfo().eSpaceId, AppInfo.GetAppInfo().Tenant.Id,

                AppInfo.GetAppInfo().OsContext.Session.UserId, message, "", "Mandrill");

            }

            else

            {

                for (var currentPosition = 0; currentPosition <= message.Length - 1; currentPosition += 2000)

                {

                    var endPosition = Math.Min(message.Length - currentPosition, currentPosition + 2000);

 

                    ErrorLog.StaticWrite(DateTime.Now, AppInfo.GetAppInfo().OsContext.Session.SessionID,

                        AppInfo.GetAppInfo().eSpaceId, AppInfo.GetAppInfo().Tenant.Id,

                        AppInfo.GetAppInfo().OsContext.Session.UserId, message.Substring(currentPosition, endPosition), "", "Mandrill");

                }

            }

        }

Justin James wrote:
Here's the answer you want, in C#. What it does is give you a "LogError" method that will send stuff to the ERROR LOG (not the audit log!) so that it ends up where you expect it to. That lets you handle errors inside a try/catch block, log the error the the log, and set an error message or otherwise gracefully handle the error... because I *hate* getting exceptions from Extensions, and therefore causing the transaction to be aborted... ugh.

Also, you'll note that my version handles the 2,000 character cap (just be careful what you feed this!) nicely, and just produces multiple log messages of 2,000 characters each.

At the top of your extension:

using OutSystems.HubEdition.RuntimePlatform.Log;

Then in your extension (replace "Mandrill" with the name of the extension!):
 

        private void LogError(WebException e)

        {

            var fullMessage = "MESSAGE: " + e.Message;

 

            using (var streamReader = new StreamReader(e.Response.GetResponseStream()))

            {

                fullMessage += "\nRESPONSE: " + streamReader.ReadToEnd();

            }

 

            fullMessage += "\nDATA:";

 

            foreach (var key in e.Data.Keys)

            {

                fullMessage += "\n" + key + ": " + e.Data[key];

            }

 

            LogError(fullMessage);

        }

 

        private void LogError(Exception e)

        {

            ErrorLog.StaticWrite(DateTime.Now, AppInfo.GetAppInfo().OsContext.Session.SessionID,

            AppInfo.GetAppInfo().eSpaceId, AppInfo.GetAppInfo().Tenant.Id,

            AppInfo.GetAppInfo().OsContext.Session.UserId, e.Message, e.ToString(), "Mandrill");

        }

 

        private void LogError(string message)

        {

            if (message.Length <= 2000)

            {

                ErrorLog.StaticWrite(DateTime.Now, AppInfo.GetAppInfo().OsContext.Session.SessionID,

                AppInfo.GetAppInfo().eSpaceId, AppInfo.GetAppInfo().Tenant.Id,

                AppInfo.GetAppInfo().OsContext.Session.UserId, message, "", "Mandrill");

            }

            else

            {

                for (var currentPosition = 0; currentPosition <= message.Length - 1; currentPosition += 2000)

                {

                    var endPosition = Math.Min(message.Length - currentPosition, currentPosition + 2000);

 

                    ErrorLog.StaticWrite(DateTime.Now, AppInfo.GetAppInfo().OsContext.Session.SessionID,

                        AppInfo.GetAppInfo().eSpaceId, AppInfo.GetAppInfo().Tenant.Id,

                        AppInfo.GetAppInfo().OsContext.Session.UserId, message.Substring(currentPosition, endPosition), "", "Mandrill");

                }

            }

        }

 

Hi Justin,

I'm trying to use your solution but I'm getting compiling error from Integration Studio, but in Visual Studio it builds correctly... If I comment your 3 methods it compiles...
I'm using Integration Studio 6.0.0.8 and Visual Studio 2008...

Any thoughts?

Thanks.
 
Jose -

And what error are you getting?

J.Ja
Hi James,

I'm getting none:



Is there a file were Integration Studio writes the log?

Thx!
Jose -

Ah, you've encountered one of the first issues I had with OutSystems! This dialog is VERY confusing.

Click on the *second from the bottom line* (the one that says ".NET Compilation" and "Compiler Error") and the error will be displayed at the bottom.

J.Ja
Hi Justin,

Thanks for the tip!

I had 2 errors:
1) I didn't have SDK installed;
2) The compiler complain about 'var', throwing error CS0246.

As my time was short to dig into the problem, just changed the implicit 'var' to explicit types...

Thx,
JA
Jose -

Yes, that's another known Integration Studio problem... not a "bug", just an "aggravation".

You need to change Integration Studio's .NET options to use the 3.5 .NET compilation, by making the compiler options line look like this:

/nologo /verbosity:minimal /target:Rebuild /property:Configuration=Release /ToolsVersion:3.5

J.Ja
Hi Justin,

Indeed, I was using the IS' default (/nologo /verbosity:minimal /target:Rebuild /property:Configuration=Release).

Tomorrow, at the office, I'll test with the new settings.

Thanks again for the help,
JA
And how about nowadays, folks?

How can I manipulate exceptions from inside an extension?
Just for the record, the Audit function changed slightly in the latest version of ServiceStudio.

Here's the updated version:

GenericExtendedActions.Audit(AppInfo.GetAppInfo().OsContext, "Module", "Message");