Página 2 de 3
Erro em acesso ao banco de dados com internet lenta
Enviado: 15 Ago 2014 22:59
por cjp
Resolvi de uma forma simples: mandar o error.log de dentro do recover. Ficou assim:
Código: Selecionar todos
bError := ErrorBlock( {|e| Break(e) } )
begin sequence
DBUSEAREA( .T.,, "SELECT * FROM ativ where data>'"+alltrim(str(year(date()-2)))+"-"+substr(dtoc(date()-2),4,2)+"-"+substr(dtoc(date()-2),1,2)+"'", "ativ")
recover
logerro()
mandmail1("error.log","Erro recuperado com o recover no fazmail")
endsequence
ErrorBlock( bError )
Só que para isso eu precisei apenas separar uma parte do meu arquivo errorsys.prg em uma função que chamei logerro(), apenas para construir o error.log. Ficou assim:
Código: Selecionar todos
function logerro
cMessage := "Ocorreu o erro: "+ansi(cMessage) + Hb_Eol()
cMessage += "Data..........: "+dtoc(date())+ Hb_Eol()
cMessage += "Hora..........: "+time() + Hb_Eol()
cMessage += "Máquina.......: "+netname() + hb_eol()
cMessage += "Programa......: " + Hb_CmdArgArgV() + Hb_Eol() //+ hb_eol()
cMessage += ansi("Versão........: ") + vers + Hb_Eol() + Hb_Eol()
cMessage += ansi("Na função.....: ") + ProcName(2) + Hb_Eol() //+ hb_eol()
cMessage += "Na linha......: " + NTRIM(ProcLine(2)) + Hb_Eol() //+ hb_eol()
cMessage += "No prg........: " + procfile(2) + Hb_Eol() + hb_eol()
cMessage += "Pasta.........: " + CurDir() + Hb_Eol()
cMessage += ansi("Usuário.......: ") + us + Hb_Eol()
cMessage += "Base em uso...: " + Alias() + Hb_Eol()
cMessage += ansi("Área em uso...: ") + alltrim(str(select())) + Hb_Eol()
cMessage += hb_eol() + ansi("Área 1........: ") + alias(1) + Hb_Eol()
cMessage += if(!empty(alias(2)),ansi("Área 2........: ") + alias(2) + Hb_Eol(),"")
cMessage += if(!empty(alias(3)),ansi("Área 3........: ") + alias(3) + Hb_Eol(),"")
cMessage += if(!empty(alias(4)),ansi("Área 4........: ") + alias(4) + Hb_Eol(),"")
cMessage += if(!empty(alias(5)),ansi("Área 5........: ") + alias(5) + Hb_Eol(),"")
cMessage += if(!empty(alias(6)),ansi("Área 6........: ") + alias(6) + Hb_Eol(),"")
cMessage += if(!empty(alias(7)),ansi("Área 7........: ") + alias(7) + Hb_Eol(),"")
cMessage += if(!empty(alias(8)),ansi("Área 8........: ") + alias(8) + Hb_Eol(),"")
cMessage += if(!empty(alias(9)),ansi("Área 9........: ") + alias(9) + Hb_Eol(),"")
cMessage += Hb_Eol()
cMessage += "Caminho Percorrido Antes do Erro:" + Hb_Eol()
i := 2
While ( !Empty( ProcName(i) )) //.and. procname(i)#"MAIN" )
cMessage += "Vindo de......: " + Trim(ProcName(i)) + "(" + NTRIM(ProcLine(i)) + " - " + procfile(i) + ")" + Hb_Eol() + hb_eol()
i++
EndDo
cMessage += Hb_Eol()+"Mem¢ria dispon¡vel para valores caracteres: "+alltrim(str(memory(0)))
cMessage += Hb_eol()+"Maior bloco dispon¡vel para valores caracteres: "+alltrim(str(memory(1)))
cMessage += Hb_eol()+"µrea dispon¡vel para comandos RUN: "+alltrim(str(memory(2)))
cMessage += Hb_Eol()
cMessage += Hb_Eol()
cMessage += "V¡deo Screen Dump:" + Hb_Eol()
cMessage += Replicate( '-', nCols -15 ) + Hb_Eol()
nCellSize := len( Savescreen( 0, 0, 0, 0 ) )
nRange := ( nCols + 1 ) * nCellSize
For nCount := 1 To nRows + 1
cOutString := ''
cSubString := Substr( cScreen, nStart, nRange )
For nForLoop := 1 To nRange step nCellSize
cOutString += Substr( cSubString, nForLoop, 1 )
Next
cMessage += "|" + cOutString + "|" + Hb_Eol()
nStart += nRange
Next
cMessage += Replicate( '-', nCols -18 ) + Hb_Eol()
Use
Ferase( "error.log")
MemoWrit( "error.log", cMessage )
return
Erro em acesso ao banco de dados com internet lenta
Enviado: 11 Set 2014 16:46
por cjp
Amigos,
Como havia dito, estou usando com êxito esse begin sequence para contornar erros. Em tudo está funcionando sem problema.
Mas agora tentei usar em uma outra função, desta forma:
Código: Selecionar todos
if upper(exclusivo)#"S"
if select(bssembarra) = 0
bError := ErrorBlock( {|e| Break(e) } )
begin sequence
use &nomebase. shared
recover
logerro()
mandmail1("error.log","Erro na função usebase contornado com o recover do begin sequence 19142",,2)
return .f.
endsequence
ErrorBlock( bError )
else
DbSelectArea(bssembarra)
exit
endif
else
if select(bssembarra) = 0
bError := ErrorBlock( {|e| Break(e) } )
begin sequence
use &nomebase. //new
recover
logerro()
mandmail1("error.log","Erro na função usebase contornado com o recover do begin sequence 19157",,2)
return .f.
endsequence
ErrorBlock( bError )
else
DbSelectArea(bssembarra)
if exclusivo="S"
use
use &nomebase. //new
else
exit
endif
endif
endif
if neterr()
vezusb++
@ maxrow()-1,1 clear to maxrow()-1,maxcol()-1
@ maxrow()-1,1 say "Base "+upper(nomebase)+" ocupada; aguarde liberação ("+alltrim(str(vezusb))+")"
inkey(8)
@ maxrow()-1,1 clear to maxrow()-1,maxcol()-1
desiste:=inkey(8)
if desiste= 27 //K_ESC
conf="N"
@ maxrow()-2,1 clear to maxrow()-1,maxcol()-1
@ maxrow()-1,5 say "Confirma abandono da tentativa da utilização?"get conf pict "@!"
read
@ maxrow()-2,1 clear to maxrow()-1,maxcol()-1
if conf="S"
retf="S"
exit
* return .f.
endif
endif
else
exit
endif
Só que, neste caso, ao invés de entrar no recover, o programa está fechando automaticamente, e sem dar qualquer mensagem de erro.
Notei que isso ocorre sempre que a base está em uso por outro programa. Portanto, quando deveria entrar no neterr(), esperando a liberação da base. Mas daí, com o begin sequence, ele já fecha o programa logo.
Alguém saberia me dizer qual a razão disso? E o que eu faço para consertar isso?
Erro em acesso ao banco de dados com internet lenta
Enviado: 12 Set 2014 03:26
por alxsts
Olá!
Embora o assunto deste tópico já tenha sido desviado, vou responder a mensagen anterior.
cjp escreveu:...Só que, neste caso, ao invés de entrar no recover, o programa está fechando automaticamente, e sem dar qualquer mensagem de erro...
No momento em que este erro ocorre, o manipulador padrão de erros já é o teu e não mais o ErrorSys. Não sei porque mas NetErr() (DOS Error 32) não é ativado quando ocorre esse erro (arquivo em uso por outra sessão). Assim, o erro não é tratado pelo teu manipulador de erro e não existe outro manipulador. Então, o controle é passado ao sistema operacional o programa é fechado. Teria que tratar "na unha", testando o DOS Error 32.
Veja o exemplo abaixo e adapte-o à suas necessidades.
Código: Selecionar todos
#define DB_SHARED .T.
#define DB_EXCLUSIVE .F.
FUNCTION Main()
AltD()
SetMode( 50,100)
OpenTable( "CadOs", { "CadOs" }, NIL, NIL, DB_EXCLUSIVE )
Browse()
If Select( "CadOs" ) > 0
DbCloseArea( "CadOs" )
Endif
CLS
RETURN NIL
//-------------------------------------------------------------------------------------------------------------
FUNCTION OpenTable( cName, aIndexList, cAlias, lNewArea, lShared, cDriver, lReadonly )
LOCAL bError := ErrorBlock( {|e| Break(e) } )
LOCAL oErr
LOCAL nPos, nLen
LOCAL lRet := .F.
IF ! Empty( cName )
// verificação dos parâmetros
lShared := If( lShared == NIL, .T., lShared )
lNewArea := If( lNewArea == NIL, .T., lNewArea )
lReadonly := If( lReadonly == NIL, .F., lReadonly )
cAlias := If( cAlias == NIL, cName, cAlias )
cDriver := If( cDriver == NIL, "DBFNTX", cDriver )
aIndexList := If( Empty( aIndexList ), {}, aIndexList )
BEGIN SEQUENCE
// Abre a tabela
IF lNewArea
DbSelectArea( 0 )
ENDIF
// DBUSEAREA( lNewArea, cDriver, cName, cAlias, lShared, lReadonly ) ou
IF lShared
USE (cName) ALIAS (cAlias) SHARED VIA (cDriver) // não é necessário usar o macro operator (&) ...
ELSE
USE (cName) ALIAS (cAlias) EXCLUSIVE VIA (cDriver) // ... basta colocar as variáveis entre parêntesis...
ENDIF
// Abre eventuais índices.
// Abre de traz para a frente. Assim, o primeiro índice da lista se torna a ordem atual...
IF ( nLen := Len( aIndexList ) ) > 0
FOR nPos := nLen TO 1
DbSetIndex( aIndexList[ nLen ] )
NEXT
ENDIF
lRet := .T.
RECOVER USING oErr
IF oErr:osCode == 32
NetErr( .T. ) // é possível setar NetErr() para True mas, não se tem detalhes do erro...
ENDIF // ... mas se existe o objeto erro, com os detalhes do erro, é melhor explorar ele
ErrShow( oErr ) // passando o objeto erro para uma rotina de erro ou geração de logs
// Dispara o erro com o manipulador padrão.
Eval( bError, oErr ) // No caso de oErr:osCode == 32, nada acontece. Não olhei o ErrorSys.Prg padrão mas acho que ele não trata isso...
ENDSEQUENCE
END
// Restaura o manipulador de erros padrão
ErrorBlock( bError )
RETURN lRet
//-------------------------------------------------------------------------------------------------------------
STATIC FUNCTION ErrShow( oErr )
/*
oErr:args An array of function or operator arguments
oErr:canDefault Indicates whether or not default recovery is available
oErr:canRetry Indicates whether or not a retry is possible after an error
oErr:canSubstitute Indicates if a new result can be substituted after an error
oErr:cargo User-definable variable
oErr:description Character description of the error condition
oErr:filename Name of the file associated with the error
oErr:genCode CA-Clipper error code number
oErr:operation Character description of the failed operation
oErr:osCode Operating system error code number
oErr:severity Indicates error severity
oErr:subCode Subsystem-specific error code number
oErr:subSystem Character description of the subsystem generating the error
oErr:tries Number of times the failed operation has been attempted
*/
HB_Alert( { "Ocorreu o erro : " + oErr:subSystem + "/" + Ltrim(Str(oErr:subCode)), "DOS Error " + LTrim(Str(oErr:osCode)), "Descricao: " + oErr:description, "Operacao: " + oErr:Operation, "Arquivo: " + oErr:fileName },{ 'Ok' },"W+/B", 15 )
RETURN NIL
//-------------------------------------------------------------------------------------------------------------
Erro em acesso ao banco de dados com internet lenta
Enviado: 12 Set 2014 23:18
por cjp
Obrigado, meu caro.
Mas fiquei com duas dúvidas:
1) quando vc diz "No momento em que este erro ocorre, o manipulador padrão de erros já é o teu e não mais o ErrorSys", isso significa que o erro ocorre na linha "use &nomebase.", certo? E este comando está dentro do begin sequence, certo? Bom, mas daí, dando erro dentro do begin sequence, ele não deveria desviar pro recover? E daí executar a função mandmail1?
2) No programa que vc postou, não está da mesma forma que o meu? Quero dizer a linha "USE (cName) ALIAS (cAlias) SHARED VIA (cDriver)" está entre o begin sequence e o RECOVER USING oErr, não?
Erro em acesso ao banco de dados com internet lenta
Enviado: 13 Set 2014 12:12
por alxsts
Olá!
A resposta é sim para as duas questões.
Creio que ocorre algum erro dentro da MandMail1 e, como não há tratamento, o programa é encerrado.
Restaure o manipulador de erros padrão antes de invocar a função e veja o que acontece.
Código: Selecionar todos
RECOVER USING oErr
// Restaura o manipulador de erros padrão
ErrorBlock( bError )
MandMail1()
ENDSEQUENCE
END
Erro em acesso ao banco de dados com internet lenta
Enviado: 13 Set 2014 12:45
por cjp
Eu já fiz isso. Retirando o manipulador, volta ao normal, não há qualquer erro na mandmail1.
Poderia ser algum erro gerado no próprio manipulador?
Erro em acesso ao banco de dados com internet lenta
Enviado: 13 Set 2014 14:22
por alxsts
Olá!
O que é "volta ao normal"?
Se na MandMail() não tem erro, talvez tenha na LogErro(). Teste este código:
Código: Selecionar todos
LOCAL bError := ErrorBlock( {|e| Break(e) } )
BEGIN SEQUENCE
? 1 / 0
RECOVER USING oErr
// Restaura o manipulador de erros padrão
ErrorBlock( bError )
LogErro()
MandMail1()
ENDSEQUENCE
Erro em acesso ao banco de dados com internet lenta
Enviado: 13 Set 2014 16:05
por cjp
Quando disse "volta ao normal" quis dizer que não dava mais erro.
Mas vc tinha razão: tinha erro na logerro(). Corrigi e resolveu. Agora vou testar na função que eu estava usando.
Mas antes, fiquei com uma dúvida: o erro que tinha era por falta de definição de uma variável (cMessage), que está definida no errorsys. Quando o logerro() é chamada da errorsys, sem problema. Mas quando ela era chamada do recover do meu manipulador, dava problema. Tentei definir essa variável dentro do próprio logerro(), do jeito que está no teu exemplo anterior, mas não funcionou. Veja, fiz assim:
Não era pra funcionar assim? Por que continuou dando variável inexistente?
Agora eu defini ela no início do programa, e deu certo. Mas eu preferia fazer desta forma que vc fez.
Erro em acesso ao banco de dados com internet lenta
Enviado: 13 Set 2014 18:44
por alxsts
Olá!
cjp escreveu:Tentei definir essa variável dentro do próprio logerro(), do jeito que está no teu exemplo anterior, mas não funcionou.
Neste caso você não definiu a variável. Apenas testou se o conteúdo da variável estava NIL ou não. Como a variável não existe neste instante, gera erro.
Naquele exemplo acima, foram testadas as variáveis que a função recebeu como parâmetros de entrada. Sempre que uma função recebe parâmetros são criadas variáveis do tipo LOCAL, com os nomes dos parâmetros recebidos ou seja, são declaradas variáveis locais à função. Veja:
Código: Selecionar todos
FUNCTION Teste()
? MessageShow( "Brasil" )
RETURN NIL
//--------------------------------------------
FUNCTION MessageShow( cMessage )
cMessage := IF( cMessage == NIL, "NIL", cMessage ) // ou cMessage := IF( Empty( cMessage ), "NIL", cMessage )
Alert( cMessage )
RETURN NIL
//--------------------------------------------
Resumindo: a melhor prática é sempre declarar todas as variáveis que o programa utiliza. Use sempre variáveis dos tipos LOCAL e/ou STATIC. Com os avanços do Harbour, é possível especificar o tipos das variáveis definidas. Ainda não podemos dizer que o Harbour é uma linguagem fortemente tipada mas, usar este recurso faz com que o compilador gere avisos (warnings), dependendo das configurações de compilação, quando o programador faz atribuições de valores de dados às variáveis diferentes dos tipos declarados.
Código: Selecionar todos
LOCAL aArray AS Array
LOCAL nVal AS Numeric
LOCAL dDate AS Date
LOLAL cVal AS Character
// as linhas abaixo gerarão warnings pois os dados atribuídos às variáveis são diferentes dos declarados
aArray := "Teste"
nVal := "10"
dDate := DtoS( Date() )
cVal := Date()
Erro em acesso ao banco de dados com internet lenta
Enviado: 14 Set 2014 02:21
por cjp
Entendido. Muito obrigado.
Erro em acesso ao banco de dados com internet lenta
Enviado: 18 Out 2014 00:36
por cjp
Caros colegas,
Por favor, observem este trecho de uma função minha:
Código: Selecionar todos
bError := ErrorBlock( {|e| Break(e) } )
begin sequence
append blank
repl arqmand with am
repl assmail with asm+"; bsusonow: "+alltrim(str(bsusonow))+"; área: "+alltrim(str(select()))+"; data: "+dtoc(date())+"; hora: "+time()
repl maildst with md
repl anexo with an
repl tela with cMessage
recover
cMessage := ErrorMessage(e)
logerro()
mandmail1("error.log","Erro recuperado com o recover no fazmail",,2)
endsequence
ErrorBlock( bError )
A ideia é contornar eventuais erros no primeiro trecho, o que tem acontecido satisfatoriamente.
O único problema é que, quando ocorre o erro, eu precisaria saber qual é o erro. Por isso coloquei:
Daí, na função logerro() eu crio um arquivo de log com vários detalhes que o programa me envia automaticamente com a função mandmail1().
O problema é que cMessage está retornando vazio. E assim eu não consigo saber qual o erro que está dando dentro da sequência.
Em outros testes, acontece também de o programa fechar sem dar nenhum erro. Daí eu restaurei o ErrorBlock( bError ) e deu o seguinte erro:
Se eu bem entendi, esse E não seria o e do bError := ErrorBlock( {|e| Break(e) } )? Se for, por que seria uma variável inexistente?
Alguém me ajuda?
Erro em acesso ao banco de dados com internet lenta
Enviado: 22 Out 2014 18:02
por Toledo
Inácio, se você estiver usando o comando bError := ErrorBlock( {|e| Break(e) } ), infelizmente não tem como saber qual o erro que ocorreu, pois o comando Break(e) vai encerrar a chamada do ERRORSYS e desviar a execução do programa para depois do comando RECOVER. Então, sem a execução do ERRORSYS, não tem como saber qual foi o erro.
Quando lhe foi sugerido o uso do bError := ErrorBlock( {|e| Break(e) } ), foi exatamente porque você não queria que o ERRORSYS fosse executado.
Então, ou você usa o bError := ErrorBlock( {|e| Break(e) } ) e evitar que o ERRORSYS seja executado, ou retira a linha do ErrorBlock e deixa o ERRORSYS ser executado, onde você poderá saber qual o erro que está ocorrendo, criar o log e enviar o e-mail.
Abraços,
Erro em acesso ao banco de dados com internet lenta
Enviado: 22 Out 2014 18:23
por cjp
Entendi.
Mas tem como executar o errorsys() e depois retornar, com o recover ou de alguma outra forma, para o ponto do programa onde deu erro e prosseguir normalmente?
O que eu preciso evitar é a interrupção do programa pelo erro, o que o begin sequence está fazendo maravilhosamente bem. Mas também preciso do log do erro. Dá pra juntar o melhor dos dois mundos?
Erro em acesso ao banco de dados com internet lenta
Enviado: 22 Out 2014 19:17
por Toledo
cjp escreveu:Mas tem como executar o errorsys() e depois retornar, com o recover ou de alguma outra forma, para o ponto do programa onde deu erro e prosseguir normalmente?
Sim, é possível, basta modificar o
ERRORSYS e trocar o comando
QUIT por
Break(e). Mas neste caso, o uso do comando
Begin sequence... Recover... endsequence será obrigatório em todos os módulos do seu programa, porque quando ocorrer qualquer erro tem que ter o Recover para retornar a execução do programa, caso contrário, o erro ocorrer sem existir um Recover para retorno, a execução vai retornar no mesmo ponto onde ocorreu o erro e ai se o erro persistir será chamado novamente o ERRORSYS (ficando num LOOP infinito).
Geralmente quando o ERRORSYS tem o QUIT trocado por Break(e), o Begin Sequence é colocado no menu do programa inicial, logo após o comando MENU TO. Assim quando ocorrer qualquer erro no programa, a execução vai retornar para o menu no programa inicial. Já o comando QUIT no ERRORSYS vai finalizar o programa, retornando para o DOS.
Abraços,
Erro em acesso ao banco de dados com internet lenta
Enviado: 23 Out 2014 02:01
por alxsts
Olá!
Inácio: creio que o que você não está entendendo é o funcionamento, a mecânica da coisa.
Sempre que um erro é detectado, o Clipper ou (x)Harbour automaticamente gera um objeto erro e passa este objeto erro ao manipulador de erros ativo. Se for o ErrorSys padrão, o objeto erro vai ser passado para ele. Se houver um manipulador de erros personalizado, como nos exemplos mostrados, o objeto erro será passado para este manipulador personalizado.
Veja:
1 - você define o seu manipulador de erros personalizado com bError := ErrorBlock( {|e| Break(e) } ). Neste momento, o ErrorSys padrão é armazenado em bError e um novo manipulador de erros se torna ativo.
2 - durante o processamento da aplicação, ocorre um erro qualquer. O Clipper ou (x)Harbour intercepta o erro e gera um
objeto da classe Error
3 - em seguida, o Clipper ou (x)Harbour passa o objeto Error gerado para o manipulador de erros ativo. Supondo que o manipulador ativo tenha sido definido com bError := ErrorBlock( {|e| Break(e) } ), o objeto Error será recebido como o parâmetro e ( {|
e| Break( e ) } ) no code block do manipulador de erros e será repassado como parâmetro de entrada da função tratadora de erros. Neste caso, está sendo usada a função
Break() mas poderia ser uma outra, definida por você mesmo. Veja que a função recebeu o objeto de erro como parâmetro de entrada (
Break( e ) )
4 - Para que você possa capturar e tratar este objeto Erro gerado em sua aplicação, é necessário receber este objeto Erro. Este é o ponto que acho que você ainda não percebeu. Para receber o objeto, é necessária a cláusula RECOVER USING. Quando Break(
e ) é executado, o objeto Erro é passado para a variável declarada (neste caso oErr) e fica disponível para o teu programa tratar
LOCAL oErr
BEGIN SEQUENCE
.
.
.
RECOVER USING oErr
.
.
.
ENDSEQUENCE
5 - com este objeto Erro em mãos, você poderá tratar o erro de forma muito flexível, explorando todas as propriedades do objeto Erro (listadas no link acima), sem ter que recorrer ao GetSys. Se preferir recorrer a ele, basta executar um Break( bError ) - lembra que você guardou o manipulador padrão em bError?
6 - exemplo de operação que dá erro e permite que se tente novamente:
Código: Selecionar todos
FUNCTION Teste()
LOCAL bError := ErrorBlock( {|e| Break(e) } ), nOption
Altd()
CLS
WHILE .T.
BEGIN SEQUENCE
?
FOR nOption := 10 TO 0 STEP -1
? 10 / nOption
NEXT
RECOVER USING oErr
nOption := Alert( "Ocorreu o erro " + ;
oErr:subsystem + "/" + ;
Ltrim( Str( oErr:subCode ) ) + ;
" - " + oErr:description + ;
' na operação "' + oErr:operation + '"', { " Continuar ", " Fechar "} )
//LogErro()
//MandMail1()
IF nOption != 1
EXIT
ELSE
LOOP
ENDIF
ENDSEQUENCE
ENDDO
// Restaura o manipulador de erros padrão
ErrorBlock( bError )
RETURN NIL