Using the Directory Iterator Class for Log Files

My favorite use of the Directory Iterator class is cycling through a logs directory. Sometimes I use it when I have a huge directory full of things I need to get a quick look at. Setting up the class to use is a cinch, and can even be easy for someone just beginning to work with object oriented programming.

To set up the object and begin working with it, all you need to know is the path to the directory you wish to iterate through. In our example, that path will be “logs”.

new DirectoryIterator('logs');

The DirectoryIterator class has a lot of great methods to work with, but the ones we will be working with are: isDot(), getBasename(), getSize(), and getMTime()

  • isDot() checks for the (.) and (..) that appear when iterating through a directory at the command line.
  • getBasename() gets the name of the current file in that iteration.
  • getSize() does exactly as it says, it gets the size of the current file.
  • getMTime() gets the last modified time of the current file.

First I will display the code, and then I will explain it. I have concatenated the echos to improve the formatting of the code for display purposes of this article.

To begin iterating through the directory, we will be using a foreach loop:

<?php

// Size to check against //
$size = 0;

// Cycle through logs //
foreach (new DirectoryIterator('logs') as $file) {

    // If not (.) or (..) //
    if (!$file->isDot()) {
        echo '&gt; <a href="log_viewer.php?view='
        . $file->getBasename() . '">'
        . ucfirst($file->getBasename('.txt')) 
        . '</a>';

        // Displays new sign and last time //
        // modified or if file is empty //
        if ($file->getSize() > $size) {
            echo '<span class="red">'
            . '&lt; New &gt;</span> '
            . date("l, F d, Y g:ia", $file->getMTime());
        } else {
            echo '<span class="empty">&lt; Empty &gt;'
            . '</span>';
        }
        echo '<br>';
    }
}
?>

What we are doing here is iterating through the directory and displaying each file as a clickable link. If the file size is higher than our $size variable, a new tag and a time appears to let us know that the file contains new content and when the content arrived. If the file does not contain any new information, a tag appears to let us know it’s empty. Initially I set this variable to zero to signify an empty file for demonstration purposes. Usually, when in production you may choose to empty the file and leave behind a note that you emptied it for future reference. Setting this value higher will let you know if there is  actually anything new or not in the file to make it worth checking.

The $file->isDot() checks to see if the current item is either a (.) or (..). We don’t want these items because they are pretty useless to us. We want to create links to the files inside the directory.

We use the $file->getBasename() to build our links. Just like with the regular php basename(), if we put an extension in as a parameter, it will remove it from the output. So the first call leaves the extension in for the link, and the second call removes it for display purposes.

Now that we have our links built, we want to see how big the file is and insert our tags. The $file->getSize() returns a value in bytes, which we compare against our predetermined $size variable.

If the size comparison is true, then we build the new tag and give it a date. The $file->getMTime() returns a Unix time signature that we can turn into a string for easier reading. If the comparison is false, it just builds an empty tag.

Sure, this may seem like a lot to take in, but it really isn’t. When you break the code down and remove all of the concatenation, you’re left with less than 20 lines of code.

The output from the code:

> Error < New > Thursday, November 22, 2012 9:33pm
> Users < New > Thursday, November 22, 2012 9:24pm
> Empty < Empty >
> Function < New > Thursday, November 22, 2012 9:29pm

Pretty sweet. This is all of the information that we wanted and needed. These are some mock up files that I made to demonstrate this program. As you can see, one of the files is completely empty, and the other three contain new content.

Now, why did we build them into links? Usually, when I’m building a site, I use another page as a log viewer so that I can add extra functionality, like the ability to delete the contents of the file and leave behind a little note for myself. But in our little program, the log viewer is going to be the same page. So, we are going to be working with the $_GET variable that will supply us with a view of the log that we want to look at. By clicking on one of the links, we are given a display of the file’s contents as a list.

Here is that code:

<?php

// If view is set, displays log //
if (isset($_GET['view'])) {

    // Path to log file //
    $log = 'logs' . DIRECTORY_SEPARATOR . $_GET['view'];

    // If the file path is good and file opens //
    if (file_exists($log) && is_readable($log)
            && $handle = fopen($log, 'r')) {

        // Name of log //
        echo '<h2>> ' . ucfirst(basename($log, '.txt'))
        . ' Log</h2>';

        // Display contents as list //
        echo '<ul>';

        // Get contents //
        while (!feof($handle)) {
            $entry = fgets($handle);
            if (trim($entry) != "") {
                echo '<li>' . $entry . '</li>';
            }
        }

        echo '</ul>';

        // Close handle //
        fclose($handle);
    } else {

        // Error file bad or could not be opened //
        echo '<br>';
        echo '<span class="red">Could not read from '
        . basename($log) . '</span>';
    }
}
?>

This nugget here builds our display for us. It checks to see if the $_GET['view'] is set, and if it is, gets the contents of our file to display. The if statement makes sure that the file exists, that the file is readable, and that the handle opened up successfully. If all of this is true, it outputs our generated list. If any of these conditions fail, such as somehow the view does not equate to a file name, then it outputs an error message.

When I build a log, I like to build it like so:

 "{Time stamp} |: {Function name / cause of error} :| {Error message}" 

The complete output of the page from clicking on the “Function” link is:

Welcome to the log viewer

> Error < New > Friday, November 23, 2012 12:53am
> Users < New > Friday, November 23, 2012 12:53am
> Empty < Empty >
> Function < New > Friday, November 23, 2012 12:53am

> Function Log
     2012/11/22 - 21:26:22 |: NameChanger :| Operation Failed
     2012/11/22 - 21:27:47 |: FindUser :| Could not locate user: Alexander
     2012/11/22 - 21:28:26 |: EraseUser :| Could not locate user: Bobby
     2012/11/22 - 21:29:27 |: MemoryDump :| Operation timed out

As you can see, all of our links are listed so we can switch back and forth between logs, and our log’s output is neatly displayed. As I said at the start of the article, using the Directory Iterator class is extremely easy and fast to use. If you’ve ever tried to build something similar on your own, you know exactly what I am talking about. It’s great the PHP community has given us such a magnificent tool to use.

If you would like to see more great things that the DirectoryIterator class can do for you, or would just like more information on it, you can find it here at the official php.net site.

Like always, I hope you found this useful and you learned something new.

If you would like to obtain the practice files from this article, you can download the .zip or the .tar.gz.

This entry was posted in Development, PHP and tagged , , , , , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply