Ola,

Als er in mijn applicatie iets fout gaat, dan wil ik daar een error-level aan kunnen koppelen.

Zelf dacht ik om gebruik te gaan maken van 3 gradaties:

1) notice: een kleine fout, bijv. een user vult een getal in in plaats van een string
2) warning: een serieuze fout, bijv. een file kan niet worden opgeslagen
3) error: er gaat iets goed mis, bijv. er kan geen database-verbinding tot stand komen

Een notice zou ik dan alleen loggen in een log-bestand. Een warning zou ik zowel loggen als mailen, en bij een error zou ik loggen en mailen en de rest van de applicatie stoppen.

Zijn deze 3 gradaties toereikend volgens jullie? Of zijn er nog tussenstappen te bedenken, en zo ja welke?
>> Kun je eens schematisch een praktijkvoorbeeldje geven van wat je bedoelt?

Jij schuift het loggen steeds op. Jij logt de error niet op de plek dat het gebeurd, maar op de plek wanneer iemand hem ooit gaat opvangen (moet je nog afwachten of dat uberhaupt gebeurd). Dat betekend dat de cacher een exception kan gooien en deze nog voordat hij in een catch terecht komt eindigt in een fatal error waardoor je applicatie plat ligt en de rest niet meer wordt uitgevoerd. Resultaat? Je ziet alleen die fatal error en je logs bevatten niet de exception (hij was immers nog niet opgevangen).
Wat wij zeggen is dat je bij het gooien al moet loggen. Het loggen staat los van de exception. Wanneer er nu een fatal error plaatsvind voor het opvangen van de exception zal je wel een exception in je log file aantreffen (de exception hoeft immers niet meer opgevangen te worden om te cachen).

sorry ward...

>> Wat is bijvoorbeeld het verschil tussen een error en critical? En tussen critical en alert? Dat komt toch allemaal op hetzelfde neer?

Ik denk dat je het meer als een hierarchy moet zien. De critical is een algemene level voor problematieke errors. Wat specifieker zijn de alert en emergency (beide zijn kritiek).
Ah, op die manier. Ik geloof dat ik nu begin te begrijpen wat jullie bedoelen. Maar moet ik dan niet loggen op de plek waar ik opvang, of moet ik én én loggen?
Je logt wat er fout gaat waar het fout gaat en je logt dat je een exception opvangt waar je hem opvangt.
Eenvoudiger dan je denkt. Stel dat we een Foo hebben waaruit we Foo::$FooStuff kunnen ophalen met Foo::getFooStuff():

<?php
class Foo
{
    public function getFooStuff
    {
        $this->initFooStuff();
        $return = $this->FooStuff;
        if ($return === false) {
            throw new FooException('We are out of foo stuff.');
        }
        return $return;
    }
}
?>

Dat lijkt logisch. Bij onvoldoende $FooStuff hebben we een false en een nette exception. Daar kunnen we elders wat mee:

<?php
try {
    $foo = new Foo();
    $bar = $foo->getFooStuff();
} catch (FooException $e) {
    // Auw, een Foo-fout
    Logger::warning($e->getMessage);
}
?>

Maar dan... treedt er ineens in Foo::initFooStuff() een onverwachte fout op die het script laat crashen of het afbreekt. Je logger komt er dan niet meer aan te pas. Je had eigenlijk al in Foo moeten loggen. Het is immers ook een Foo-fout.
Er ontstaat dus een fout van PHP (dus geen eigen exception o.i.d) en daardoor stopt het script meteen op die plek, zelfs al voordat je een exception kunt gooien.
Wat heb je dan nog aan het throwen van exceptions? Ik dacht dat de rauwe PHP errors ook bij het catch() gedeelte van een try-catch block worden opgevangen, en dan automatisch het script stopt.
Harry hogeveen op 25/11/2013 18:37:52

Er ontstaat dus een fout van PHP (dus geen eigen exception o.i.d) en daardoor stopt het script meteen op die plek, zelfs al voordat je een exception kunt gooien.
Wat heb je dan nog aan het throwen van exceptions? Ik dacht dat de rauwe PHP errors ook bij het catch() gedeelte van een try-catch block worden opgevangen, en dan automatisch het script stopt.

Zelfs dat kan, door van elke PHP-error een exception te maken. Maar wat niet kan, is een rijtje A -> B -> C -> Logger uitvoeren in de hoop dat alle errors en exceptions van {A,B,C} uiteindelijk als één fout in de Logger belanden.
@Wouter: oké. Stel nu even het voorbeeld van een cacher actie.

Class A gaat iets cachen via cacher Class C. Class C gebruikt daar voor Filesystem class F.

Nu lukt het niet om het bestand op te slaan. Class F gooit een exception. Class C vangt 'em op en gooit ook een exception. Class A vangt de exception van class C op. Mijn idee was dan om om de catch van class A de exception van clas C met als previous exception de exception van class F te loggen. Als ik jou goed begrijp zeg jij nu dat je op 3 punten moet loggen, zowel in class F, class C als class A. Correct? Maar dan krijg je wel rare log-berichten:


25-11-2013 19:26:05 Filesystem: could not save file foo.php
25-11-2013 19:26:05 Cacher: could not cache file foo.php. Filesystem: Could not save file foo.php
25-11-2013 19:26:05 Class A: could not cache foo.php. Cacher: could not cache file foo.php. Filesystem: Could not save file foo.php

Is dat niet wat vreemd :-s

@Ward:

Dankjewel voor je voorbeeld. Echter wel een vraag daarover.

>> Maar dan... treedt er ineens in Foo::initFooStuff() een onverwachte fout op die het script laat crashen of het afbreekt.

Oké... maar als dat gebeurt, dan kom je ook niet meer bij de throw terecht, en zal er dus net zo goed niks gelogd worden. Toch?
>> Is dat niet wat vreemd :-s

Ja, dat is inderdaad vreemd. Weet je hoe dat komt? Dat komt doordat jij weer de exceptions logt...


[2013-11-25 19:38:25] core.ERROR: FileSystem - could not save file route-13dag84.php
[2013-11-25 19:38:25] core.WARNING: Cacher: could not cache routes
[2013-11-25 19:38:25] core.NOTICE: Catched Exception: ...
>> Ja, dat is inderdaad vreemd. Weet je hoe dat komt? Dat komt doordat jij weer de exceptions logt...

Hihi... LOL :-)))
Weten we in ieder geval waar het aan ligt ;)

Oke, dus als ik je goed begrijp:

1) log je altijd als je een exception gooit
2) log je altijd als je een exception opvangt

Correct?

Bij 2, log je dan alleen WAT er fout gaat "could not cache config"?

Op de manier die jij beschrijft, maak jij dan gebruik van previous errors? Ik dacht dat het idee van de previous errors was dat je de message van een previous error kunt toevoegen aan de huidige message, maar dat zie ik bij nou niet terug??

Maar.. nu lees ik nog even dit

>> Dat komt doordat jij weer de exceptions logt...

Je wil toch de $message loggen? Waarom zou je anders een message meegeven?
Tijd voor een voorbeeldje:
<?php

class Filesystem
{
// ...
public function write($file, $content)
{
if (problem) {
$this->logger->error(__CLASS__.' - could not save file '.$file);

throw new FilesystemException('File cannot be written: '.$the_problem);
}
}

public function read($file)
{
if (!file_exists) {
$this->logger->debug(__CLASS__.' - requested not existing file '.$file);

throw new FilesystemException('File "'.$file.'" does not exists');
}

return file_get_contents($file);
}
}

class RouteCacher extends FileCacher
{
// ...
public function set($routes)
{
try {
$data = serialize($routes);
$this->filesystem->write($this->generateNewFileName(), $data);
} catch (\Exception $e) { // geen FileSystemException, omdat serializen ook exceptions kan opleveren
$this->logger->warning('Could not cache routes ');

throw new CacheWriteException('Routes cannot be cached', 0, $e);
}
}

public function getAll()
{
try {
$data = $this->filesystem->read($this->getFileName());

return deserialize($data);
} catch (FileSystemException $e) {
$this->logger->debug('Could not read cached routes');

throw new CacheReadException('Cached routes cannot be find or loaded', 0, $e);
}
}
}

class Router
{
public function doLoad()
{
try {
$routes = $this->cacher->getAll();
} catch (CacherReadException $e) {
// cacher mislukt, blijkbaar gaat er wat mis of bestaat het cache bestand nog niet
$routes = ...; // load routes

try {
$this->cacher->set($routes);
} catch (CacheWriteException $e) {
$this->logger->notice('Catched exception: '.DebugUtil::stringifyException($e));
}
}

return $routes;
}
}
?>

Reageren