Ha a WordPress rendszerünk életciklusa során arra adjuk a fejünket, hogy belenyúlunk a kódba, előfordulhat, hogy látni szeretnénk működés közben a program viselkedését, a változóink értékét, vagy, hogy egy bonyolult elágazás mely ágában köt ki a kódunk. Hova tovább hasznos lehet a konkrét hiba behatároláshoz, ha nem csak a weboldal látogatóinak, felhasználóinak szánt –kevésbé informatív, általános–üzeneteket látjuk a fejlesztéseinkből, mely általában csekély információ arra, hogy nekiinduljunk a hibakeresés rögös útvesztőjének.
Egy ’éles’ weboldalon pedig vajmi kevés esélyünk van arra, hogy kedvenc php-fejlesztő IDE-nket megnyitva töréspontokkal tűzdeljük tele kódot, és futás közben meg-megállva vizsgálódjunk hibák után kutatva.
Számtalan megbízható módszer létezik a hibák okának feltárására, kezdve a javascriptes konzolra kiíratós módszertől egészen a logokat adatbázisba mentő megoldásokon át, személyes kedvencem azonban a ’fájlba írós’ módszer. Ehhez szinte semmi másra nincs szükségünk, mint hogy a webtárhely FTP-jéhez hozzáférjünk, (akár egy FTP-kliens, akár a webes mondjuk cPanel segítségével) valamint a jó öreg fopen, fwrite és fclose függvénycsaládra. Ezt a módszert lehet központosítva is használni, a wp-content/plugins mappába helyezve a logoló osztályunkat, én azonban plugin-szinten szoktam hozzáadni a kódbázishoz, mivel így még könnyebben lehet akár minden egyes pluginhez egy külön mappát gyártani, melyben csak az őrá jellemző debuggolást találjuk. (Megj. : aki dolgozott már több tíz megás log fájlokkal, az talán értékelheti ezt a megoldást)
A kódrészlet, fő függvényünkkel tehát a következőképpen fog kinézni:
public static function _fecho( $log, $fileMode = "a" ) {
$path = "/plugin-logs/log";
$path .= date( '_Ymd-Hi' ).".txt";
$myfile = fopen( $path, $fileMode ) or die( "Unable to open file!" );
fwrite( $myfile, self::get_log_time().":".$log."\n" );
fclose( $myfile );
}
private static function get_log_time() {
$t = microtime( true );
$micro = sprintf( "%06d",( $t - floor( $t ) ) * 1000000 );
$d = new \DateTime( date( 'Y-m-d H:i:s.'.$micro, $t ) );
return $d->format( "Y-m-d H:i:s.u" );
}
Mint látjuk, mindkét függvény statikus, ezáltal nincs szükség osztály-példányosításra. A get_log_time segéd-függvény csupán arra szolgál, hogy a fájlunkban kiíratott logokhoz egy pontos időbélyeg is tartozzon.
Amint látható fő _fecho függvényünkben megadunk egy alapértelmezett helyet a mappaszerkezeten belül, ahova elmentjük majd a log fájlokat, s mely útvonal akár paraméterként is beadható, vagy módosítható függvényen belülről.
Működése egyszerű: egyetlen egy paramétert vár a függvény, a $log változóban, melyet ki fogunk íratni a fájlba, a második paraméter – mint látható opcionális – alapértelmezett értékkel rendelkezik, s ezzel a fájlhoz illesztjük a tartalmat. Egy friss időbélyeg segítségével beleírjuk a debuggolni kívánt tartalmat, majd lezárjuk. Remélhetőleg az fopen, fwrite valamint az fclose függvények mindenki számára ismeretesek. A log mappánkban pedig szépen sorakoznak majd a log fájlok.
Apró egyszerűsítés, ha írunk az osztályunkhoz pár burkoló függvényt, így egyszerű és letisztult függvényhívásokat hajthatunk végre. A szöveg-formátum megadása egyéni ízlés kérdése, de helyezzük az olvashatóságot, átláthatóságot az előtérbe.
function _log( $caller, $str ) {
_l( '[ LOG ]: '.$caller.' : '. $str );
}
function _error( $caller, $str ) {
_l( '[ ERROR ]: '.$caller.' : '. $str );
}
function _l( $str ) {
Logger::_fecho( $str );
}
A wordpress pluginünk kódjában pedig szintén egyszerű a meghívása, mindössze importálnunk kell a Logger osztályt, valamint a wrapper függvényeket.
use function Logger\_log;
use function Logger\_error;
Egy-egy log fájl tartalma pedig tetszőlegesen:
A képek önmagukért beszélnek. Végezetül a teljes kód pár tíz sorban:
<?php
namespace Logger;
define( 'PLUGIN_DEBUG', true ); // This should be turned off, to silence logs.
class Logger {
/* Save the log output to a file, with timestamp.
*
* @param $log - The string to output in the file.
* @param $fileMode - Whether to append or simply write to the file.
*/
public static function _fecho( $log, $fileMode = "a" ) {
if ( PLUGIN_DEBUG ) {
$path = "/plugin-logs/log";
$path .= date( '_Ymd-Hi' ).".txt";
$myfile = fopen( $path, $fileMode ) or die( "Unable to open file!" );
fwrite( $myfile, self::get_log_time().":".$log."\n" );
fclose( $myfile );
}
}
/* Get the time in milliseconds. */
private static function get_log_time() {
$t = microtime( true );
$micro = sprintf( "%06d",( $t - floor( $t ) ) * 1000000 );
$d = new \DateTime( date( 'Y-m-d H:i:s.'.$micro, $t ) );
return $d->format( "Y-m-d H:i:s.u" );
}
} /* ~Logger */
function _log( $caller, $str ) {
_l( '[ LOG ]: '.$caller.' : '. $str );
}
function _error( $caller, $str ) {
_l( '[ ERROR ]: '.$caller.' : '. $str );
}
function _l( $str ) {
Logger::_fecho( $str );
}
//Logger::_fecho('I\'m alive');
/* ~namespace Logger; */
Látható, hogy az osztály végén ki van kommentelve egy sor, mely kommentje feloldásával arra hivatott, hogy ellenőrizzük vele, hogy a fájl valóban betöltődött-e a PATH-ra.
Ez az egyszerű megoldás számos továbbfejlesztési lehetőséget rejteget magában. Ilyenek például:
- Logok szintjének megkülönböztetése. (Pl. log 1. szint, log 2. szint, hiba 1. szint, stb.)
Konstansok bevezetésével, melyek ki/be kapcsolhatóak (bool típus, pl. localhost-on szükséges a debug, éles rendszeren nem, valamint más log-szintek jelenjenek meg egy teszt oldalon, mint az élesen.) - Egyedi string azonosító más objektum-példányoknál (részletesebb kimenet, minden oldalbetöltés egy-egy példányosítás.)
- Kerekített időbélyegek használata. (pl. minden 5 percről készüljön csak új fájl.)
A lista folytatódhatna. Ez a kis ízelítő talán felkeltette az érdeklődést, hogy milyen irányban lehetne elindulni, ha hibát kell/akarunk keresni. Ez egy remek eszköztár a hátizsákunkban, használjuk hát bátran.