[eluser]ilumos[/eluser]
Hi Guys,
I'm building an application which will be used internally at
LAN parties and I want to be able to log errors as well as other operations, in a database, for analysis and improvements after an event.
Here's the data I'd like to store:
Code:
mysql> desc lan_logs;
+-----------+----------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+----------------------+------+-----+-------------------+----------------+
| log_id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| user_id | bigint(20) unsigned | YES | MUL | NULL | |
| ip | varchar(15) | NO | | NULL | |
| timestamp | timestamp | NO | | CURRENT_TIMESTAMP | |
| category | varchar(50) | NO | | NULL | |
| severity | text | NO | | NULL | |
| message | text | NO | | NULL | |
| data | text | YES | | NULL | |
| file | text | YES | | NULL | |
| line | smallint(5) unsigned | YES | | NULL | |
+-----------+----------------------+------+-----+-------------------+----------------+
So far I've extended Exceptions.php to log PHP and CI errors, and created a library with a log_action method which allows me to log whatever I want behind the scenes in any of my controllers. I've named the library "usage", as using "log" conflicts with the core - is there any way around this, or can anyone think of any better names?
MY_Exceptions.php
Code:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class MY_Exceptions extends CI_Exceptions
{
function log_exception($severity, $message, $file, $line)
{
$severity = ( ! isset($this->levels[$severity])) ? $severity : $this->levels[$severity];
$this->log_error($severity, $message, $file, $line); // pass to general purpose error logging function
}
function show_404($page = '', $log_error = TRUE)
{
$heading = '404 Page Not Found';
$message = 'The page you requested was not found.';
echo $this->show_error($heading, $message, 'error_404', 404);
exit;
}
function show_error($heading, $message, $template = 'error_general', $status_code = 500)
{
set_status_header($status_code);
$log_message = implode("\r\n", ( ! is_array($message)) ? array($message) : $message);
$log_message = $heading.': '.$log_message;
$this->log_error($status_code,'fatal',$log_message); // no way to specify a meaningful error category, or line numbers
$message = '<p>'.implode('</p><p>', ( ! is_array($message)) ? array($message) : $message).'</p>';
if (ob_get_level() > $this->ob_level + 1)
{
ob_end_flush();
}
ob_start();
include(APPPATH.'errors/'.$template.'.php');
$buffer = ob_get_contents();
ob_end_clean();
return $buffer;
}
function log_error($category, $severity, $message, $file = '', $line = '')
{
$CI =& get_instance();
$error_data = array('category' => $category,
'severity' => $severity,
'message' => $message,
'file' => $file,
'line' => $line,
);
$insert = $CI->db->insert('logs', $error_data);
if(!$insert) // fall back to file logging if database insert fails
{
$message = 'Severity: '.$severity.' --> '.$message. ' '.$file.' '.$line;
log_message('error', $message, TRUE);
}
}
}
Usage.php (library)
Code:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Usage {
function log($category, $severity, $message, $data = NULL)
{
$CI =& get_instance();
$CI->load->helper('session');
$user_id = get_user_id();
// find out where the log request was called from
$backtrace = debug_backtrace();
$file = $backtrace[0]['file'];
$file = strstr($file,'/application');
$line = $backtrace[0]['line'];
if(is_array($data))
{
$data = print_r($data,TRUE);
}
$insert_data = array(
'category' => $category,
'severity' => $severity,
'message' => $message,
'data' => $data,
'user_id' => $user_id,
'ip' => $_SERVER['REMOTE_ADDR'],
'file' => $file,
'line' => $line,
);
$CI->db->insert('usage', $insert_data);
}
}
<b>I'd prefer not to log errors by hijacking data as it goes through the show_error() method, but it seems like the only way to catch errors that originate from within CodeIgniter's core.</b>
For fatal errors I'd like to do something along these lines:
Code:
$error = 'Unable to retrieve user details from Steam';
$this->usage->log('auth','fatal',$error);
show_error($error);
But of course this would result in two log entries pertaining to the same error - not good.
Is there a better way I could be doing all this? I'm open to general critique of my code too!
Thanks for reading quite a long post!