Setting Up/Using Log System

Swift Mailer is a fantastic library for sending email with php. Discuss this library or ask any questions about it here.

Moderators: Chris Corbyn, General Moderators

Post Reply
curseofthe8ball
Forum Commoner
Posts: 73
Joined: Sun Jun 01, 2003 12:33 am

Setting Up/Using Log System

Post by curseofthe8ball »

I have setup the Swift Mailer scripts on our server and have tested them with the Smoke tests. Everything is working properly and I've put together a small test for myself using the below code:

Code: Select all

require_once "/Library/WebServer/Documents/swift/lib/Swift.php";
require_once "/Library/WebServer/Documents/swift/lib/Swift/Connection/Sendmail.php";
 
//Try to connect using /usr/sbin/sendmail -bs
$swift =& new Swift(new Swift_Connection_Sendmail());
 
$message =& new Swift_Message("Some subject", "Your message <u>here</u>", "text/html");
 
 
if ($swift->send($message, "curseofthe8ball@yahoo.com", "curseofthe8ball@yahoo.com"))
{
    echo "Message sent";
}
else
{
    echo "Message failed to send";
}
 
$log =& Swift_LogContainer::getLog();
echo $log->dump(true);
 
//It's polite to do this when you're finished
$swift->disconnect();
The test works as expected however I'd like to setup/enable the logging system and then figure out how to use it. I've read the docs on the logging feature and have figure out that I need to add the following code somewhere:

Code: Select all

$log =& Swift_LogContainer::getLog();
$log->setLogLevel(3);
The questions I have are:

1. I'm not sure where this code is supposed to go. Do I add it to the above code after the two require_once lines?

2. Is there anything else that needs to be done to enable the logging function, such as CHMOD a directory or specific file?

3. What file is the actual log file?

4. How do I setup a page to read from the log?
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Setting Up/Using Log System

Post by Chris Corbyn »

Swift's standard log is stored in an array for the lifetime of that page request only. It doesn't write to a file. You could get it to write to a file if you implement the Swift_Log interface yourself however.

You can put the lines of code you posted anywhere after the require_once() to Swift.php.

If you'd like some guidance on writing a log which does write to disk I'll be willing to help here on the forum. To be honest, I'd like to try and ecourage users to make use of the abstraction inside Swift a little more since there are all of these things which can be done but nobody really knows about them :P
curseofthe8ball
Forum Commoner
Posts: 73
Joined: Sun Jun 01, 2003 12:33 am

Re: Setting Up/Using Log System

Post by curseofthe8ball »

Chris,

Thanks for the reply. Your support is one of the biggest reasons I have moved towards using the program. The main reason being that one of our clients who we utilize the mail() function in PHP and PEAR for their email confirmations of online event and dining reservations has reported an error related to the function that is stopping the email confirmations from being sent out. It doesn't happen for all reservations but it is happening more frequently as of late and unfortunately noone has been able to help us troubleshoot the problem to figure out how to fix it. Which leads us to Swift Mailer as a possible alternative. I am happy thus far and would like to deploy it into our client's site however I am definately interested in setting up the log to write to a file, or atleast setup the script to email me the details if/when the function fails. The reason this is important is because I'd like to add this to the site and keep a close eye on things over the next 7-14 days so I can pass some data onto the client to verify that the problem has been fixed.

So, let me know what we need to do to get the log to write to a file or email the log details to me when/if it fails.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Setting Up/Using Log System

Post by Chris Corbyn »

Excellent to hear :)

I'm just finishing off something for the upcoming version so gimme a tick then I'll come back here with a little walkthrough.
curseofthe8ball
Forum Commoner
Posts: 73
Joined: Sun Jun 01, 2003 12:33 am

Re: Setting Up/Using Log System

Post by curseofthe8ball »

Chris,

No problem. I'm hoping to add the Swift Mailer to our client's site this week so do you think it's possible you can assist me in setting up the log to write to a file or email me the log details for failures sometime in the next few days?
User avatar
Zoxive
Forum Regular
Posts: 974
Joined: Fri Apr 01, 2005 4:37 pm
Location: Bay City, Michigan

Re: Setting Up/Using Log System

Post by Zoxive »

curseofthe8ball wrote:... or atleast setup the script to email me the details if/when the function fails.
Just wanted to poke in and question how you would email your self the details, if your e-mail functionality fails?
curseofthe8ball
Forum Commoner
Posts: 73
Joined: Sun Jun 01, 2003 12:33 am

Re: Setting Up/Using Log System

Post by curseofthe8ball »

Zoxive,

Yeah... I probably should've explained that one a bit more. What I was thinking is that the log/message could be stored somewhere, perhaps a MySQL database, that would allow the script to go back and mail the error log to me when the script begins to function again. Generally when/if the script fails, it would be a one-time failure due to connection/server problems so the script should be able to start back up a few minutes later to handle the emailing of the error log.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Setting Up/Using Log System

Post by Chris Corbyn »

Ok, sorry for the delay, had a few visa things to sort out last night :|

So in the Swift code base there's an abstract class called Swift_Log (lib/Swift/Log.php).

This class is what defines the API for a log. You must extend it if you wish to provide your own Log implementation. Now, because it's abstract it forces you to implement 3 methods:

Code: Select all

/**
   * Add a new entry to the log
   * @param string The information to log
   * @param string The type of entry (see the constants: COMMAND, RESPONSE, ERROR, NORMAL)
   */
  abstract public function add($text, $type = self::NORMAL);
  /**
   * Dump the contents of the log to the browser.
   * @param boolean True if the string should be returned rather than output.
   */
  abstract public function dump($return_only=false);
  /**
   * Empty the log contents
   */
  abstract public function clear();
So effectively to Log to a file you just want to implement add() so that it opens a file and writes to it:

Code: Select all

class FileLogger extends Swift_Log {
  private $_logFile;
  public function __construct($logFile) {
    $this->_logFile = $logFile;
  }
  public function add($text, $type = self::NORMAL) {
    $fp = fopen($this->_logFile, 'a'); //Append mode
    flock($fp, LOCK_EX); //Make sure no other request is trying to write to the file!!
    fwrite($fp, $type . $text . PHP_EOL); //Append the entry
    fclose($fp);
  }
 
 
  // ... SNIP ...
  
}
So that's adding, but now we need to be able to clear() the log. Fortunately that's an easy fwrite(fp, '');

Code: Select all

public function clear() {
  $fp = fopen($this->_logFile, 'w'); //Write mode
  flock($fp, LOCK_EX); //Make sure no other request is trying to write to the file!!
  fwrite($fp, '');
  fclose($fp);
}
And finally we want to be able to dump() the log contents. This a bit tricker if we only want to dump() content from the current request rather than the entire log file which could potentially get pretty large. Using ftell() we can work out the offset.

Code: Select all

public function add( ... ) {
  $fp = fopen($this->_logFile, 'a'); //Append mode
  flock($fp, LOCK_EX); //Make sure no other request is trying to write to the file!!
  if (!isset($this->_startOffset)) {
    $this->_startOffset = ftell($fp);
  }
  // ... SNIP ...
}
 
public function dump($return_only=false) {
  $fp = fopen($this->_logFile, 'r');
  fseek($this->_logFile, $this->_startOffset, SEEK_SET);
  $dump = '';
  while (!feof($fp)) {
    $dump .= fread($fp, 8192);
  }
  fclose($fp);
  if ($return_only) {
    return $dump;
  } else {
    echo $dump;
  }
}
Putting that all together it looks like this:

Code: Select all

class FileLogger extends Swift_Log {
  private $_logFile;
  private $_startOffset;
  public function __construct($logFile) {
    $this->_logFile = $logFile;
  }
  public function add($text, $type = self::NORMAL) {
    $fp = fopen($this->_logFile, 'a'); //Append mode
    flock($fp, LOCK_EX); //Make sure no other request is trying to write to the file!!
    if (!isset($this->_startOffset)) {
      $this->_startOffset = ftell($fp);
    }
    fwrite($fp, $type . $text . PHP_EOL); //Append the entry
    fclose($fp);
  }
  
  public function clear() {
    $fp = fopen($this->_logFile, 'w'); //Write mode
    flock($fp, LOCK_EX); //Make sure no other request is trying to write to the file!!
    fwrite($fp, '');
    fclose($fp);
  }
  
  public function dump($return_only=false) {
    $fp = fopen($this->_logFile, 'r');
    fseek($this->_logFile, $this->_startOffset, SEEK_SET);
    $dump = '';
    while (!feof($fp)) {
      $dump .= fread($fp, 8192);
    }
    fclose($fp);
    if ($return_only) {
      return $dump;
    } else {
      echo $dump;
    }
  }  
}
Great! We have a custom built Log implementation. Now we need to make sure Swift is using it

Code: Select all

Swift_LogContainer::setLog(new FileLogger('/tmp/swift.log'));
$log = Swift_LogContainer::getLog();
$log->setLogLevel(4);
That should now be logging to a file at /tmp/swift.log :)

Tracking failures should be in the log file (lines starting with "!!"). There are however other ways of getting the failed address back out:

http://www.swiftmailer.org/wikidocs/v3/sending/batch (See "Checking Addresses which didn't deliver").
curseofthe8ball
Forum Commoner
Posts: 73
Joined: Sun Jun 01, 2003 12:33 am

Re: Setting Up/Using Log System

Post by curseofthe8ball »

Chris,

I forgot to mention that our server has PHP 4 so we are using the PHP 4 version of Swift Mailer. I believe the abstract class you refer to in your code is only in the PHP 5 version, correct?
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Setting Up/Using Log System

Post by Chris Corbyn »

No, the same class exists for PHP4, it's just not declared abstract for obvious reasons ;) You use the same approach to implement your own log, except using PHP4 syntax :)
curseofthe8ball
Forum Commoner
Posts: 73
Joined: Sun Jun 01, 2003 12:33 am

Re: Setting Up/Using Log System

Post by curseofthe8ball »

Chris,

Sorry to be a bother, but what changes would be needed to make the code above with with the PHP 4 version?
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Setting Up/Using Log System

Post by Chris Corbyn »

Code: Select all

class FileLogger extends Swift_Log {
  var $_logFile;
  var $_startOffset;
  function FileLogger($logFile) {
    $this->_logFile = $logFile;
  }
  function add($text, $type = SWIFT_LOG_NORMAL) {
    $fp = fopen($this->_logFile, 'a'); //Append mode
    flock($fp, LOCK_EX); //Make sure no other request is trying to write to the file!!
    if (!isset($this->_startOffset)) {
      $this->_startOffset = ftell($fp);
    }
    fwrite($fp, $type . $text . PHP_EOL); //Append the entry
    fclose($fp);
  }
 
  function clear() {
    $fp = fopen($this->_logFile, 'w'); //Write mode
    flock($fp, LOCK_EX); //Make sure no other request is trying to write to the file!!
    fwrite($fp, '');
    fclose($fp);
  }
 
  function dump($return_only=false) {
    $fp = fopen($this->_logFile, 'r');
    fseek($this->_logFile, $this->_startOffset, SEEK_SET);
    $dump = '';
    while (!feof($fp)) {
      $dump .= fread($fp, 8192);
    }
    fclose($fp);
    if ($return_only) {
      return $dump;
    } else {
      echo $dump;
    }
  }  
}

Code: Select all

$log =& new FileLogger('/tmp/swift.log');
Swift_LogContainer::setLog($log);
$log->setLogLevel(4);
I haven't tested that code... it was supposed to be a walkthrough example rather than a copy & paste example so I'd suggest trying to understand how it works before copying and pasting and finding an error in it ;)
Post Reply