Exceções e Loggers

Melhores Práticas

Quantos tipos de Exceções existem?

Checked
 

Unchecked
 

Exception

RuntimeException

Error

(checked)

(unchecked)

(unchecked)

Como usá-las?

Como tratá-las?

Lidando com exceções

public class ConnectionFactory {
    public Connection create() throws SQLException {
        return DriverManager.getConnection(
                   "jdbc:mysql://localhost:3306/bd"
                   "usuario", "senha");
    }
}

public class FooDao {
    public List<Foo> getAll() {
        Connection conn = ConnectionFactory.create();

        // código para carregar a lista de foo
        // usando conn
    }
}

que que precisamos fazer?

O mais fácil...

public class FooDao {
    public List<Foo> getAll() throws SQLException {
        Connection conn = ConnectionFactory.create();

        // código para carregar a lista de foo
        // usando conn
    }
}
public class FooController {
    private final FooService service;
    private final Canvas canvas;
    
    public void showFoo() {
        try {
            List<Foo> foos = service.getAll();
            canvas.show(foos);
        } catch (SQLException e) {
            canvas.showText("Deu problema conectando com o banco!");
        }
    }
}

public class FooService {
    private final FooDao dao;

    public List<Foo> getAll() throw SQLException {
    	List<Foo> foos = dao.getAll();
    	System.out.println("Encontrou total de " + foos.size() + " foos");
        return foos;
    }
}

public class FooDao {
    public List<Foo> getAll() throw SQLException {
        Connection conn = ConnectionFactory.create();

        // código para carregar a lista de foo
        // usando conn
    }
}

Checked Exception:

  • Deixa claro os erros
  • Obriga lidar com o erro
  • Propaga até o infinito!
  • Imcompatível com linguagem funcional

Não passamos a Checked Exception!

public class FooController {
    private final FooService service;
    private final Canvas canvas;
    
    public void showFoo() {
        List<Foo> foos = service.getAll();
        canvas.show(foos);
    }
}

public class FooService {
    private final FooDao dao;

    public List<Foo> getAll() {
    	List<Foo> foos = dao.getAll();
    	System.out.println("Encontrou total de " + foos.size() + " foos");
        return foos;
    }
}

public class FooDao {
    public List<Foo> getAll() {
        try {
            Connection conn = ConnectionFactory.create();
    
            // código para carregar a lista de foo
        } catch (SQLException e) {
            return null;
        }
    }
}

NullPointerException

public class FooController {
    private final FooService service;
    private final Canvas canvas;
    
    public void showFoo() {
        List<Foo> foos = service.getAll();
        canvas.show(foos);
    }
}

public class FooService {
    private final FooDao dao;

    public List<Foo> getAll() {
    	List<Foo> foos = dao.getAll();
    	System.out.println("Encontrou total de " + foos.size() + " foos");
        return foos;
    }
}

public class FooDao {
    public List<Foo> getAll() {
        try {
            Connection conn = ConnectionFactory.create();
    
            // código para carregar a lista de foo
        } catch (SQLException e) {
            return new ArrayList<>();
        }
    }
}

Mas e se nunca tá conectando no banco?

NUNCA ENGULA UMA EXCEÇÃO!

que fazemos então?

Problemas

  • Não temos como nos recuperar
  • Escondemos o problemas
  • Não queremos jogar uma Checked Exception

como resolvemos?

public class DaoException extends RuntimeException {
    // constructors
}

public class FooDao {
    public List<Foo> getAll() {
        try {
            Connection conn = ConnectionFactory.create();
    
            // código para carregar a lista de foo
        } catch (SQLException e) {
            throw new DaoException();
        }
    }
}

SEMPRE ADICIONE INFORMAÇÃO DO CONTEXTO!

public class FooDao {
    public List<Foo> getAll() {
        try {
            Connection conn = ConnectionFactory.create();
    
            // código para carregar a lista de foo
        } catch (SQLException e) {
            throw new DaoException("Não foi possível pegar todos os Foos do banco!", e);
        }
    }
}

Capture exceção quando

  • Quer adicionar contexto a ela
public Foo findById(long id) {
    try {
        // logica
    } catch (SQLException e) {
        throw new DaoException("Problema ao pegar do banco Foo com id " + id, e);
    }
}

Faça Wrap correto da Exceção

} catch (SQLException e) {
    throw new DaoException("Problema no banco: " + e.getMessage());
}
} catch (SQLException e) {
    throw new DaoException("Problema no banco: ", e);
}

Errado

Certo

public class FooController {
    private final FooService service;
    private final Canvas canvas;
    
    public void showFoo() {
        List<Foo> foos = service.getAll();
        canvas.show(foos);
    }
}

public class FooService {
    private final FooDao dao;

    public List<Foo> getAll() {
    	List<Foo> foos = dao.getAll();
    	System.out.println("Encontrou total de " + foos.size() + " foos");
        return foos;
    }
}

public class FooDao {
    public List<Foo> getAll() {
        try {
            Connection conn = ConnectionFactory.create();
    
            // código para carregar a lista de foo
        } catch (SQLException e) {
            throw new DaoException("Não foi possível pegar todos os Foos do banco!", e);
        }
    }
}

Não temos como lidar com a exceção

Último ponto antes de chegar no usuário

public class FooController {
    private final FooService service;
    private final Canvas canvas;
    
    public void showFoo() {
        try {
            List<Foo> foos = service.getAll();
            canvas.show(foos);
        } catch (Throwable e) {
            canvas.showText("Deu problema conectando com o banco!");
        }
    }
}

public class FooService {
    // não podemos fazer nada sobre a exceção aqui!
}

public class FooDao {
    public List<Foo> getAll() {
        try {
            Connection conn = ConnectionFactory.create();
    
            // código para carregar a lista de foo
        } catch (SQLException e) {
            throw new DaoException("Não foi possível pegar todos os Foos do banco!", e);
        }
    }
}

Exception

RuntimeException

Throwable

Error

StackOverflow

OutOfMemory

public class FooController {
    private final FooService service;
    private final Canvas canvas;
    
    public void showFoo() {
        try {
            List<Foo> foos = service.getAll();
            canvas.show(foos);
        } catch (Throwable e) {
            canvas.showText("Deu problema conectando com o banco!");
        }
    }
}

public class FooService {
    // não podemos fazer nada sobre a exceção aqui!
}

public class DaoException extends RuntimeException {
    // constructors
}

public class FooDao {
    public List<Foo> getAll() {
        try {
            Connection conn = ConnectionFactory.create();
    
            // código para carregar a lista de foo
        } catch (SQLException e) {
            throw new DaoException("Não foi possível pegar todos os Foos do banco!", e);
        }
    }
}

NUNCA DÊ CATCH
 EM THROWABLE!

public class FooController {
    private final FooService service;
    private final Canvas canvas;
    
    public void showFoo() {
        try {
            List<Foo> foos = service.getAll();
            canvas.show(foos);
        } catch (Exception e) {
            canvas.showText("Deu problema conectando com o banco!");
        }
    }
}

public class FooService {
    // não podemos fazer nada sobre a exceção aqui!
}

public class DaoException extends RuntimeException {
    // constructors
}

public class FooDao {
    public List<Foo> getAll() {
        try {
            Connection conn = ConnectionFactory.create();
    
            // código para carregar a lista de foo
        } catch (SQLException e) {
            throw new DaoException("Não foi possível pegar todos os Foos do banco!", e);
        }
    }
}

Só existe exception de banco?

SEJA ESPECÍFICO!

public class FooController {
    private final FooService service;
    private final Canvas canvas;
    
    public void showFoo() {
        try {
            List<Foo> foos = service.getAll();
            canvas.show(foos);
        } catch (DaoException e) {
            canvas.showText("Deu problema conectando com o banco!");
        }
    }
}

public class FooService {
    // não podemos fazer nada sobre a exceção aqui!
}

public class DaoException extends RuntimeException {
    // constructors
}

public class FooDao {
    public List<Foo> getAll() {
        try {
            Connection conn = ConnectionFactory.create();
    
            // código para carregar a lista de foo
        } catch (SQLException e) {
            throw new DaoException("Não foi possível pegar todos os Foos do banco!", e);
        }
    }
}

throw early

 

catch later

JOGUE EXCEÇÃO DA ONDE PROBLEMA OCORREU

RECUPERE ONDE PODES RESOLVER O PROBLEMA

Dos and Don'ts

Nunca jogue Exception e
 use Log

} catch (SQLException e) {
    LOGGER.error("Problema no banco!", e);
    throw new DaoException(e);
}

Faça um ou o outro

Use bloco finally para garantir fechamento de recursos

public void fazAlgoCom(String arquivo) {
    FileReader reader = null;
    try {
        reader = new FileReader(arquivo);
        // do stuff
    } finally {
        if (reader != null) {
            try { 
                reader.close();
            } catch (IOException e) {}
        }
    }
}

Nunca jogue Exceção de um bloco finally

public void fazAlgoCom(String arquivo) {
    FileReader reader = null;
    try {
        reader = new FileReader(arquivo);
        // do stuff
    } finally {
        if (reader != null) {
            reader.close();
        }
    }
}

Exceção original ficará perdida

Resumito para lidar com Exceções

SÓ CAPTURE E JOGUE EXCEÇÃO...

Quer adicionar mais informação a exceção

Quer esconder implementação

} catch (HibernateException e) {
    throw new DaoException("Nao foi possivel recuperar X do banco", e);
}
public void addUserFromFacebook(FbUser fbUser) {
    User user = converter.convert(fbUser);
    try {
        dao.add(user);
    } catch (DaoException e) {
        throw new InvalidUserException("Não foi possível adicionar usuário " + user
            + " convertido do usuario do facebook " + fb, e);
    }
}

CAPTURE E LIDE COM A EXCEÇÃO

Quando podes fazer algo sobre o problema

public void fazAlgoCom(String arquivo) {
    FileReader reader = null;
    try {
        reader = new FileReader(arquivo);
        // do stuff
    } finally {
        if (reader != null) {
            try { 
                reader.close();
            } catch (IOException e) {}
        }
    }
}

use try/finally quando quer que algo seja feito mesmo que dê exceção

Mas e loggers?

Usando Logger

private static final Logger LOGGER = LoggerFactory.getLogger(X.class);

public void hello(String name) {
    LOGGER.info("Hello {}", name);    
}

nivel

param

valor

...
LOGGER.info("Store {} with id {} and shipping type {} just disappeared in thin air! O:",
    store.getName(), store.getId(), store.getShipping().getType());
...

E se store for null?

LOGGER NÃO PODE SER PONTO DE FALHA

public class Store {
    ...
    @Override
    public String toString() {
        return Objects.toStringHelper(this)
            .add("name", this.name)
            .add("id", this.id)
            .add("shipping", this.shipping)
            .toString();
    }
    ...
}
...
LOGGER.info("Store {} just disappeared in thin air! O:", store);
...

DEIXE O OBJETO SABER COMO SE IMPRIMIR!

public class UserService {
    public User findUser(Long userId) {
        try {
           return userDao.findById(userId);
        } catch (DaoException e) {
            LOGGER.error(e.getMessage(), e);
        }
    }
}

ALÉM DA LINHA, QUE INFORMAÇÃO TEMOS DO QUE ACONTECEU?

ADICIONA CONTEXTO AO LOGGER!

public class UserService {
    public Optional<User> findUser(Long userId) {
        try {
           return userDao.findById(userId);
        } catch (DaoException e) {
            LOGGER.error("Houve problema ao tentar recuperar usuário com id {}",
                userId, e);
        }
    }
}

2 parametros

1 argumento

se não adicionar argumento para um throwable, será impresso sua stacktrace!

e os níveis?

void update(User user) {
    try {
        userDao.update(user);
    } catch (DatabaseException e) {
        LOGGER.error("Could not update user {}", user, e);
    }
}

INDICA FALHA!

ERROR

public void executeSearch(SearchOption searchOption, String query) {
    switch (searchOption) {
         case USER:
             searchUser(query);
             break;
         default:
              LOGGER.warn("Received unexpected search option! Option got: {}",
                searchOption);
    }
}

INDICA POSSÍVEL FALHA!

WARNING

...
   List<Threshold> thresholdsExceeded = thresholds.getExceeded(metricSample);
 
   for (Threshold thresholdExceeded : thresholdsExceeded) {
       LOGGER.info("Metric Sample {} exceeded threshold {}",
            metricSample, thresholdExceeded);
       ...
   }
...

MUDANÇAS IMPORTANTES

INFO

...
   LOGGER.DEBUG("Testing metric {} with thresholds {}", metricSample, thresholds);
   List<Threshold> thresholdsExceeded = thresholds.getExceeded(metricSample);
 
   for (Threshold thresholdExceeded : thresholdsExceeded) {
       LOGGER.info("Metric Sample {} exceeded threshold {}",
            metricSample, thresholdExceeded);
       ...
   }
...

DEPURAÇÃO (DUH)

DEBUG

public PowerRanger choosePowerRangerToFight(Monster monster)
        throws PowerRangerAvailabilityException {
    LOGGER.trace("choosePowerRangerToFight(monster={})", monster);
 
    for (PowerRanger ranger : alpha.getAvailablePowerRangers()) {
        if (ranger.hasFightingPowerFor(monster)) {
            LOGGER.trace("choosePowerRangerToFight(monster={}): {}",
                monster, ranger);
 
            return ranger;
        }
    }
    throw new OnlyWeakPowerRangerAvailableException(monster);
}

DEPURAÇÃO (DUH)

+ detalhada

TRACE

DEVE APARECER SOMENTE EM DESENVOLVIMENTO!

Obrigado :)

exceções e loggers

By Caesar Ralf Franz Hoppen