Logging Logic errors in PHP
Tags: php, webdev, logs, errors
Published: 2024-05-13 16:10:38

This is for PHP developers. People writing PHP code and having trouble debugging their sites/apps. 

For some reason, a lot of developers seem to have trouble understanding how to add error logging to their applications. 
 
People make it sound overly complicated to implement, and generally want to begin talking about 3rd party services and tools, most of which do not actually tell you what's wrong, but just that there's problems [You don't need to know there's a thousand 500 errors, you need to know WHAT they are, WHEN they happen and WHERE in my code to find them]

## What are Logic errors.
First you need to understand what Logic Errors are. The errors that print out and give you a 500 browser response are pretty much all syntax errors. Occasionally you cause timeouts due to bad loops an database queries. 
Logic errors are those pesky errors where something goes wrong and you can't see it. OR perhaps it displays the wrong information to the user. Your code is executing fine and doing "exactly what you told it to"...but you told it something wrong and don't know that yet. Hence, a LOGIC error. 
Debugging these is the hardest because it requires an understand of the code and finding where to put logs that help tell you something went wrong. An example is, you call a 3rd party API and you always get back a number that you then display. Except one day it returns a letter and your display shows 0 because you're treating it like a number. Nothing breaks exactly, but your info is wrong. You don't know why because you don't have enough validation to tell you it's no longer returning a number. 
Having proper validation is the real culprit, but without it logging that something is wrong, you still wouldn't know, and you don't want to be printing out strange messages to the user, so instead I recommend you log it to a text file instead.

So, here's a few simple steps to implement error logging in your custom [or even not so custom] code.

### Default Error logging in PHP
If you're running PHP, syntax errors are important to debug. You probably do have Apache error logs already in place. By default they're in something like /var/log/apache2/error.log 
You can customize apache's config to tell it to put the logs for your specific site in its own separate log file. 
In the VirtualHost directive you can set:
```
ErrorLog "/whatever/path/you/want/your-custom-file.log"
```
There's a lot more stuff you can do in Apache conf, but that's a story for another time. 
The point here is, you can control where your errors are logged by default, and you should take the time to fix all of them.

Now, this is going to log standard PHP errors, but not your LOGIC errors. Stuff like "if X,  great , else error" in your own code. 
Logical errors are where most devs pretty much fail completely and their apps are full of problems they can't fix because they know something is going wrong but they have no logs telling them what's happening. 

### Setup Logic error logs
In your application, setup a directory somewhere where you want to store logs and give it a name like "payments.log" to log errors related to your payments or maybe "users.log" to log errors within your user actions [i dunno, when updating their profile, changing their password, whatever actions], you decide how you want to split the logs, or if you want you can have one file to log it all [not recommended, depending on site/app complexity]

Once you've created that you need to add a logging function that all of your code can call. Preferably you're using classes and objects and you can create a method within each class, for example If you're using a $user object, perhaps you could call it like so $user->log_error("some message");

In the User class you could create a method like so:
```
public function logError($txt){
	file_put_contents($this->logpath, date('m/d/y-h:ia')." - ".$txt."\n\n", FILE_APPEND | LOCK_EX);
}
```

The big takeaway is that it's just a simple 
```
file_put_contents()
``` 
call with the FILE_APPEND attribute set so it always appends to the file instead of overwriting it, and it has a LOCK_EX attribute so multiple log calls don't overwrite each other and wait until the file is unlocked again.

In between this is the first parameter, the PATH to your log file. 
In my implementation i set a class private variable named "logpath", so i can set a different log file for each class or object [this is optional, just set the path to whatever you want]

The second attribute is the message we're logging, and this is where i've automated it to always add the timestamp, so you don't have to manually code that into each log. So your logging just needs to set a message, this little function adds the time and place.

And TADA, you now have a log that stores WHEN an error happens, WHAT the error is [you need to understand your code to say what that is and you can append objects to it using print_r($var,true) so you can log your variables into the log files.

Something i like to do is in my message to put in what line number this is and which file. 

An example of a log would be after a CURL call:
```
if ($response === false) {
	$logtxt = 'cURL error line 138: ' . curl_error($ch);
	$this->logError($logtxt);
}
```
or within a page. This for example is a scenario where an array should contain items, but doesn't.
```
if (count($array) == 0){
      $logtxt = 'main error line 26: ' . print_r($response,true);
      $someobject->logError($logtxt);
}
```

With that i hope that helps you improve your code by having logs for things that "should never happen" but some times do.
And they may help you debug so they indeed never happen again.

Counter-intuitive as it sounds: 
Having empty logic error logs is always the goal.

A PHP codebase should have ZERO Errors logged, maybe a few warnings and notices...but never any errors, because all PHP errors can be fixed, often quite easily, so if you have errors, you have work to do :)

There are other ways to do this, i hope this was detailed enough. Let me know if i missed anything or how i can improve it and I'll edit it.