PHP: Logging von SQL Queries / PDO Prepared Statements
PHP Januar 17th, 2015Das Debugging von (legacy) PHP Anwendungen kann ganz schoen nervig sein. Da hilft auch der ganze Werkzeugkasten nicht, den man aus anderen Umgebungen gewohnt ist.
Percona Toolkit pt-query-digest auf tcpdump hat mal direkt Probleme bei der Auswertung.
Instrumentierung / AOP von PHP „Anwendungen“ – eher mau. Das einzige was einigermaßen tut, ist ‚runkit‘. Jedoch kann das nicht alles, was man gerne möchte. Insbesondere kann man sich damit nicht an die Konstruktoren von PHP internen Klassen (PDO) anhängen. Damit war der Plan vollständig ohne PHP Code Änderungen auszukommen erst mal dahin.
Aber erst mal die guten Nachrichten: Verwendet man runkit, kann man von mysql_query bzw. mysqli_query schonmal alles mitlesen.
PHP runkit ist schnell aus dem git repo (PECL ist veraltet) installiert:
cd /tmp
git clone git://github.com/zenovich/runkit.git
cd runkit
pecl install package.xml
echo 'extension=runkit.so' >> /etc/php5/fpm/conf.d/20-runkit.ini
echo 'runkit.internal_override = true' >> /etc/php5/fpm/conf.d/20-runkit.ini
echo 'opcache.enable = 0' >> /etc/php5/fpm/conf.d/20-runkit.ini
php5-fpm restart
Danach hängt man ein bisschen Magie in den PHP code und alle mysqli_query Aufrufe werden abgefangen:
if (function_exists("runkit_function_redefine"))
{
//clean up from cached versions
if (function_exists('__mysqli_query'))
{
runkit_function_remove('__mysqli_query');
}
$myqsliDelegate = 'file_put_contents("/tmp/queries.txt", $sql. ";\n", FILE_APPEND); return __mysqli_query($db, $sql);';
runkit_function_rename('mysqli_query', '__mysqli_query');
runkit_function_add('mysqli_query', '$db, $sql', $myqsliDelegate);
}
Jetzt kommt noch PDO, auch hier hätte man ja gerne die Prepared-Statements. Hier ist die Besonderheit dass erst die Datenbank die prepared sql statements mit Parametern belebt.
Dazu kann man sich dieser Klasse bedienen: https://github.com/noahheck/E_PDOStatement – die Interpolierung wird dann simuliert.
In meinen Fällen reichte das voll und ganz.
So sieht dann der runkit code für das Logging der PDO Statements mit der Hilfsklasse E_PDOStatement aus, die mir im Property fullQuery
die interpolierte Query bereitstellt:
$pdoDelegate = '$res = $this->__execute(); $query = $this->fullQuery; file_put_contents("/tmp/queries.txt", $query. "; --\n", FILE_APPEND); return $res;';
runkit_method_rename("E_PDOStatement", "execute", '__execute');
runkit_method_add("E_PDOStatement", "execute", '', $pdoDelegate);
Leider muss jetzt noch an den Stellen, an denen das PDO instantiiert wird das E_PDOStatement konfiguriert werden. Normalerweise ist die Anzahl aber überschaubar. Damit nur auf Entwicklerrechnern das Logging aktiviert wird, habe ich noch einen environemnt Check dazu gepackt:
if ((getenv("environment") === "LOCAL"))
{
require_once("lib/E_PDOStatement.php");
$con->setAttribute(PDO::ATTR_STATEMENT_CLASS, array("E_PDOStatement", array($con)));
}
Januar 23rd, 2015 at 21:04
Diese Klassen sind ausgezeichnet! Ich stimme zu, diese klassen reichen mir auch voll und ganz!