I need to open a log file for writing. Trouble is, many things may do this at the same time, and I don't want conflicts. Each write will be a single line, generally about 150 bytes (and always less than 1K), and getting things in chronological order is not strictly required.
I think what I want is to attempt to flock()
, and if it fails, keep trying for a few seconds. If a lock can't be established after a number of tries, then give up.
$fh=fopen($logfile, "a");
if (flock($fh, LOCK_EX|LOCK_NB)) {
$locked=TRUE;
} else {
$locked=FALSE;
// Retry lock every 0.1 seconds for 3 seconds...
$x=0; while($x++ < 30) {
usleep(100000);
if (flock($fh, LOCK_EX|LOCK_NB)) {
$locked=TRUE;
break;
}
}
}
if ($locked) {
if (fwrite($fh, strftime("[%Y-%m-%d %T] ") . $logdata . "\n")) {
print "Success.\n";
} else {
print "Fail.\n";
}
flock($fh, LOCK_UN)
} else {
print "Lock failed.\n";
}
I have two questions, one general and one specific. First, aside from implementing the same solution in different ways (do...while
, etc), is there a better general strategy for handling this kind of problem, that runs solely in PHP? Second, is there a better way of implementing this in PHP? (Yes, I separated these because I'm really interested in the strategy part.)
One alternative I've considered is to use syslog(), but the PHP code may need to run on platforms where system-level administration (i.e. adding things to /etc/syslog.conf) may not be available as an option.
UPDATE: added |LOCK_NB
to the code above, per randy's suggestion.
less
,tail -f
andawk
. – Moskowitzfile_put_contents('/path/to/log', $content, FILE_APPEND|LOCK_EX)
would suffice – Bufordbugfile_put_contents
will slow you down since it has to open the file, acquire the lock, write the data and close the file. See this comment. If you need speed, then using a message queue is the way to go as it's non-blocking (fire and forget). Otherwise, either scenario should work. – Bufordbugfile_put_contents
takes care of the fopen/flock/fwrite/fclose, what else do you suppose it's doing that would account for the extra overhead? I don't really need speed (as I'm willing to wait 3 seconds for a lock to be set), but I'd rather not corrupt or lose log entries. – MoskowitzLOCK_NB
. It sounds as if while in an ideal world,flock()
should work. If the risks are just usage gotchas, I'm okay with that. I'll explore MQ, but my first thought was that I don't like the idea of the client not knowing whether its logging attempt was successful. – MoskowitzLOCK_NB
andusleep
would negate each other. i.e.file_get_contents
will block by default which means it will block other calls in the stack until it can obtain an exclusive lock on the file for writing. Iffile_get_contents
returnsFALSE
after that, it most likely won't be because it couldn't obtain the lock... if that makes sense? This is all speculation from reading the docs. I don't have any examples or definitive proof that it would behave like this in the Real World – Bufordbugfile_get_contents
doesn't support LOCK_NB, so it seems it would block indefinitely. Which is makes timing out hard. It would returnFALSE
if it couldn't open the file for write. So ... I prefer the manual fopen/flock/fwrite/fclose combo overfile_put_contents
because it provides me with more granular control over the process. I totally agree that a daemon would be the way to go (even syslog), but I can't guarantee access to one. – Moskowitzif()
line. – Moskowitz