Riktig feilhåndtering: Feilkategori og -hendelse

Feilkategori vs. -hendelse

Når man feilsøker kan man skille mellom to ulike behov:

  • Feilkategori beskriver hvilken feil som har oppstått. Den kategoriserer hendelser av samme type, og brukes for å forstå hva som har skjedd.
  • Identifisering av feilhendelsen forteller hva som har skjedd til en bestemt tid i en spesifikk sammenheng. Typisk er dette nyttig for å finne ut hva som har skjedd når man feilsøker en bestemt feilsituasjon meldt av en bruker.

Feilkategorisering

Feilkategorisering har flere alternative tilnærminger:

  • Hver feilkategori representeres med en egen Exception. Java SE APIet er et eksempel på denne tilnærmingen. Dette resulterer typisk i mange Exception-klasser  definert på ulike steder i pakkestrukturen. Tilnærmingen kan derfor kalles distribuert.
  • Et fåtall sentrale Exception-klasser definerer feiltypene (gjerne Applikasjonsfeil, Systemfeil og Programmeringsfeil). For hver feiltype defineres en ID som feiltypen bærer med seg (typisk en Enum spesifisert i constructor). Denne tilnærmingen kan ses på som sentralisert.
  • Kombinasjoner av disse, for eksempel et fåtall sentrale Exception-klasser med distribuerte subklasser.
Sentralisert feilhierarki
Sentralisert feilhierarki

Uavhengig av tilnærming er hensikten å gjenkjenne feilen og forstå hvorfor den oppstår. En driftsperson vil kanskje lage en erfaringsbase for kjente feilkategorier. En utvikler som skal feilrette har også god nytte av god feilkategorisering for å finne årsaken.

Min erfaring er at applikasjoner lykkes best med en sentralisert tilnærming, der man definerer feilkategoriene som en felles Enum. Rammeverk (f.eks. Java SE API og Spring Framework) velger gjerne en distribuert tilnærming; ettersom dette ikke er applikasjoner har man ikke det samme behovet for sentral oversikt over feilkategoriene.

Enum Kategori { INGEN_PENGER, FEIL_ROLLE, UTESTENGT };
...
 
public class AbstractException() {
   public AbstractException(Kategori kategori, ...) {
      ...
   }
}

Kategorisereing av feil ved hjelp av Enum

Identifisering av feilhendelse

For å finne ut av en spesifikk feil ønsker man å identifisere den unike hendelsen som forårsaket feilen. Typisk er dette en situasjon der en bruker melder en feil eller når man feilsøker en hendelse som andre har rapportert tidligere. Å kunne identifisere denne ene feilen i en stor loggfil er gull verdt. Dette krever at hver enkelt feil blir logget med en unik ID.

En teknikk som fungerer bra nok i de fleste sammenhenger er å bruke tidspunktet feilen oppstår (i millisekunder) som ID. Denne IDen vises i feilsammenheng og skrives samtidig til feilloggen. Sannsynligheten for at to eller flere feil oppstår i samme millisekund er tilstede, men denne teknikken vil uansett begrense utvalget til et minimum. Funksjonaliteten for å lagre IDen implementeres i Exception-superklassen.

public class AbstractException() {
   private final long uid;
   public AbstractException(...) {
      uid = System.currentTimeMillis(); // skrives til loggen
   }
}

Eksempel på identifisering av Exception-instans med timestamp.

Oppsummering

God feilhåndtering forutsetter riktig kategorisering av feil i tillegg til støtte for å identifisere hver enkelt feilhendelse. Som alltid krever dette at man har et bevisst og målrettet forhold til feilhåndtering gjennom hele prosjektløpet. Det er fullt mulig å unngå at feilkategorisering og idendifisering av feilhendelse blir klattet på mot slutten av et prosjekt; å involvere driftpersoner som kravstillere på lik linje med annen funksjonalitet er en god start. God kontekstinformasjon i en feilsituasjon er også viktig, dette blir et eget tema i denne serien.

4 Comments

  1. Posted 18/11/2009 at 15:51 | Permalink

    Supert innlegg Trond Arve!

    Jeg er enig at en sentralisert tilnærming kan være vedre enn en distribuert tilnærming, men jeg synes det er noen untak(exceptions;). Det kan være lurt å trekke de frem i lyset slik at man ikke bare velger en sentralisert løsning, fordi man tror det er en “best practise”… Hva man bør velge kommer (selvfølgelig) ann på hva man skal lage og hvilke behov man har.

    En distribuert tilnærming er bedre hvis man faktisk skal gjøre (fornuftige) valg bassert på forskjellig feil man kan få. Støtten er bedre bygget for dette i Java med try catch selvom det kan gjøres greit med switch på kategori Enum’en. (Mange av de som bruker den distribuerte tilnærmingen hadde ikke enum i språket når de startet.) Jeg mener det kan det være lurt å bruke en distribuert tilnærming når man har:
    * Veldig mange forskjellige feiltyper. Disse kan man gruppere i forskjellig exception slik at man får en mellomting mellom distribuert og sentralisert løsning. På den måten så kan det bli enklere å håndtere dette kodemessig.
    *Undertyper at en feil. Enums kan ikke arve, men det kan exception klasser.
    *Forskjellig alvorlighetsgrad av feil. På den måten kan man feks. velge å håntere enkelte feil eller sende de videre uten å måtte kaste de på nytt og det blir tydelig hva man kaster videre og hva man håndterer selv.

    En stor fordel med å benytte en stralisert tilnærming ligger jo i at det blir mindre exceptions å lage ihvertfall i Java. En fil per exception kan fort bli mange filer. På den andre siden så skallerer det bedre med en distribuert løsning siden man ikke trenger å uroe seg over om det blir veldig mange feiltyper som skal ha enum etterhvert. Jeg har ikke helt grunnlag for å si det, men jeg har på følelsen av at en distribuert løsning med mange untak som er lett å håndtere er bedre når man skal lage veldig robust kode som kan håndtere mange forskjellige problemer i et større system med mange lag over hverandre.

  2. Trond Arve Wasskog
    Posted 18/11/2009 at 16:06 | Permalink

    Takk for bra innspill! Er enig, viktig å understreke at det ikke finnes en fasitløsning her heller :) Jeg tror grunnen til at applikasjoner har større sannsynlighet for å lykkes med sentralisert tilnærming er at den er veldig tydelig, enkel å forstå, kommunisere og håndheve. Har sett at den distribuerte tilnærmingen større grad fører til at feilhåndteringen blir tilfeldig og inkonsistent. Imidlertid er det fullt mulig å få til riktig feilhåndtering for en applikasjon med en distribuert tilnærming, det krever disiplin og godt håndtverk.

  3. Posted 29/11/2009 at 23:06 | Permalink

    … … … … … … … … … … … … … …

  4. Lena Seternes
    Posted 10/02/2010 at 07:32 | Permalink

    I JavaZone foredraget sier du (som Joshua Bloch i Effective Java) at man bør bruke eksisterende (unchecked) exceptions (f.eks. IllegalArgumentException). Hvordan tenker du dette brukt sammen med dine sentraliserte exceptions (ApplicationException og SystemException)? Skal de wrappes inn i de sentraliserte, men da er kanskje meningen borte?

One Trackback

  1. [...] feilhåndtering forutsetter fornuftig kategorisering og identifisering av feilhendelse. Feilkategori og unik feil-ID er en viktig del av [...]

Post a Comment

Your email is never shared. Required fields are marked *

*
*

Spam Protection by WP-SpamFree