ColdBox logger plugin

Luis,

I would like to suggest a change to the ColdBox Logger Plugin. I may
not have thought this all through correctly so please let me know if I
am missing something.

Long story short, the issue is that it is impossible to use the
ColdBox Logger Plugin with a LogBox appender that expects complex data
for the extraInfo value. By default the logger plugin is what we have
injected into out model and handlers with the following DSL:
"coldbox:plugin:logger"

In the past, in my custom exception handler I would call
logger.logErrorWithBean(exceptionBean).
That method extracts out several pieces of the error object and
appends them into a string buffer.

Now, we are flexing the power of LogBox and implementing a custom DB
Appender which will log all out errors in a database. We wish for the
entire cfcatch struct be passed into out db appender in the logEvent
object so we can do whatever we want with it; however logErrorWithBean
is "helpfully" turning it into text for us before that point. My
first thought was to stop calling logErrorWithBean in the logger
plugin and call logEntry() or error() directly. However, both of
those methods only accept an extraInfo parameter that is a "string".
So, basically the logger plugin will never hand anything other than a
"string" extraInfo over to LogBox.

We have worked around it for now by directly wiring LogBox in with
"coldbox:logbox", asking it for a logger named after the appName
setting (since that's what the logger plugin did. Is that correct?)
and calling its error() method directly.
That works, but now we lose the bugReport E-mail that the logger
plugin used to take care of. Ok, no problem, we'll just add in an E-
mail appender as well to replace that. (I've actually been meaning to
ask you for a while why the bugReport functionality isn't replaced
entirely by an E-mail appender in LogBox)

So, all that being said-- I think it might have made sense for the
logger plugin to ONLY pass strings through as extraInfo back in the
old days prior to the days of LogBox, but I don't think it's fair that
the logger plugin in ColdBox assumes that any extraInfo in an error
message should be reduced to a string now considering the vast power
and options LogBox gives us. I would like to suggest that the
logErrorWithBean method in the logger plugin pass through the
extraInfo in whatever state it is passed in as. Then, the decision of
what to do with it should be left up to the appender in question. The
logEvent.getExtraInfoAsString() method that you already have in place
might be an excellent place to move that logic to.

Well, actually I think logErrorWithBean should be completely
deprecated, and the logger plugin's error() method should be called
everywhere (including the ExceptionService) and a separate E-mail
appender configured if you want the bug report, but you may not agree
with me to that level. :slight_smile:

Thoughts?

Thanks!

~Brad

Yes, Brad to pretty much everything. Moving to LogBox has been a progress and I think it will take me up to 3.1 to finalize all aspects of it. Also, bug reporting is something that will need to be abstracted out also, but until 3.1

I am thinking of maybe having the bug reporting features as an interceptor, so it can be very customizable or as an appender dedicated for bug reports. Anyways, that is 3.1

Also, I am updating the Logger to extraInfo=any, nice catch.

Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Also, I remembered that I turned the exception bean into a string, because some exception structures when passed for logging where throwing exceptions, as if it goes into an appender that is multi-threaded, it makes copies of the exception structure, and some objects there might be expensive and some java exceptions non-serializable. I remember this, so I decided to log the exceptions as plainly as I could and if users wanted to use the real exception structure, then they can intercept and do as they please.

But at least the method described in the logger, for the logErrorWithBean() was a way to consistently log errors. Anyways, I have revamped it a lot and committed tonight.

Luis F. Majano
President
Ortus Solutions, Corp

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Thanks for the replies Luis. For what it's worth, this is what I have
in place right now, but since I don't like to modify the core. I might
not keep it if you don't adopt something similar.

First, I created a new method in exceptionBean called $toString()
because it felt like the exception bean should be responsible for
knowing how to turn itself into a string, not the logger plugin, or
logbox. I REALLY wanted the method to be named toString(), but that
is only possible in ACF with a "hacky" line like so in the component
and it felt dirty:
<cfset this.toString = $toString>

Here is the $toString() method (basically the guts of the logger
plugin's logErrorWithBean method):

<cffunction name="$toString" access="public" returntype="string"
output="false" hint="I return the exceptionBean in string format.">
  <cfscript>
    // Prepare String Buffer (framework isn't handy to ask for the
String Buffer plugin)
    var myStringBuffer =
createObject("java","java.lang.StringBuffer").init(javaCast("int",
500));

    if ( getType() neq "" ){
      myStringBuffer.append("CFErrorType=" & getType() & chr(13) );
    }
    if ( getDetail() neq "" ){
      myStringBuffer.append("CFDetails=" & getDetail() & chr(13) );
    }
    if ( getMessage() neq "" ){
      myStringBuffer.append("CFMessage=" & getMessage() & chr(13) );
    }
    if ( getStackTrace() neq "" ){
      myStringBuffer.append("CFStackTrace=" & getStackTrace() &
chr(13) );
    }
    if ( getTagContextAsString() neq "" ){
      myStringBuffer.append("CFTagContext=" & getTagContextAsString() &
chr(13) );
    }

    return myStringBuffer.toString();
  </cfscript>
</cffunction>

Next I modified the LogEvent's getExtraInfoAsString method to be a bit
smarter:

<cffunction name="getExtraInfoAsString" access="public"
returntype="string" output="false">
  <cfset var info = instance.extraInfo>
  <cfscript>
    if( isSimpleValue(info) )
      {
        return info;
      }
    else if( IsObject(info) AND structKeyExists(info,'$toString') )
      {
        return info.$toString();
      }
    else if( IsObject(info) )
      {
        return ""; // CFC's don't support easy serialization. Throw
error? Do nothing?
      }
    else
      {
        return info.toString();
      }
  </cfscript>
</cffunction>

Essentially, any component can be passed in as the extra info, so long
as it has a $toString() method defined. The other option I had was to
use isInstanceOf() (ACF8 and up) to sniff and see if the extraInfo was
an exception bean, but that has the same performance problems
getMetadata() has, and seems to needlessly couple LogBox and ColdBox
internals.

What are you thoughts on that?

~Brad