################################################################################ # # # ######################################################################## # # # Title : Data::Logger.pm # # # # Description : A super simple logger, you don't actually need to tell it # # # # a single thing. # # # # Coded by : Justin "I'm Really Tired Now" Lee # # # # Version : 0.7.0-PRE # # # # Date : Monday, March 10, 2008 # # # # License : GNU General Public License v.2 # # # # Copyright (C) 2008 Justin Lee # # # ######################################################################## # # # # ######################################################################## # # # This program is free software; you can redistribute it and/or # # # # modify it under the terms of the GNU General Public License version 2 # # # # as published by the Free Software Foundation. # # # # # # # # 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., 51 Franklin Street, Fifth Floor, Boston, MA # # # # 02110-1301, USA. Or, see: # # # ######################################################################## # # # ################################################################################ ############################### ## ######################## ###### MODULE PREAMBLE package Data::Logger; use strict; use warnings; use subs qw( skew ); use Carp; use Deans::Functions qw( skew housekeep ); use FileHandle; use File::Basename; use File::Find; use Class::Date qw(now); ########################################## ## ################################### ###### `GLOBAL' VARIABLES AND CONSTANTS use constant { DEFAULT_LEVEL => 2, DEFAULT_STRING => '\\[%h:%m:%s\\] [(%F)] %L:[ %A]' }; my @LVL = ( # now with less evil '^one|first|(?:warn(?:ing)?|err(?:(?:o|0)r)?|fatal|d(?:ie|eath))(?:s|z)?|1$', '^(?:log(?:ging)?|noted?|info(?:rmation))(?:s|z)?|second|two|2$', '^third|debug(?:g?(?:er|ing))?(?:s|z)?|all|three|3$' }; my @ORD = qw( MSG LVL ERR FNC ); ############################### ## ######################## ###### START OF THE OBJECT INTERFACE sub new { my ($class,$config) = @_; my $log = { _setup($config,(caller)); }; return if exists $log->{ERR}; return bless $log, $class; } ############################### ## ######################## ###### THE _SETUP FUNCTIONZ sub _stp_mode { my ($stp) = @_; if (exists $stp->{Explicit} and $stp->{Explicit}) { return 1; } else { return 0; } } sub _stp_level { my ($stp) = @_; if (defined $stp->{Level}) { for (my $i = 0; $i < $#LVL; $i++) { return $i if $stp->{Level} =~ /$LVL[$i]/i; } } return if _stp_mode($stp); #explicit return DEFAULT_LEVEL; } sub _stp_string { my ($stp) = @_; return _charachangi($stp->{String}) if defined $stp->{String}; return if _stp_mode($stp); #explicit return DEFAULT_STRING; } sub _stp_path { my ($stp,$tok) = @_; if (not defined $stp->{Path}) { return if _stp_mode($stp); #explicit $stp->{Path} = DEFAULT_dir; mkdir($stp->{Path},0777) or return; } if (defined $stp->{Dated}) { $stp->{Path} = skew $stp->{Path}, decode('%Y-%M-%D',$tok); mkdir($stp->{Path},0777) or return; } return $stp->{Path} if -d $stp->{Path}; return; } sub _stp_clean_house { my ($stp,$dir) = @_; if (defined $stp->{Keep}) { return unless housekeep( $dir, int($stp->{Keep}), # I don't even trust my mom. _stp_mode($stp) ); } return 1; } sub _stp_tokens { my ($stp) = @_; my $tok = _have_a_toke_man(); if (defined $stp->{Functional}) { $tok->{F}->{toe} = 1; } if (defined $stp->{Title}) { $tok->{ } sub _setup { my ($stp,@caller) = @_; unless (defined $stp) { $stp = {}; } unless (ref $stp eq 'HASH') { return _throw_error('STP','Hash'); } my %log; $log->{Handle} = new FileHandle; $log->{Level} = _stp_level($stp); $log->{String} = _stp_string($stp); $log->{Token} = _stp_tokens($stp); $log->{Path} = _stp_path($stp,$log->{Token}); $log->{Explicit} = _stp_mode($stp); $log->{Handle}->open($log->{Path},'>'); defined $_->[0] or _throw_error(@{$_->[1]}) foreach ( [$log->{Level}, ['STP','Level']], [$log->{String}, ['STP','String']], [$log->{Path}, ['STP','Path']], [$log->{Handle}, ['STP','Handle']], [_stp_clean($stp,$log->{Path}), ['STP','Clean']] ); undef $stp; return %log; } ############################### ## ######################## ###### TOKEN FUNCTIONS sub _deblock { my ($str) = @_; while ($str =~ s/\[([^\[\]]*)\]/\%0/o) { my $blk = $1; if ($blk =~ /\%([^\d])/o) { my $wtf = _gettoke($1); if ($wtf and $wtf ne '?') { $str =~ s/\%0/$blk/; next; } } else { $str =~ s/\%0//o; } } return $str; } sub _detoke { my ($str) = @_; $str =~ s/\%(.)/_gettoke($1)/ge; return $str; } sub _gettoke { my ($log,$tak) = @_; my $tik = $log->{token}; if (exists $tik->{$tak} and $tik->{$tak}->{toe}) { # exists & active if (defined $tik->{$tok}->{val}) { # defined if (ref $tik->{$tok}->{val} eq 'CODE') { return &{$tik->{$tok}->{val}}; } else { return $tik->{$tok}->{val}; } } else { return '?'; # active, but no value (suggests problem) } } else { return ''; # doesn't exist or not active } } sub _charachangi { my ($str) = @_; $str =~ s/\\\\/\%1/og; $str =~ s/\\\%/\%2/og; $str =~ s/\\\[/\%3/og; $str =~ s/\\\]/\%4/og; return $str; } ################################# ## ########################## ###### STUFF THAT BUILDS STUFF sub _mk_hash { return $_[0] if ref $_[0] eq 'HASH'; return { MSG => $_[0], LVL => $_[1], ERR => $_[2], FNC => $_[3] }; } sub _mk_dt_obj_thg { my ($gm) = @_; return unless defined $gm; return sub { my %dt; @dt{ qw( year month day hour minute second ) } = split(/(?:\s|:|-|;)+/,now); return scalar $dt{lc($gm)}; } } ############################### ## ######################## ###### CORE LOGGING FUNCTIONS sub print { my ($log,@tik) = @_; $log = $log->update($tik); } sub decode { my ($log,$str) = @_; $str = _deblock($log,$str); $str = _detoke($log,$str); return $str; } sub update { my ($log,@tik) = @_; if (ref $tik[0] eq 'HASH') { $log = $log->_update_tokens(map { exists $tik[0]->{$_} ? $tik[0]->{$_} : undef } @ORD); } else { $log = $log->_update_tokens(@tik) } $log or return; return $log; } ############################### ## ######################## ###### OBJECT FUNCTION ALIASES sub LOGPRINT { shift->print (@_); } sub LOGDEBUG { shift->print(_mk_hash('DEBUG',@_)) } sub LOGDIE { shift->print(_mk_hash('FATAL',@_)) } sub LOGWARN { shift->print(_mk_hash('WARNING',@_)) } sub LOGNOTE { shift->print(_mk_hash('INFO',@_)) } sub raw { shift->print (@_); } sub log { shift->LOGNOTE (@_); } sub die { shift->LOGDIE (@_); } sub warn { shift->LOGWARN (@_); } sub close { shift->LOGCLOSE (@_); } sub debug { shift->LOGDEBUG (@_); } sub note { shift->LOGNOTE (@_); } sub DEBUG { shift->LOGDEBUG (@_); } sub WARN { shift->LOGWARN (@_); } sub DIE { shift->LOGDIE (@_); } sub LOG { shift->LOGNOTE (@_); } sub NOTE { shift->LOGNOTE (@_); } sub PRINT { shift->print (@_); } # add yer own!!! PARRTYYYYYY!! ############################### ## ######################## ###### REALLY LONG FUNCTIONS sub _have_a_toke_man { return ( A => { # message val => '', toe => 1 }, F => { # function title val => '', toe => 0 }, T => { # script name val => '', toe => 1 }, L => { # log level val => '', toe => 1 }, Y => { val => _mk_dt_obj_thg('year'), toe => 1 }, M => { val => _mk_dt_obj_thg('month'), toe => 1 }, LETS_GET_DRUGS => { val => 'lets get drugs!', toe => 0 }, D => { val => _mk_dt_obj_thg('day'), toe => 1 }, h => { val => _mk_dt_obj_thg('hour'), toe => 1 }, m => { val => _mk_dt_obj_thg('minute'), toe => 1 }, s => { val => _mk_dt_obj_thg('second'), toe => 1 }, ); } ##################### # ############## ### ONE!!!1 ... 1;