Manipulando Logs com Qt5

Logs com Qt5

O Qt, hoje na versão 5.X, é uma poderosa ferramenta que cada vez mais ganha novos recursos e aprimora os já existentes, provendo recursos para que a mesma aplicação possa ser executada em um Android, Windows, Linux, além de dispositivos embarcados utilizando Linux ou Windows, e poderosos RTOS's como QNX e VxWorks. Mais informações em Plataformas Suportadas.

 

Uma prática comum na maioria das aplicações executando sobre Linux e Linux Embarcado é gerar logs, estes contendo informações triviais sobre a execução do software ou informações de alertas, problemas e erros. E o Qt5 possui algumas funções prontas para auxiliar nesta tarefa, ou então configurar o uso delas de acordo com a sua necessidade.

 

O que iremos abordar neste artigo é quais são estas funções, onde usar, como usar, variáveis de ambiente para controle destas funções e um recurso que surgiu no Qt5.6 para utilizar syslog ou journald (desde o 5.3) para estas informações, estes dois sendo os sistemas de logs padrão na grande maioria dos SO Linux.

 

 

Ambiente

 

  • Linux Mint 15 Olivia AMD64
  • Qt Creator 4.1.x
  • Qt 5.6.2 (requisito > 5.6.x)

 

 

Funções Qt5 para logging

 

As funções utilizadas para manipular os logs de acordo com os seus propósitos são:

  • qDebug(): Usado para customizar a saída de uma Depuração; 
  • qInfo(): Usado para criar mensagens informativas; 
  • qWarning(): Usado para reportar alertas e erros recuperados da aplicação; 
  • qCritical(): Usado para reportar erros críticos e erros no sistema; 
  • qFatal(): Usado para reportar erros fatais, normalmente reportados logo antes de sair da aplicação.

 

Em algumas literaturas você pode encontrar a referência e citação destas funções como Qt logging framework, estas funções que fazer parte da listas Global de Macros em <QtGlobal>.

 

Existe uma outra solução caso as funções acima não tenham agradado ou adequado a aplicação, onde você pode criar o seu arquivo de log em um local específico e ir acrescentando dados conforme a necessidade. Para isso usa-se o QTextStream, que também veremos num simples exemplo, ou pode criar sua própria estrutura de logging na aplicação, vide exemplo no Qt Wiki – Simple-logger.

 

Um detalhe muito importante é sobre o uso de stdout (Standard Output) e stderr (Standard Error) no Qt, onde se você utilizar std::cout da iostream ou printf() da stdio estes serão canalizados para stdout padrão, já qDebug(), qInfo() e demais funções apresentadas todas serão canalizadas para stderr, logo mais iremos validar isso.

 

 

Criando Projeto Exemplo

 

Abra a Qt Creator, e vamos criar um simples projeto que será somente console, sem Interface Gráfica. Para isso vá em File > New File or Project..., selecione Application e logo ao lado direito Qt Console Application e clique em Choose, pode digitar qualquer nome, no caso irei usar lab01-logging e no diretório para criar o projeto pode escolher onde quer salvá-lo, as próximas etapas é só ir avançando e clicar em Finish.

 

Se tudo ocorreu bem, foi criado um arquivo main.ccp com o seguinte conteúdo:

 

 

Vamos adicionar um qDebug() apenas como exemplo e ver como seria sua implementação.

 

 

Então podemos usar o qDebug() escrevendo um texto dentro dos (), adicionar um %s e incluir uma variável QString, ou ainda usar << para anexar uma quantidade maior de dados ou variáveis no mesmo qDebug().

 

Compilando e executando teremos:

 

 

Atente sobre o uso de .toUtf8().constData(), isso sempre será necessário quando se usa %s em qInfo(), qWarning(), qDebug(), qFatal() e qCritical() pois ele espera codificação UTF-8, caso contrário um erro sera reportado.

 

A partir do Qt5.4 foi incluída a função qUtf8Printable(const Qstring), que equivale a fazer .toUtf8().constData(), uma maneira mais amigável de realizar a mesma tarefa.

 

Vamos alterar nosso código e fazer o seguinte teste, iremos incluir um printf() e um std::cout, sim usaremos ambos juntos, e qDebug() e qInfo() no mesmo código, então nosso main.cpp ficará como o código abaixo:

 

 

Após salvar, compilar a aplicação e executar, teremos como resultado o conteúdo abaixo:

 

 

Mas agora iremos executar o binário via terminal, vá até onde criou o projeto e acesse o diretório de build, e execute o binário como abaixo:

 

 

Agora vamos executar a aplicação novamente, mas iremos redirecionar o stdout (fd 1) para um arquivo em /tmp/lab01.stdout. Em seguida, executar novamente e redirecionar o stderr (fd 2) para o arquivo /tmp/lab01.stderr e visualizar a saída de ambos.

 

 

Então conseguimos executar e comprovar que qDebug(), qInfo() e demais funções utilizam stderr, e std::cout e printf() o stdout como padrão.

 

 

Criando o seu próprio logging com QTextStream

 

As funções que vimos podem ajudar muito durante o desenvolvimento e etapas de informações, alertas e erros, mas as vezes é necessário alimentar um arquivo com informações de algumas etapas.

 

Desta forma, podemos criar um simples logging usando a classe QTextStream, um excelente e poderoso recurso para se trabalhar com Stream de texto em Qt, conseguindo manipular dispositivos ou arquivos em disco. Utilizando os operadores << e >> consegue-se facilmente escrever e ler do QTextStream, além de possibilitar recursos como configurador um padrão de codificação como UTF-8.

 

Para este exemplo iremos criar o Projeto lab02-logging e adicionar o seguinte conteúdo:

 

 

Salve o código, compile e execute, e um arquivo com o mesmo nome do binário será criado com a extensão .log e irá conter o conteúdo "[LOG] TESTE LOG LOCAL", como abaixo.

 

 

O código e a ideia é simples, mas pode ser estendida e bem elaborada em uma classe para anexar log de diversos eventos da aplicação, pode-se basear no exemplo de Simples-Logger.

 

 

Utilizando o log do Sistema

 

Este é um recurso novo a partir do Qt5.6.0 que permitir anexar logs da aplicação no sistema de log do Sistema, aqui iremos adotar o SysLog para estudo, padrão este utilizando em Sistemas Unix e Linux de servidores a roteadores, mas também aplica-se ao journald.

 

Porém, este recurso não vem habilitado por padrão, então caso compilou o Qt5 em seu host ou realizou compilação-cruzada para algum target embarcado, este recurso deverá ser habilitado para utilizá-lo, para confirmar é só visualizar o arquivo config.summary gerado durante o processo de configuração do Qtbase. Abaixo o config.summary de uma compilação-cruzada para uso em um ARM.

 

 

Para habilitar as opções deve-se incluir nos parâmetros do configure o -syslog e/ou -journald.

 

Com esta dependência resolvida, iremos criar o Projeto lab03-logging, configurar uma variável ambiente e informar ao Qt para utilizar o Log do Sistema, segue o código:

 

 

Se executarmos o código, irá funcionar da mesma maneira anterior, todas as mensagens irão para o console. Para “forçar” do redirecionamento das mensagens do qDebug() e demais pra o Sistema de Log deve-se configurar uma variável ambiente para isso, chamada QT_LOGGING_TO_CONSOLE.

 

Na linha 9 onde esta comentado, podemos desabilitar o envio para syslog ou journald configurando como 1 ou habitar o envio configurando como 0.

 

Inseri comentado no código para mostrar que é possível deixar configurado na aplicação desta forma, mas iremos fazer nosso exemplo utilizando variável ambiente no Linux.

 

 

Na saída acima, configurando QT_LOGGING_TO_CONSOLE como 0 teremos apenas a saída do printf() no console e as mensagens de stderr no /var/log/syslog.

 

 

Dicas Extras

 

Para encerrar este artigo, iremos juntar todas as funções de manipulação de logs, e aplicar algumas configurações que podem ajudar e muito no desenvolvimento de um software, utilizando variáveis ambientes e alguns defines. Vamos criar o projeto lab04-logging e utilizar o código abaixo:

 

 

Compilando e executando a mensagem do printf() não será exibida, pois em qualquer momento que houver um qFatal() a aplicação é abortada e retorna algo diferente de 0.

 

Porém, conseguimos o mesmo efeito com qWarning() e qCritical(), setando as variáveis ambiente QT_FATAL_WARNINGS e QT_FATAL_CRITICALS para 1, vamos exportar no terminal o QT_FATAL_WARNINGS para 1 e executar a aplicação novamente, na sequência o mesmo com QT_FATAL_CRITICALS.

 

 

Desta maneira, podemos configurar quando ocorrer qualquer qWarning(), qCritical() a aplicação pode ser abortada, isso utilizando variáveis de ambiente que podem ser configuradas na aplicação ou exportadas no terminal antes de executar.

 

Outra dica muito útil é sobre as mensagens do qDebug(), em meus projetos acabo abusando muito no uso de mensagens de qDebug(), qInfo() e qWarning(), mas na versão de produção muitas vezes você não quer ver estas mensagens ou ver apenas qInfo() ou qWarning(), para isso conseguimos manipular com três defines QT_NO_DEBUG_OUTPUT, QT_NO_WARNING_OUTPUT e QT_NO_INFO_OUTPUT.

 

Vou adicionar no lab04-logging.pro o QT_NO_DEBUG_OUTPUT.

 

 

Compilando e executando o lab04-logging.

 

 

Agora irei adicionar também QT_NO_WARNING_OUTPUT no lab04-logging.pro e irei comentar o qFatal() no main.cpp.

 

 

Não temos mais qDebug() e qWarning() nas saídas e sem o qFatal() o nosso printf() foi exibido. Por último podemos também ignorar o qInfo() adicionando QT_NO_INFO_OUTPUT, nosso lab04-logging.pro ficando como abaixo:

 

 

Compilando e executando:

 

 

Muito útil estes defines, onde você pode utilizar diversos qDebug() pelo projeto e na versão final de produção, ele pode ser omitido sem você ter que varrer N linhas para comentar ou remover estas entradas.

 

Outra dica é remover espaços e citação "" no texto utilizando qDebug(), vamos alterar o exemplo lab01-logging.

 

 

Compilando e executando:

 

 

O qDebug() utilizado nas linhas 11 e 14 eram para apresentar a mesma saída, porém o segundo incluiu um espaço e adicionou "" na QString da variável debugMsg, isso sempre irá ocorrer quando se utilizar 'qDebug() << "VARIAVEL OU TEXTO"', para resolver este impasse, iremos utilizar funções do próprio qDebug(), noquote() e quote() para remover ou inserir citação, e nospace() e space() para remover ou adicionar espaço.

 

Iremos alterar a linha 14 de:

 

 

Para:

 

 

Compilando e executando:

 

 

Conseguimos o mesmo resultado com das suas maneiras de utilizar qDebug(), a citação "" no texto pode-se remover inserindo .toUtf8().constData(), mas se fosse um texto direto não resolveria, então só com noquote().

 

Estas informações e muitas outras encontram-se na documentação da classe QDebug.

 

 

Não acaba aqui...

 

O que apresentamos aqui neste artigo, foi apenas uma base do que podemos fazer com a parte de logging no Qt5, além disso, ele possibilita diversos outros recursos para você customizar e adotar em seu projeto como o QtMessageHandler(QtMsgType, QmessageLogContex e QString), qSetMessagePattern e qInstallMessageHandler onde você podera criar e montar a sua própria estrutura e formatação das mensagens.

 

Poderá ser tema ou aplicado em um próximo artigo sobre Qt5 ou utilizado como exemplo em um projeto.

 

Esperamos que você tenha gostado do artigo, abuse das sintaxes e do framework Qt, fique à vontade para enviar sugestões ou algum comentário.

 

Boa diversão!

 

 

Referências

 

http://doc.qt.io/qt-5/qtglobal.html

http://doc.qt.io/qt-5/debug.html

http://doc.qt.io/qt-5/qtextstream.html

http://doc.qt.io/qt-5/qtglobal.html#qInstallMessageHandler

http://doc.qt.io/qt-5/qtglobal.html#QtMessageHandler-typedef

http://doc.qt.io/qt-5/qtglobal.html#QtMsgType-enum

NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Cleiton Bueno
Proprietário da B2Open onde oferecemos consultoria, treinamentos e desenvolvimento em Sistemas Embarcados. Entusiasta a filosofia open-source, mais de 10 anos de experiências em Linux e FOSS. Em sistemas embarcado do firmware baremetal ao Linux Embedded, e há aproximadamente 8 anos desenvolvendo em (C, Python, Qt e muito Shell Script), além de profiling, hardening e tuning para targets com Linux Embarcado. Graduado em Engenharia da Computação pela UNICEP com ênfase em robótica e sistemas embarcados.

Deixe um comentário

avatar
 
  Notificações  
Notificar