Eccezione di registrazione nell'applicazione multilivello

0

Sto costruendo un'applicazione aziendale multilivello utilizzando Spring. Ho diversi livelli: Controller, Business e Provider. All'interno dell'applicazione ho creato un mini-framework di gestione degli errori personalizzato basato su una singola RuntimeException che ha un codice di errore per discriminare diversi tipi di errori.

I codici di errore sono enumerazioni che implementano questa interfaccia:

public interface ErrorCode {
    public int getNumber();
    public String getDeveloperMessage();
    public String getHelpURL();
    public boolean isSystemError();
}

Quindi, ad esempio, ho:

public enum SystemErrorCode implements ErrorCode{
    E_UNKNOWN_ERROR(10000, "Unkwown internal error", true),
    E_MISSING_ARGUMENT(10001, "Missing argument", true),
    E_INVALID_ARGUMENT(10002, "Invalid argument", true),
    E_CONTEXT_NO_TENANT(10003, "No tenant in context", true),
    E_CONTEXT_TENANT_CHANGE(10004, "Tenant change attempt", true),
    E_CONTEXT_ALREADY_INITIALIZED(10005, "Tenant already initialized", true),
    E_NOT_IMPLEMENTED(10005, "Feature not implemented", true),
    ...
}

La classe di eccezioni stessa assomiglia a questa (getter, costruttori e metodi di utilità sono eliminati per brevità):

public class EngineRuntimeException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    private final String incidentReportId;
    private final ErrorCode code;
    private final Map<String, Object> additionalInfo;

    ...

    public static EngineRuntimeException wrap(Throwable exception, ErrorCode errorCode) {
        if(exception instanceof EngineRuntimeException) {
            EngineRuntimeException se = (EngineRuntimeException)exception;
            if(errorCode != null && errorCode != se.getCode()) {
                return new EngineRuntimeException(exception.getMessage(), exception, errorCode);
            }
            return se;
        } else {
            return new EngineRuntimeException(exception.getMessage(), exception, errorCode);
        }
    }


    public static EngineRuntimeException wrap(Throwable exception) {
        return wrap(exception, SystemErrorCode.E_UNKNOWN_ERROR);
    }

Usando il metodo di utilità wrap posso incapsulare facilmente le eccezioni nei diversi livelli senza perdere lo stack originale e senza sporcarlo ripubblicando o incapsulando eccezioni più di una volta.

try {
  if(a == null) {
    throw new EngineRuntimeException(SystemErrorCode.E_UNKNOWN_ERROR, "blah, blah, blah");
  }

  // Some other code that can throw exceptions (checked or unchecked),
  // these exceptions will be wrapped inside EngineRuntimeException if they
  // are not already of that type.

} catch (Exception ex) {
    throw EngineRuntimeException.wrap(ex);
}    

Per ricapitolare, ogni livello ha un try-catch di limite di sicurezza che avvolge le eccezioni in EngineRuntimeException, solo rilanciando ciò che è già un EngineRuntimeException.

La domanda è: dove devo registrare l'eccezione? Voglio registrare l'eccezione solo una volta, quindi stavo pensando di fare le cose di logging all'interno del costruttore della classe EngineRuntimeException stessa. È una cattiva idea? È meglio fare il materiale di logging solo nei blocchi catch dei limiti del layer?

Inoltre non voglio semplicemente loggarmi usando un log4j ma voglio usare un servizio (@Service) iniettato da spring in modo che possa decidere di fare qualcosa di più che semplicemente loggare sul file system. Come posso farlo visto che non posso fare iniezioni di primavera in una classe che ho creato con l'istruzione "nuova".

    
posta Nicola 04.09.2014 - 16:47
fonte

2 risposte

1

Se non ti dispiace avere un'altra dipendenza dalla libreria, puoi usare AspectJ per registrare qualsiasi eccezioni che derivano da EngineRuntimeException con la seguente dichiarazione:

public before() throws EngineRuntimeException: mypointcut() {...}

Il motivo per cui questo metodo è preferibile è quello di disaccoppiare la registrazione dall'eccezione stessa.

Naturalmente potresti anche accedere direttamente in EngineRuntimeException e anche se non dovresti avere problemi, se hai bisogno di creare un'altra eccezione che non deriva da EngineRuntimeException , anche questo dovrebbe riutilizzare lo stesso log considerando che con AspectJ, si potrebbe avere una soluzione più pulita in generale con tutti "logging" insieme in un unico luogo. Puoi generalizzare AspectJ anche per altre parti del tuo programma in modo non intrusivo.

Si noti che se non si desidera che AspectJ influenzi le prestazioni, ci sono modi in cui è possibile applicare aspectJ ai jar mentre vengono creati in modo tale da non richiedere affatto AspectJ in produzione. In questo modo non avresti bisogno di usare la primavera.

    
risposta data 04.09.2014 - 17:37
fonte
0

The question is: where should I log the exception? I want to log the exception only once

Registra l'eccezione solo una volta, cioè nel punto in cui viene gestita (non retrocessa). Questo è il posto dove hai più informazioni.

so I was thinking about do the logging stuff inside the constructor of EngineRuntimeException class itself. Is that a bad idea?

Sì. Ciò porta a registrarlo più volte.

Also I don't want to simply log using a log4j but I want to use a service (@Service) injected by spring so that I can decide to do something more than just logging on file system.

Mi sembra un semplice piatto di ghiaccio. Scommetto che log4j può essere configurato per accedere a qualsiasi cosa tu voglia. Se non è abbastanza, passa a slfj che ti consente di utilizzare uno dei più comuni framework di registrazione 4+.

    
risposta data 04.09.2014 - 17:43
fonte

Leggi altre domande sui tag