Blog do Urubatan
msgbartop
Desenvolvedor, Palestrante, Escritor, Nerd Assumido e Pai do Marcus :D
msgbarbottom

11 May 09 Mensagens de erro são feias mas não mentem (nem mordem)

Já sou desenvolvedor a algum tempo (comecei em 1997, façam as contas se quiserem :D ), e uma das coisas mais importantes que aprendi até hoje é com certeza que todas as mensagens de erro geradas por linguagens de programação, frameworks, e assemelhados, são realmente feias.
Os Stack Traces do Java são realmente muito feios, chegam a assustar quem esta começando, os do Ruby não são muito melhores.
Em C++ não tem stack traces, mas os memory dumps fazem um papel parecido, e memory dumps podem ser conseguidos a partir de qualquer linguagem compilada.
O C# tem stack traces também, bem próximos do Java, acredito que isto seja parte do .NET e não uma particularidade do C#, mas eu conheço muito pouco de .Net, então agradeço se alguem puder confirmar isto.

Outra coisa bastante importante, e uma verdade absoluta, regra inquebrável, e como tal, tem pouquíssimas exceções, é que o código que você vai escrever não vai funcionar de primeira, você não é perfeito, e você vai cometer erros.
Pode acontecer de uma ou duas vezes durante a sua vida, você conseguir testar alguma coisa e esta coisa funcionar de primeira, mas eu não faria com que a minha felicidade dependesse disto, por que esta é uma situação bastante incomum.

Beleza, e o que uma coisa tem a ver com a outra?
Se você vai cometer erros, você vai precisar descobrir o que você fez de errado, e muitas das vezes, isto não vai ser fácil, e o seu melhor amigo para esta situação, a melhor ajuda que você vai conseguir, não vai ser do seu colega do lado, por mais Nerd que ele seja, vai ser a mensagem de erro/stack trace/memory dump que vai salvar a sua pele nesta situação.
Se o seu colega Nerd for te ajudar, provavelmente, ele vai perguntar: “Qual foi o erro?”
E se preste atenção nesta listinha de respostas:

  • Não sei.
  • Ah, apareceu um stack trace aqui, mas eu já apaguei.
  • Foi um erro estranho, acontece de vez em quando, mas nunca prestei atenção na mensagem.
  • Não lembro se foi null pointer ou out of memory, mas tu pode me ajudar aqui?
  • Ahh, vem aqui ver, eu não sei ler esta mensagem.

Estas são resposta inválidas, e provavelmente vão fazer o seu colega, que poderia te ajudar, ficar bastante chateado, e te ajudar com má vontade.
Para resolver este problema, você precisa aprender a ler estas mensagens de erro, isto vai te poupar muito tempo, e tudo o que poupa tempo, acaba te tornando mais produtivo, se tu for mais produtivo, o teu chefe vai gostar mais de ti, e tu vai ganhar mais, se tu for mais produtivo, tu vai terminar o que tem que fazer mais rápido, e por conseqüência, vai pra casa mais cedo :D

A leitura de mensagens de erro, seja qual for a encarnação, requer quatro coisas:

  1. Conhecimento básico do código da aplicação, ou conhecimento profundo da linguagem/framework utilizado (o primeiro é mais fácil de conseguir, mas não tenho como ajudar muito)
  2. Um pouquinho de técnica – nisto eu posso dar uma ajudinha, inclusive é esta a idéia deste post
  3. Conhecimento no mínimo básico de inglês – posso fornecer alguns links para ajudar, mas se você realmente quer continuar trabalhando com desenvolvimento de sistemas, você precisa aprender, no mínimo a lêr em inglês.
  4. Que o stack seja impresso na tela, em arquivo, mostrado em um dialog, ou qualquer coisa que facilite a visualização, sugiro logar todos os stack traces

Técnica básica para leitura de stack traces no Java

Algumas vezes, apenas ler a mensagem ja resolve o problema, como neste exemplo que peguei por ai na web:

INFO 13:37:20 [org.hibernate.connection.ConnectionProviderFactory] - Initializing connection provider: org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider
WARN 13:37:41 [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 17002, SQLState: null
ERROR 13:37:41 [org.hibernate.util.JDBCExceptionReporter] - Io exception: The Network Adapter could not establish the connection
WARN 13:37:41 [org.hibernate.cfg.SettingsFactory] - Could not obtain connection metadata
java.sql.SQLException: Io exception: The Network Adapter could not establish the connection
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:125)
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:162)
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:274)
 at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:328)
 at oracle.jdbc.driver.PhysicalConnection.(PhysicalConnection.java:361)
 at oracle.jdbc.driver.T4CConnection.(T4CConnection.java:151)
 at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:32)
 at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:595)
 at java.sql.DriverManager.getConnection(DriverManager.java:525)
 at java.sql.DriverManager.getConnection(DriverManager.java:140)
 at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnectionFromDriverManager(DriverManagerDataSource.java:291)
 at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnectionFromDriverManager(DriverManagerDataSource.java:277)
 at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnectionFromDriverManager(DriverManagerDataSource.java:259)
 at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnection(DriverManagerDataSource.java:241)
 at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.getConnection(LocalDataSourceConnectionProvider.java:80)
 at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:72)
 at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:1859)
 at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1152)
 at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:800)
 at org.springframework.orm.hibernate3.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:726)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1059)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:363)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:226)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:147)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:269)
 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:320)
 at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:87)
 at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:72)
 at org.springframework.test.AbstractSpringContextTests.loadContextLocations(AbstractSpringContextTests.java:121)
 at org.springframework.test.AbstractDependencyInjectionSpringContextTests.loadContextLocations(AbstractDependencyInjectionSpringContextTests.java:210)
 at org.springframework.test.AbstractSpringContextTests.getContext(AbstractSpringContextTests.java:101)
 at org.springframework.test.AbstractDependencyInjectionSpringContextTests.setUp(AbstractDependencyInjectionSpringContextTests.java:178)
 at junit.framework.TestCase.runBare(TestCase.java:125)
 at junit.framework.TestResult$1.protect(TestResult.java:106)
 at junit.framework.TestResult.runProtected(TestResult.java:124)
 at junit.framework.TestResult.run(TestResult.java:109)
 at junit.framework.TestCase.run(TestCase.java:118)
 at junit.framework.TestSuite.runTest(TestSuite.java:208)
 at junit.framework.TestSuite.run(TestSuite.java:203)
 at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)

Como pode ser visto na linha 5, os stack traces no Java, começam sempre pelo nome completo da classe da última exceção gerada, seguida imediatamente pela mensagem de erro, separadas por “:”.
No caso deste exemplo, precisamos apenas acreditar que não foi possível conectar no banco de dados, ocorreu algum problema de rede.
Isto nos leva a um ponto que você vai descobrir sozinho quando trabalhar com oracle por um tempo, eles não ajudam muito a descobrir qual o problema :D
Acredito que este erro tenha ocorrido por problemas de configuração da conexão com o banco de dados ou então problemas com o banco de dados real …
Mas este stack esta aqui só pra eu poder reclamar um pouquinho da Oracle :D
Não serve como um exemplo do que eu quero mostrar para vocês (que tiveram paciência de ler até aqui);
Vejam este outro stack que eu gerei de propósito como exemplo:

Exception in thread "main" java.lang.NullPointerException
	at java.io.File.(File.java:222)
	at utils.urubatan.StackTraceReadingExample.readConfigurationFromFileName(StackTraceReadingExample.java:29)
	at utils.urubatan.StackTraceReadingExample.readConfiguration(StackTraceReadingExample.java:25)
	at utils.urubatan.StackTraceReadingExample.verifyConfiguration(StackTraceReadingExample.java:21)
	at utils.urubatan.StackTraceReadingExample.connectAndExecuteQuery(StackTraceReadingExample.java:17)
	at utils.urubatan.StackTraceReadingExample.main(StackTraceReadingExample.java:11)

Na linha 1, já temos um erro bastante comum, e se você ler isto, olhar para o seu colega do lado, e reclamar que o seu codigo gera um NullPointerException sem dizer o que esta acontecendo, por favor, desista de programar agora, antes que você fique realmente frustrado, ou se for muito insistente, coloque o seu amigo na cadeira de um psicólogo achando que trabalha com retardados :D
Este stack é até bem fácil, e serve para demonstrar o que eu quero …
Para ler um Stack trace, comece a ler de traz para frente, ou seja, leia normalmente de cima para baixo, e pare de ler na primeira linha em que o nome da classe pertencer ao seu projeto.
Neste caso, isto ocorre na linha 3 do stack trace: utils.urubatan.StackTraceReadingExample.readConfigurationFromFileName(StackTraceReadingExample.java:29)
Onde podemos ver que o erro esta sendo gerado no método “readConfigurationFromFileName”, da classe “StackTraceReadingExample”, na linha 29 do arquivo “StackTraceReadingExample.java”, ou seja, para corrigir o problema vamos para esta linha ver o que acontece lá, segue o código do exemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package utils.urubatan;
 
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
 
public class StackTraceReadingExample {
	public static void main(String[] args) throws IOException {
		StackTraceReadingExample ex = new StackTraceReadingExample();
		ex.connectAndExecuteQuery();
	}
	private String fileName;
	private Properties configuration;
 
	private void connectAndExecuteQuery() throws IOException {
		verifyConfiguration();
	}
 
	private void verifyConfiguration() throws IOException {
		readConfiguration();
	}
 
	private void readConfiguration() throws IOException {
		readConfigurationFromFileName(fileName);
	}
 
	private void readConfigurationFromFileName(String theFileName) throws IOException {
		FileReader fr = new FileReader(new File(theFileName));
		createEmptyConfigurationIfNeeded();
		configuration.load(fr);
		fr.close();
	}
 
	private void createEmptyConfigurationIfNeeded() {
		if (configuration == null) {
			configuration = new Properties();
		}
	}
}

Na linha informada, a única variável que esta sendo utilizada é o nome do arquivo, que se formos ler o código, realmente nunca foi inicializado, para resolver este problema, basta que alteremos a linha 13 para inicializar a variável para algum nome de arquivo, vou adicionar: = “teste.config” e vamos ver o que acontece.

Depois desta alteração continuamos com um erro, e este stack continua bastante simples, mas é um pouco mais complicado que o anterior:

Exception in thread "main" java.io.FileNotFoundException: teste.config (The system cannot find the file specified)
	at java.io.FileInputStream.open(Native Method)
	at java.io.FileInputStream.(FileInputStream.java:106)
	at java.io.FileReader.(FileReader.java:55)
	at utils.urubatan.StackTraceReadingExample.readConfigurationFromFileName(StackTraceReadingExample.java:29)
	at utils.urubatan.StackTraceReadingExample.readConfiguration(StackTraceReadingExample.java:25)
	at utils.urubatan.StackTraceReadingExample.verifyConfiguration(StackTraceReadingExample.java:21)
	at utils.urubatan.StackTraceReadingExample.connectAndExecuteQuery(StackTraceReadingExample.java:17)
	at utils.urubatan.StackTraceReadingExample.main(StackTraceReadingExample.java:11)

Agora o erro esta na 5a linha do stack, que é a primeira linha com código fonte da aplicação …
Esta é a técnica básica para ler stack traces:

  1. Leia a mensagem de erro na primeira linha, as vezes ela pode te ajudar, se a mensagem não ajudar, você pode anotar o nome da classe do erro, que algumas vezes também já é informação o suficiente
  2. Logo em sequida, leia o stack de traz para frente (que é a mesma coisa que de cima para baixo no Java) e procure a primeira linha de código da tua aplicação, ou do framework que você esta utilizando, dependendo do caso e do erro que esta acontecendo, assim você acabou de localizar a linha de código que esta gerando o erro
  3. Se isto ainda não for o suficiente (muitas vezes não é), seguindo pelo Stack, mais para baixo, você pode literalmente voltar no tempo e saber quem chamou aquele método até o início dos tempos
  4. Se apenas ler o código não resolver, utilize a informação obtida no passo anterior, para saber onde colocar o “break point” e utilize o debugger da sua IDE para encontrar o problema, ou pelo menos a raiz do problema

Com estes passos, os stack traces vão te ajudar bastante, algumas IDEs como o Eclipse por exemplo, imprimem os stack clicaveis no console, ou seja, você clica em uma linha do stack trace, e o eclipse abre o arquivo, na linha em que o erro ocorreu.

Estes passos servem também para builds ANT, para programas escritos em action script (Flash ou Flex), para programas escritos em Ruby, incluindo o Rails.
E com pequenas adaptações, funciona também para C++, C, qualquer outra linguagem que gere algo parecido com um stack trace.

Agora uma perguntinha, só pra não perder o costume, você que leu até aqui, acha que valeu a pena a leitura? tem algum colega que você gostaria de poder obrigar a ler isto? ou tem algum exemplo que não se enquadra no que eu escrevi?
Eu tenho alguns amigos que eu gostaria de obrigar a ler isto, ou então abrir a cabeça e jogar isto para dentro, mas infelizmente eu não posso fazer isto.
Se você acha que o texto ficou bom, indique a leitura, se acha que precisa melhorar alguma coisa, deixe nos comentários que eu incorporo a melhoria no texto do post :D

PS.: este versionamento de posts do WP até que é legal, apaguei tudo sem querer, e acho que consegui recuperar legal com ele :D

If you enjoyed this post, make sure you subscribe to my RSS feed!

Tags:

Reader's Comments

  1. |

    Eh incrivel, mas hj em dia ainda encontra-se javeiros que respondem a pergunta “Qual foi o erro?” dessa maneira…

    Reply to this comment
  2. |

    Salve !!

    Respondendo a uma das perguntas finais:

    Tenho um colega que trabalha do meu lado e já estou enviado o link para ele.

    Caiu do céu este post !!

    Abraços

    Reply to this comment
  3. |

    Rafael, acho que nenhum especifico sobre o assunto …
    Pelo menos nenhum que eu conheça

    Reply to this comment
  4. |

    Fala Rodrigo,

    Fantástico post. Estava até pensando em escrever algo do gênero, mas você fez algo melhor do que eu estava pensando.

    Abraços!

    Reply to this comment
  5. |

    Mensagem de erro bem simples :-)

    Exceção é com “C cedilha” e não com “dois S” – por favor, corrija o artigo (pode deletar este comentário)

    Desculpe a brincadeirinha no começo.

    No mais, parabéns pelos artigos e pelo BLOG!

    Felicidades!

    Reply to this comment
  6. |

    Primeiramente, ótimo artigo.

    “Estranho” está escrito como “extranho”.

    Reply to this comment
  7. |

    Muito bom,

    Problema realmente comum este, muitos desenvolveres não sabem sequer “tracear” um stacktrace no console do Eclipse imagine qualquer tipo de erro.

    Leitura obrigatória para quem tem problemas com isso.
    Abraços.

    Reply to this comment
  8. |

    uma das primeiras coisas que eu procuro nos stacktraces são os ’causedBy:’. as vezes não possuem mas é a parte mais bonita dos stacktraces.. :)

    Reply to this comment
  9. |

    O bom d quem é autodidata é isso, vc na solidão do seu quarto, programando e aprendendo por conta acaba aprendendo a c virar sozinho (ler stacktraces). Tá certo q existe a Net pra t auxiliar … mas não tem o colega do lado, hehe … fiquei feliz por mim ao ler esse post e saber q mtos programadores recorrem ao colega do lado (pq é mais cômodo) ao invés d tentarem aprender d verdade as coisas, pq eu sei lá como aprendi a ler e me virar com stacktraces … mas ao mesmo tempo é triste saber q há programadores no mercado q no primeiro problema travam e não conseguem mais c virar sozinhos com seu código … não estou defendendo o individualismo, é totalmente válido a ajuda dos mais experientes, mas antes d sair perguntando devemos tentar, tentar e tentar novamente até c esgotar td nosso conhecimento … assim c aprende …

    Reply to this comment
  10. |

    Bastante esclarecedor.
    Show…

    Reply to this comment
  11. |

    - Quando algum colega perguntar “o que ocorreu” e vc for ver o stack. Um esclarecimento reduzido deste tutorial aplicado ao problema encontrado será show. Acredito também que suficiente para ser feito apenas uma vez quando o assunto for localizar o ponto do erro, quanto à resolver o erro, aí é outra coisa. Quanto ao “autodidatismo”, se aplicado em ambientes coporatismo,é meio complicado, pois procurar por uma vírgula ou doispontos as vezes é custoso demais. Acredito que pensando nisto é que criaram técnicas como o XP… Além do que, como já dizia Cora Coralina: “O conhecimento de nada vale se não for compartilhado.”

    Abraços à todos

    Reply to this comment
  12. |

    Nossa, muito bom este artigo!

    Eu tenho um colega que trabalhei por um tempo que de fato não conseguia ler stack traces ( bom, pra dar uma chance ao rapaz ele tinha começado a programar a menos de 1 ano )…

    Eu sempre falava pra ele que a solução do problema poderia ser encontrada simplesmente lendo a informação do erro e procurando a linha respectiva, mas acho que nunca consegui deixar isso de forma tão clara quanto a que você escreveu!

    Com certeza vou obrigá-lo a ler esse post, e talvez fazer uma lavagem cerebral ao melhor estilo de laranja mecânica hehehe!

    Reply to this comment

Leave a Comment