Wiki Spaces
Documentation
Projects
Resources
Get Help from Others
Q&A: Ask OpenMRS »
Discussion: OpenMRS Talk »
Real-Time: IRC Chat
Primary mentor |
N/A |
Backup mentor |
N/A |
Assigned to |
N/A |
Log uncaught exceptions to a database table, along with associated metadata (date/time, user, page and/or service, stack trace summary, unique identifier) and provide this information to users in the default error handler so that they can provide a specific error log id along with their bug report. Provide tools to mine this database table to find patterns in error messages, users who frequently hit problems, and to assist in proactively improving the system regardless of whether or not users are reporting their errors.
mseaton
We implemented this in our legacy system in Haiti, and it essentially involved the following:
The exception_log table lists summary information and metadata about the overall exception:
exception_log ( exception_log_id, -- Primary key of the table exception_class, -- The specific type of exception that was thrown (eg. NullPointerException) message, -- The exception message ( eg. throwable.getMessage() ) exception_datetime, -- Timestamp for the exception user_id -- References user to store which user experienced the error )
The exception_log_detail table lists the specific stack trace information that is relevant. This might be restricted on only those parts of a stack trace that provide useful, actionable information (for example, only those classes that match a certain pattern, or only those lines of a stack trace that contain certain information):
exception_log_detail ( exception_log_detail_id, -- Primary key of the table exception_log_id, -- References the exception_log table above file_name, -- The filename extracted from the stack trace class_name, -- The class name extracted from the stack trace method_name, -- The method name extracted from the stack trace line_number -- The line number extracted from the stack trace )
The exception_root_cause table is used to store details about the root cause if it exists, this is important for situations where the original thrown exception is wrapped into another exception type and the actual root cause gets masked in the process, therefore it is important to capture these details too.
The use case for the root cause is that if the message for the thrown exception is blank, we can use that of the root cause and typically the root cause should be included when reporting errors or submitting tickets to jira from the module.
exception_root_cause ( exception_root_cause_id, -- Primary key of the table exception_log_id, -- Foreign Key to the actual exception that was thrown exception_class, -- The specific type of exception that was thrown (eg. NullPointerException) message, -- The exception message ( eg. throwable.getMessage() ) )
The exception_root_cause_detail table hold extra information about the root cause
exception_root_cause _detail ( exception_root_cause _detail_id, -- Primary key of the table exception_root_cause_id, -- References the exception_root_cause table above file_name, -- The filename extracted from the stack trace class_name, -- The class name extracted from the stack trace method_name, -- The method name extracted from the stack trace line_number -- The line number extracted from the stack trace )
In our default error page, prior to displaying any uncaught exception, we first record the exception details to these tables. Then, as a part of the default error page that is shown, we provide a reference to the exception_log id that is recorded, as well as a "More details" link which provides a summary of the exception based on what was recorded in the tables above. This often provides very useful in tracking down errors both in development mode and in production, long after end-users have overcome their initial error.
Once this system is in place, phase 2 would be to build some basic query tools and summary reports that allow for proactive identification and resolution of the exceptions that users are experiencing. This will most likely involve:
It is always quite useful to know the root cause of the exception, so listing it out may be quite useful for debugging purposes.
Since we already use Apache Commons Lang, we can do this quite easily using the ExcptionUtil class (Not to be confused with the OpenMRS ExceptionUtils class. I would recommend using this utility class to not only log the root cause, but many other fields associated with the root cause.
Important methods in the ExceptionUtil class are,
static java.lang.Throwable |
getRootCause(java.lang.Throwable throwable) |
static java.lang.String |
getRootCauseMessage(java.lang.Throwable th) |
This util class contains a lot of helpful methods which deserve further exploration. We may even introduce a new table to capture a very compact stack trace based on these data.
What are our plans for allowing the system to be expandable to support the potential need to log different exception classes differently ?
For example, assume that we get an AuthenticationException. This is a very sensitive exception which is usually caused by a registered user mistyping his usename or password. But what if we want to log this more stringently to check if this was not a hacking attempt ?
In such an event we would need to say,
if(instance of AuthenticationException){ LogToDataBaseDifferently(); //Code behaves differently generateAlerttoAdmin(); } else{ LogToDatabaseNormally(); //Code behaves normally for all other exceptions }
Ultimately, we (or our implementors) may want to introduce their own custom database logging fields.
To me (surangak), the ideal way to support this would be via Spring AOP. As an additional benefit, Spring AOP will also allow separation of concerns, and wont clutter our normal code with logging details.
3 Comments
Dmitry Krivenko
Hi! I'm interested on this idea. My proposal and mockup UI is here:
Logging Errors to the Database
Wyclif Luyima
There is one thing we have been overlooking, we need to add a column for openmrs_version in the exception_log table, otherwise things like line numbers and class_name, method_name can vary version to version
Wyclif Luyima
I know we want to be able to link exception logs to ticket. We might need another table to store ticket info, to get ticket info from jira we might need to know the ticket number, project key, ticket url, i will have to look at the jira api to know what data it exposes. Though we dont have to do this now