Estou escrevendo uma biblioteca que possui muitas informações que podem ser úteis em um log do programa que a utiliza, mas não sei a melhor maneira de expô-la de tal maneira que o programa que usa minha biblioteca possa integrar os logs da minha biblioteca com seus próprios logs de maneira aparentemente inexistente (se desejado).
Escolher uma biblioteca de log específica para minha biblioteca adiciona à lista de dependências para usar minha biblioteca e vincula o programa principal a essa biblioteca - e se várias bibliotecas usadas pelo programa principal fizessem isso, cada uma poderia ter selecionado uma biblioteca diferente .
Já pensei em ter o programa registrar um objeto de fluxo C ++ na biblioteca para ele usar. Parece que seria um objetivo relativamente geral, mas também pensei em fazer com que o programa principal registrasse uma função de retorno de chamada que seria solicitada com o conteúdo e os metadados quando os dados forem registrados. Outra opção seria apenas armazenar os dados de log na biblioteca em algum tipo de lista para o programa principal pegar sempre que quiser lidar com esses dados, deixando o programa principal decidir quando tem tempo para lidar com os dados.
Estou procurando sugestões e prós / contras de diferentes abordagens para que eu possa decidir o que é melhor na minha situação.
Respostas:
Você pode expor vários métodos para receber o log da sua biblioteca e agrupar todos, exceto um nos adaptadores, ao "real" usado na biblioteca.
Por exemplo, você decide internamente ter uma
std::function<void(std::string)>
coleção que é cada retorno de chamada do criador de logs. Tu forneces:e também
e também
e mais variações dos tipos "receber seqüências de caracteres de algum lugar" para os quais você deseja implementar adaptadores.
fonte
A maneira mais simples de permitir que um aplicativo possa acessar a funcionalidade de log é permitir que ele registre uma classe / função para receber a mensagem de log. O que eles fazem com essa mensagem depende inteiramente do aplicativo.
Usando C / C ++, você pode usar o seguinte em sua biblioteca:
Um aplicativo pode registrar uma função chamando
registerLogMessageReceiver
. com o apropriadouser_data
. Na seção de log da sua base de código, você deve chamar essa função com a mensagem apropriada e a registradauser_data
.Se você não precisa se preocupar com C, pode usar uma classe como destinatário de mensagens.
e adicione uma função na biblioteca para permitir que um aplicativo registre um receptor de mensagem de log.
Um aplicativo pode registrar um
LogMessageReceiver
chamando a função acima. Você terá que tomar algumas decisões políticas em relação à propriedade dos registradosLogMessageReceiver
. Se a biblioteca se apropriar do receptor, ele deve serdelete
o ponteiro. Se a biblioteca não se apropria do receptor, o aplicativo deve cuidar dodelete
receptor.O uso de uma classe como receptor de mensagem de log permite que o
user_data
bit seja omitido,registerLogMessageReceiver
pois o subtipo deLogMessageReceiver
é livre para armazenar quaisquer dados úteis para seu funcionamento. Não precisa receber dados adicionais do usuário nareceive
função.A partir daí, pode ficar mais complexo, dependendo de quão sofisticado é o seu mecanismo de registro.
Por exemplo, você pode ter vários níveis de log: Conciso, Normal, Detalhado ou LoggingLevel1, LoggingLevel2, ..., LoggingLevelN.
Nesse caso, você deverá permitir que o aplicativo controle o nível de log que deseja usar.
Há um conjunto interminável de opções quando você decide ir além do mecanismo simplista de registro. Não faz sentido se aprofundar neles aqui.
fonte
Eu diria que você deve repensar a necessidade de ter o log associado à sua biblioteca; Especialmente para C ++, onde não há interface de logger padrão.
Aplicativos diferentes têm políticas diferentes em relação ao log. Uma biblioteca deve ser independente de política.
O objetivo de uma biblioteca é fornecer um serviço e, de preferência, indicar se uma solicitação para esse serviço foi bem-sucedida ou falhou; idealmente, com uma indicação de por que [falhou] por meio de erro, código de retorno, exceção ... Se o seu desejo de registrar for porque uma função fornecida pode falhar em vários locais, você pode estar tentando fazer muito em uma função. Talvez não , mas considere a possibilidade.
fonte
[...] answer can be “don’t do that" [...]
.É fácil escrever uma biblioteca que faça interface facilmente com o sistema de log do aplicativo host, desde que você saiba o que é esse sistema. Quando há várias possibilidades para isso, é mais difícil, e você fica constrangido pela tirania do menor denominador comum. Você poderia ter uma camada de compatibilidade que adapta sua biblioteca aos vários sistemas de log, mas agora não pode confiar em algum recurso exclusivo e útil que apenas um sistema possui. Se o usuário final tiver o outro sistema, esse recurso exclusivo útil não estará lá.
No mundo .Net, existem (pelo menos) dois sistemas de log comumente usados, log4net e NLog. Ambos são semelhantes, mas não idênticos. Eu já estava usando o log4net e comecei a usar o NHibernate (uma biblioteca ORM). Felizmente, ele usa o log4net internamente, por isso foi fácil adicioná-lo a um projeto. (E aqui eu discordo da resposta de @ Daniel: às vezes é tremendamente útil para o NHibernate registrar sua atividade detalhadamente no mesmo sistema que estou usando em outros lugares, sem trabalho extra.) Se eu já investi no NLog, isso significa que Eu mudo ou tenho um projeto que usa ambos.
Pelo menos com o log, geralmente há a opção de criar um aplicativo de mensagem personalizado que você pode configurar para encaminhar uma mensagem registrada em um sistema para outro sistema de log. Portanto, se você já estava usando o NLog e realmente queria continuar com ele, mas também o NHibernate, eu poderia cerrar os dentes e escrever um aplicativo de log4net que encaminha cada mensagem para o NLog. Seria fácil se as APIs fossem semelhantes.
As alternativas são realmente escolher o melhor sistema para suas necessidades disponível e usá-lo sem reservas, ou ter algum tipo de camada adaptadora, que está sujeita ao menor problema de denominador comum. O que não é uma resposta muito satisfatória.
fonte