Luka - Exception handling and reporting framework
use Error qw(:try); use Luka; try { $ftp->login("someuser", "somepass") || throw Luka::Exception::External ( error => $ftp->message . $@, id => "login", context => "FTP error: couldn't login", severity => 3, args => "user=someuser,pass=somepass" ); } catch Luka::Exception with { my $e = shift; $e->report; return 17; } catch Error with { my $e = shift; $e->report; return 18; };
Luka is an exception handling and reporting framework. It's useful to look at it as an event handling framework.
It comes from operational understanding of networks.
Scenario that Luka is addressing is following: on a network with multiple hosts running multiple applications, it is very difficult to track operational status of all the functionality that those applications and hosts are meant to deliver. In order to make it easier, we decided to specify the error handling and reporting data model that each component delivering functionality has to conform to. What is a component? In most cases, it is a script, often run from cronjob, in some cases it is a class in an application. In all cases, a component has to successfully complete a task on which functionality of an application, or entire network, relies on.
It is common practice that programmers choose their way of handling errors and reporting. Luka is an attempt to standardize that process. Its primary goal is to make it easier for smaller number of people to keep larger number of applications and networks running.
Policy on script error handling that Luka suggests:
Already a common practice, especially in applications/components that are not small.
As opposed to each network library, for example, having it's own way to report connection error.
Already a common practice. Luka suggests that link to page describing all possible errors, along with dependencies and schedules (for components that run regularly), should exist. It is part of the Luka event data model.
[global] debug=0 single_char_error_code=E single_char_success_code=I doc_base=http://localhost/ email_domain=lists.mydomain.org syslogopt=pid,nowait syslogfacility=daemon expected_ip=10.1.8 [myscript.pl] on_success=Task completed doc=LukaTests about=this library does something useful from=root@localhost cc=me@mydomain.org nomail=0
On an error caught, in syslog:
Feb 26 15:34:39 localhost myscript.pl[1298]: Luka initiating... Feb 26 15:34:39 localhost myscript.pl[1298]: Error at line 20: Net::FTP: Bad hostname 'bla.org' at myscript.pl line 324. Feb 26 15:34:39 localhost myscript.pl[1298]: Error report sent to myscript.pl@lists.mydomain.org,me@mydomain.org
Email headers:
From: root@localhost To: myscript.pl@lists.mydomain.org Cc: me@mydomain.org Subject: [galeb][2006-2-26T15:34:42][E] Net::FTP: Bad hostname 'bla.org'
Event (used verbatim in email body):
this library does something useful http://localhost/LukaTests#ftp_object_creation host=galeb hosterr= ipaddr=10.1.8.18 time=2006-2-26T15:34:42 script=myscript.pl path=/home/toni/dev/cvs/perl/modules/luka line=245 pid=1298 severity=3 context=FTP error: couldn't create object args=ftp.false id=ftp_object_creation error=Net::FTP: Bad hostname 'bla.org' Trace begun at myscript.pl line 245 main::__ANON__ at /usr/local/share/perl/5.8.7/Error.pm line 372 eval {...} at /usr/local/share/perl/5.8.7/Error.pm line 371 Error::subs::try at myscript.pl line 255 main::ftp_luka_catch at myscript.pl line 123 main::__ANON__ at /usr/local/share/perl/5.8.7/Test/Exception.pm line 281 eval {...} at /usr/local/share/perl/5.8.7/Test/Exception.pm line 281 Test::Exception::lives_and at myscript.pl line 124
On a captured report, in syslog:
Feb 26 15:34:22 localhost myscript.pl[1273]: Luka initiating... Feb 26 15:34:22 localhost myscript.pl[1273]: Success report sent to myscript.pl@lists.mydomain.org,me@mydomain.org
From: root@localhost To: myscript.pl@lists.mydomain.org Cc: me@mydomain.org Subject: [galeb][2006-2-26T15:34:22][I] Task completed
this library does something useful http://localhost/LukaTests host=galeb hosterr= ipaddr=10.1.8.18 time=2006-2-26T15:34:22 script=myscript.pl pid=1273
ABOUT COMPONENT \n DOC \n attribute=value attribute=value attribute=value attribute=attribute=value,attribute=value attribute=value \n \n STACKTRACE
ABOUT COMPONENT Comes from config file component section.
DOC Location of the documentation. Can be URL, or some other protocol address. Can be specific to the error reported, or component general. Comes from config file component section.
host - Name of the host where the event originates from. Collected.
hosterr - Name of the services that Luka couldn't use as expected on the host. Collected. The only possible value is, at the moment, syslogd.
ipaddr - IP address of the host. Collected. When multiple IPs present (most cases), regular expression matching one from the configuration file field expected_ip will be chosen.
expected_ip
time - Timestamp, conforming to RFC3339 "Date and Time on the Internet: Timestamps", see http://www.ietf.org/rfc/rfc3339.txt. Example: 2006-2-26T15:34:42. Constructed out of host time.
2006-2-26T15:34:42
script - Name of the component that generated event. Collected.
pid - Process ID of the component that generated the event. Collected from the host.
path - Path to the component that generated event. Collected. Error event only.
line - Line number where event generation occurred. Collected. Error event only. Error event only.
severity - Severity level of the event (ambiguous, see TODO section). Supplied by the programmer at the location of event creation. Error event only.
context - Descriptive text, context of the event. Specific to the functionality that components performs from the user perspective, rather than from the strictly technical perspective of programming libraries. Supplied by the programmer at the location of event creation. Error event only.
args - Arguments relevant for the event generated (passed to the function, object). Supplied by the programmer at the location of event creation. Error event only.
id - ID of the event. Supplied by the programmer at the location of event creation. Matches the documentation for the component. Error event only.
error - Technical text of the error. Supplied by the programmer at the location of event creation. Supplement to the context field, from the technical perspective, can contain error text returned by used programming library. Error event only.
context
Luka report to syslog what happens by default.
Aug 26 11:27:49 localhost myscript.pl[1038]: Error at line 46: Net::FTP: Bad hostname 'ftp.bla.bla' at myscript.pl line 80. Aug 26 11:27:49 localhost myscript.pl[1038]: Error report sent to myscript.pl@lists.mydomain.org
If the $message is not supplied, value of the field on_success from the component section of Luka configuration file will be used.
$message
on_success
Luka system not functional for '%s' script. Couldn't read its section '%s' in config file '%s'
Throws Luka::Exception::Program exception. Luka can not deliver event if section for given script is missing in given config. Sections are by default named by the script name.
Luka system disabled. Couldn't read its config file '%s': %s
Throws Luka::Exception::Program exception. Luka can not do anything if its config file is missing or can not be parsed. However, if syslogd is running, it will place a warning in syslog:
Feb 26 12:26:59 localhost Luka::Conf[30438]: Luka system disabled. Couldn't read its config file 'bla.txt': Failed to open bla.txt: No such file or directory at lib/Luka/Conf.pm line 63
Couldn't report by email to:%s;cc:%s;from:%s
Throws Luka::Exception::External. If MTA is not running, or if Luka can not connect to it, stdout will receive:
ERROR: Can't connect to localhost:25 Couldn't report by email to:test@localhost;cc:toni@localhost;from:root@localhost
In the syslog, warning will be:
Feb 26 13:42:08 localhost myscript.pl[3071]: Couldn't report by email: to: myscript.pl@lists.mydomain.org, cc: me@mydomain.org, from: root@localhost Feb 26 13:42:08 localhost myscript.pl[3071]: Mail system reported: ERROR: Can't connect to localhost:25
Single section, applies to the host on which Luka runs.
[global] debug=0 single_char_error_code=E single_char_success_code=I doc_base=http://localhost/ email_domain=lists.mydomain.org syslogopt=pid,nowait syslogfacility=daemon expected_ip=10.1.8
Fields:
debug - Turns debugging mode on when set to 1.
single_char_error_code - Delivery field. Single character error code. Default is "E". In email delivery, part of header SUBJECT field.
single_char_success_code - Delivery field. Single character success code. Default is "I". In email delivery, part of header SUBJECT field.
doc_base - Event field. Base part of the DOC field in the event data model.
email_domain Delivery field. Email. Domain part of the header TO field.
syslogopt $logopt options passed to Sys::Syslog's openlog function
syslogfacility $facility option passed to Sys::Syslog's openlog function
expected_ip Event field. See above IPADDR field in the event data model. Luka discovers IPs on the host. Since multiple IPs are present in most cases, regular expression matching one from this configuration file field expected_ip will be selected. This would be a drawback on a host with many interfaces, and solution with fixed IP would be a lot more efficient in that case.
One or more sections, applies to components programmed to use Luka.
[myscript.pl] on_success=Task completed doc=LukaTests about=this library does something useful from=root@localhost cc=me@mydomain.org nomail=0
on_success - Delivery field. In email delivery, part of header SUBJECT field.
doc - Event field. Component part of the DOC field in the event data model.
about - Event field. See above COMPONENT field in the event data model.
from - Delivery field. Email. Header FROM field.
cc - Delivery field. Email. Header CC field.
nomail - If set to 1, event will not be delivered via email.
Error - implementation of try/catch syntax
Exception::Class - easy definition of hierarchy of exception classes
Config::IniFiles - config file handling
Sys::Syslog - writing to syslog
Sys::Hostname - determining hostname
Sys::Hostname::Long - determining hostname
Mail::SendEasy - sending reports by email
Class::Std - inside/out classes builder
Mod-perl, due to use of Class::Std. I wasn't aware of Class::Std limitations at the time of writing Luka. There are other implementations of inside-out classes on CPAN that should be used as replacements in of next releases of Luka. At the moment, best candidate seems to be Object::InsideOut.
Please report any bugs or feature requests to bug-luka@rt.cpan.org, or through the web interface at http://rt.cpan.org.
bug-luka@rt.cpan.org
Migration from Class::Std to Object::InsideOut, or some other inside-out class.
Severity needs defining, according to appropriate existing standard.
Timezone needs adding to the date-time (RFC 3339) format.
Reporting is also event delivery, and event delivery can be done in many ways. Currently, email is hardcoded as a delivery mechanism. Instead, reporting delivery has to be configurable. It could be done via dynamic loading (from a value in the config) of class implementing desired mechanism.
It is default now that Luka method report uses syslog for short reporting. It should be made optional, in global, and script, setting. Global config setting should be default; individual script setting should override it.
report
When a section for component is missing in the config file, exception is thrown (see DIAGNOSTICS above). Instead, event should still be delivered, as long as relevant details about the destination of delivery are in the global part of the Luka configuration file.
Documentation needs careful re-reading and improving. Any comments on this especially appreciated.
Ideas for underlining premises of Luka came out of discussions with Bill Hulley.
Toni Prug <toni@irational.org>
Copyright (c) 2006. Toni Prug. All rights reserved.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
See http://www.gnu.org/licenses/gpl.html
To install Luka, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Luka
CPAN shell
perl -MCPAN -e shell install Luka
For more information on module installation, please visit the detailed CPAN module installation guide.