################################################################################ # # Apache::Voodoo::Debug - handles operations associated with debugging output. # # This object is used by Voodoo internally to handling various types of debugging # information and to produce end user display of that information. End users # never interact with this module directly, instead they use the methods from # the Apache::Voodoo base class. # ################################################################################ package Apache::Voodoo::Debug::Native; $VERSION = "3.0200"; use strict; use warnings; use base("Apache::Voodoo::Debug::Common"); use Apache::Voodoo::Constants; use DBI; use HTML::Template; use JSON::DWIW; sub new { my $class = shift; my $id = shift; my $conf = shift; my $self = {}; bless($self,$class); $self->{id}->{app_id} = $id; my $ac = Apache::Voodoo::Constants->new(); my @flags = qw(debug info warn error exception table trace); my @flag2 = qw(profile params template_conf return_data session); $self->{enabled} = 0; if ($conf eq "1" || (ref($conf) eq "HASH" && $conf->{all})) { foreach (@flags,@flag2) { $self->{enable}->{$_} = 1; } $self->{enable}->{anydebug} = 1; $self->{enabled} = 1; } elsif (ref($conf) eq "HASH") { foreach (@flags) { if ($conf->{$_}) { $self->{enable}->{$_} = 1; $self->{enable}->{anydebug} = 1; $self->{enabled} = 1; } } foreach (@flag2) { if ($conf->{$_}) { $self->{enable}->{$_} = 1; $self->{enabled} = 1; } } } if ($self->{enabled}) { my $file = $INC{"Apache/Voodoo/Constants.pm"}; $file =~ s/Constants.pm$/Debug\/html\/debug.tmpl/; $self->{template} = HTML::Template->new( 'filename' => $file, 'die_on_bad_params' => 0, 'global_vars' => 1, 'loop_context_vars' => 1 ); $self->{template}->param( debug_root => $ac->debug_path(), app_id => $self->{id}->{app_id} ); $self->{json} = JSON::DWIW->new({bad_char_policy => 'convert', pretty => 1}); $self->{db_info} = $ac->debug_dbd(); my $dbh; eval { $dbh = DBI->connect(@{$self->{db_info}}); }; if ($@) { warn "Debugging infomation will be lost: $@"; $self->{enabled} = 0; return; } # From the DBI docs. This will give use the database server name my $db_type = $dbh->get_info(17); eval { require "Apache/Voodoo/Debug/Native/$db_type.pm"; my $class = 'Apache::Voodoo::Debug::Native::'.$db_type; $self->{db} = $class->new(); }; if ($@) { die "$db_type is not supported: $@"; } $self->{db}->init_db($dbh,$ac); } # we always send this since is fundamental to identifying the request chain # regardless of what other info we log $self->{enable}->{url} = 1; $self->{enable}->{status} = 1; $self->{enable}->{session_id} = 1; return $self; } sub init { my $self = shift; my $mp = shift; return unless $self->{enabled}; $self->{id}->{request_id} = $mp->request_id(); $self->{db}->set_dbh(DBI->connect(@{$self->{db_info}}) || die DBI->errstr); $self->_write({ type => 'request', id => $self->{'id'} }); $self->{template}->param(request_id => $self->{id}->{request_id}); } sub enabled { return $_[0]->{enabled}; } sub shutdown { $_[0]->{db}->db_disconnect(); return; } sub debug { my $self = shift; $self->_debug('debug', @_); } sub info { my $self = shift; $self->_debug('info', @_); } sub warn { my $self = shift; $self->_debug('warn', @_); } sub error { my $self = shift; $self->_debug('error', @_); } sub exception { my $self = shift; $self->_debug('exception',@_); } sub trace { my $self = shift; $self->_debug('trace', @_); } sub table { my $self = shift; $self->_debug('table', @_); } sub _debug { my $self = shift; my $type = shift; return unless $self->{'enable'}->{$type}; my $data; if (scalar(@_) > 1 || ref($_[0])) { # if there's more than one item, or the item we have is a reference # then we need to serialize it. $data = $self->_encode(@_); } else { # simple scalar can be logged as is. $data = $_[0]; } my $full = ($type =~ /(exception|trace)/)?1:0; $self->_write({ type => 'debug', id => $self->{id}, level => $type, stack => $self->_encode([$self->stack_trace($full)]), data => $data }); } sub mark { my $self = shift; return unless $self->{'enable'}->{'profile'}; $self->_write({ type => 'profile', id => $self->{id}, timestamp => shift, data => shift }); } sub return_data { my $self = shift; return unless $self->{'enable'}->{'return_data'}; $self->_write({ type => 'return_data', id => $self->{id}, handler => shift, method => shift, data => $self->_encode(shift) }); } # these all behave the same way. With the execption of session_id which # also inserts it into the underlying template. sub url { my $self = shift; $self->_log('url', @_); } sub status { my $self = shift; $self->_log('status', @_); } sub params { my $self = shift; $self->_log('params', @_); } sub template_conf { my $self = shift; $self->_log('template_conf', @_); } sub session { my $self = shift; $self->_log('session', @_); } sub session_id { my $self = shift; my $id = shift; $self->{template}->param(session_id => $id); $self->_log('session_id',$id); } sub _log { my $self = shift; my $type = shift; return unless $self->{'enable'}->{$type}; my $data; if (scalar(@_) > 1 || ref($_[0])) { # if there's more than one item, or the item we have is a reference # then we need to serialize it. $data = $self->_encode(@_); } else { # simple scalar can be logged as is. $data = $_[0]; } $self->_write({ type => $type, id => $self->{id}, data => $data }); } sub _encode { my $self = shift; my $j; if (scalar(@_) > 1) { $j = $self->{json}->to_json(\@_); } else { $j = $self->{json}->to_json($_[0]); } return $j; } sub _write { my $self = shift; my $data = shift; my $handler = 'handle_'.$data->{'type'}; if ($self->{db}->can($handler)) { $self->{db}->$handler($data); } } sub finalize { my $self = shift; return () unless $self->{enabled}; foreach (keys %{$self->{'enable'}}) { $self->{template}->param('enable_'.$_ => $self->{'enable'}->{$_}); } return (_DEBUG_ => $self->{template}->output()); } 1; ################################################################################ # Copyright (c) 2005-2010 Steven Edwards (maverick@smurfbane.org). # All rights reserved. # # You may use and distribute Apache::Voodoo under the terms described in the # LICENSE file include in this package. The summary is it's a legalese version # of the Artistic License :) # ################################################################################