How to create logs with PHP
From Web Services Wiki
Contents |
Problem
You want to keep track of events within your web application.
Solution
Use a database to store custom logs. When MySQL is unavailable, saving information to flat files is a good alternative.
Logging to a MySQL database
We suggest using MySQL to store logs. Storing logs in a database is often more useful and reliable than storing in plain files. Databases make statistics and queries much easier to perform, and there are fewer potential problems with saving data when compared to working with multiple servers in AFS.
Create a database table
First, create a table to store the values of the variables.
CREATE TABLE my_log( log_id int(11) NOT NULL AUTO_INCREMENT, remote_addr varchar(255) NOT NULL DEFAULT '', request_uri varchar(255) NOT NULL DEFAULT '', message text NOT NULL DEFAULT '', log_date timestamp NOT NULL DEFAULT NOW(), PRIMARY KEY (log_id) ) TYPE=MyISAM COMMENT='Log';
Connect to MySQL database
If you have access to MySQL, storing your logs in a database may be a worthwhile option. Visit Stanford MySQL Service to register your group, department, or service for database access. At this time Stanford does not support MySQL for individual user accounts.
Read How to connect to a MySQL database with PHP and include the database connection code in your script. The database connection object is a necessary argument to the function we use to log data to the database.
Function for logging to MySQL
The following function logs a message with corresponding information about the request to a MySQL table. The variable $db is a mysqli connection object acquired from the step above (How to connect to a MySQL database).
/** * write_mysql_log($message, $db) * * Author(s): thanosb, ddonahue * Date: May 11, 2008 * * Writes the values of certain variables along with a message in a database. * * Parameters: * $message: Message to be logged * $db: Object that represents the connection to the MySQL Server * * Returns array: * $result[status]: True on success, false on failure * $result[message]: Error message */ function write_mysql_log($message, $db) { // Check database connection if( ($db instanceof MySQLi) == false) { return array(status => false, message => 'MySQL connection is invalid'); } // Check message if($message == '') { return array(status => false, message => 'Message is empty'); } // Get IP address if( ($remote_addr = $_SERVER['REMOTE_ADDR']) == '') { $remote_addr = "REMOTE_ADDR_UNKNOWN"; } // Get requested script if( ($request_uri = $_SERVER['REQUEST_URI']) == '') { $request_uri = "REQUEST_URI_UNKNOWN"; } // Escape values $message = $db->escape_string($message); $remote_addr = $db->escape_string($remote_addr); $request_uri = $db->escape_string($request_uri); // Construct query $sql = "INSERT INTO my_log (remote_addr, request_uri, message) VALUES('$remote_addr', '$request_uri','$message')"; // Execute query and save data $result = $db->query($sql); if($result) { return array(status => true); } else { return array(status => false, message => 'Unable to write to the database'); } }
Sample usage
The example below illustrates how to use this function. Pass the message you want to log and a database connection identifier.
$something_happened = rand(0,1); if($something_happened == 1) { write_mysql_log("Something happened!", $db); }
Logging to a file
When MySQL is not an option, such as in the case of personal accounts, logging to a file is a viable alternative.
Create a log file directory
This function requires read, write, and insert access on any directory containing a log file. We suggest making a directory specifically for logs so as to not interfere with the functionality of the rest of your site and also to limit the write and insert abilities necessary for this script to function. Set the permissions as follows.
# Make directory mkdir logs # Enter the directory cd logs # Set permissions fs sa . [cgi_principal_name] rwi
Read more about setting AFS permissions.
Function for logging to a file
The function that follows takes a message and an optional path to a log file as arguments. If no path is given, the log is written to the file specified in DEFAULT_LOG. The message is written along with the remote address, requested script, and the time of the request.
// Filename of log to use when none is given to write_log define("DEFAULT_LOG","/afs/ir/your-home-directory/logs/default.log"); /** * write_log($message[, $logfile]) * * Author(s): thanosb, ddonahue * Date: May 11, 2008 * * Writes the values of certain variables along with a message in a log file. * * Parameters: * $message: Message to be logged * $logfile: Path of log file to write to. Optional. Default is DEFAULT_LOG. * * Returns array: * $result[status]: True on success, false on failure * $result[message]: Error message */ function write_log($message, $logfile='') { // Determine log file if($logfile == '') { // checking if the constant for the log file is defined if (defined(DEFAULT_LOG) == TRUE) { $logfile = DEFAULT_LOG; } // the constant is not defined and there is no log file given as input else { error_log('No log file defined!',0); return array(status => false, message => 'No log file defined!'); } } // Get time of request if( ($time = $_SERVER['REQUEST_TIME']) == '') { $time = time(); } // Get IP address if( ($remote_addr = $_SERVER['REMOTE_ADDR']) == '') { $remote_addr = "REMOTE_ADDR_UNKNOWN"; } // Get requested script if( ($request_uri = $_SERVER['REQUEST_URI']) == '') { $request_uri = "REQUEST_URI_UNKNOWN"; } // Format the date and time $date = date("Y-m-d H:i:s", $time); // Append to the log file if($fd = @fopen($logfile, "a")) { $result = fputcsv($fd, array($date, $remote_addr, $request_uri, $message)); fclose($fd); if($result > 0) return array(status => true); else return array(status => false, message => 'Unable to write to '.$logfile.'!'); } else { return array(status => false, message => 'Unable to open log '.$logfile.'!'); } }
Sample usage
Use the function above to easily write logs to a file:
// Write to default log $result = write_log("An error occurred that prevented a message from being saved"); // Write to another log define("UPLOAD_LOG", "/afs/ir/your-home-directory/logs/upload.log"); $result = write_log("User attempted to send file larger than 2 megabytes", UPLOAD_LOG);
Discussion
Why should I store logs in a database rather than a file?
A database provides more flexibility and reliability than does logging to a file. It is easy to run queries on databases and generate statistics than it is for flat files. Writing to a file has more overhead and will cause your code to block or fail in the event that a file is unavailable. Inconsistencies caused by slow replication in AFS may also pose a problem to errors logged to files. If you have access to MySQL, use a database for logs, and when the database is unreachable, have your script automatically send an e-mail to the site administrator.

