Logs em serviços (e outras coisas)

2015/06/05

Já uso logs há muito tempo. Me lembro muito bem que quando programava em BASIC o “passou por aqui” já era útil. Depois de fazer muitas bibliotecas super-flexíveis de escrita em saídas diferentes, níveis configuráveis e uso do mais complexo ao mais banal, cheguei à seguinte conclusão:

Log("Quero um log mais simples possível (de preferência ", 15, " vezes mais simples)");

Vou tentar defender meu ponto de vista.

Esse artigo do Dr. Dobbs explica de uma maneira bem completa como fazer uma lib de log leve e configurável. O que eu peguei desse exemplo foi a forma mais C++ de formatar as linhas, deixando para trás o estilão printf que depois de variadic templates já está datado.

#include <iostream>
#include <sstream>

inline void Log(std::ostringstream& os)
{
    std::cout << os.str() << std::endl;
}

template<typename First, typename...Rest >
void Log(std::ostringstream& os, First parm1, Rest...parm)
{
    os << parm1;
    Log(os, parm...);
}

template<typename...Rest >
void Log(Rest...parm)
{
    std::ostringstream os;
    LogHeader(os);
    Log(os, parm...);
}

Por que eu acho a minha versão mais legal (não valendo falar que foi porque eu fiz):

Encapsular a saída e o comportamento de um serviço hoje em dia é algo banal. Há diversos programas que fazem isso para você, sendo desnecessário programar toda aquela parte de comunicação com o Windows. O cara do DriverEntry (vulgo o kernel-mode programmer motta-focka Fernando) fez um aplicativo que faz isso, que é simples de usar e continua funcionando no Windows 8.1. Atualmente uso um outro encontrado pelo igualmente fodástico Rodrigo Strauss: o Non Sucking Service Manager (seu nome já explica por que defendo utilizar o mínimo possível das firulas da Microsoft).

Além de ser extremamente flexível e não ter falhado nas vezes que o utilizei, o NSSM consegue redirecionar a saída do aplicativo que encapsula como um serviço para um arquivo e rotacionar o arquivo por tamanho ou data (ou reexecução do serviço):

Abaixo uma receitinha básica para configurar seu aplicativo:

nssm.exe install MyService C:\Path\MyService.exe <args>
nssm set MyService AppStdout C:\Path\Logs\MyService.log
nssm set MyService AppStderr C:\Path\Logs\MyService.log
nssm set MyService AppRotateFiles 1
nssm set MyService AppRotateOnline 1
nssm set MyService AppRotateBytes 10485760

(para quem está se perguntando, 10485760 bytes são 10 MB.)

Com essa forma de fazer serviços, há uma dupla vantagem:

E ainda uma vantagem-bônus:

Bônus final

Acho que cada um deve escrever no seu header o que achar melhor para depurar seus programas. No entanto, acho válido compartilhar quais são as informações que tem sido úteis para mim:

inline void LogHeader(std::ostringstream& os)
{
    SYSTEMTIME st;
    char buffer[48] = "";

    GetLocalTime(&st);

    sprintf_s(buffer, "%04d-%02d-%02d %02d:%02d:%02d %04X.%04X %08X ",
        st.wYear, st.wMonth, st.wDay,
        st.wHour, st.wMinute, st.wSecond,
        GetCurrentProcessId() & 0xFFFF, GetCurrentThreadId() & 0xFFFF,
        GetLastError());

    os << buffer;
}

Facebook | Twitter | Linkedin | Google