Log como decorador vs. Injeção de dependência - e se eu precisar fazer logon na classe?

(Originalmente fiz essa pergunta emeste comentário, mas Mark Seemann me pediu para criar uma nova pergunta.)

Estou iniciando um novo aplicativo (.NET Core, se isso importa) e agora estou tentando decidir como exatamente fazer o log.

O consenso geral parece ser que o registro é uma preocupação transversal; portanto, o registrador não deve ser injetado diretamente na classe que deveria registrar.

Muitas vezes, há um exemplo como a seguinte classe de comonão para fazer isso:

public class BadExample : IExample
{
    private readonly ILogger logger;

    public BadExample(ILogger logger)
    {
        this.logger = logger;
    }

    public void DoStuff()
    {
        try
        {
            // do the important stuff here
        }
        catch (Exception e)
        {
            this.logger.Error(e.ToString());
        }
    }
}

Em vez disso, a classe com lógica de negócios não deve saber sobre o criador de logs (SRP) e deve haver uma classe separada que faça o log:

public class BetterExample : IExample
{
    public void DoStuff()
    {
        // do the important stuff here
    }
}

public class LoggingBetterExample : IExample
{
    private readonly IExample betterExample;
    private readonly ILogger logger;

    public LoggingBetterExample(IExample betterExample, ILogger logger)
    {
        this.betterExample = betterExample;
        this.logger = logger;
    }

    public void DoStuff()
    {
        try
        {
            this.betterExample.DoStuff();
        }
        catch (Exception e)
        {
            this.logger.Error(e.ToString());
        }
    }
}

Sempre que umIExample for necessário, o contêiner de DI retorna uma instância deLoggingBetterExample, que usaBetterExample (que contém a lógica de negócios real) sob o capô.

Algumas fontes para essa abordagem:

Publicações de blog deMark Seemann:

Instrumentação com Decoradores e InterceptoresA injeção de dependência é um acoplamento fraco

Postagem no blog e resposta SO porSteven:

Enquanto isso ... no lado de comando da minha arquiteturaWindsor - puxando objetos temporários do contêinerMinha pergunta:

Obviamente, oLoggingBetterExample A abordagem funciona apenas desde que o log possa ser feito fora da classe real.
(como no exemplo acima: capture todas as exceções lançadas porBetterExample de fora)

Meu problema é que eu gostaria de registrar outras coisas dentro da classe real.
Mark Seemannsuspeito aqui que, se alguém precisar fazer isso, talvez o método em questão esteja fazendo muito.

Como eu disse antes, estou na fase de planejamento de um novo aplicativo, então não tenho muito código para mostrar, mas o caso de uso que estou pensando agora é algo como isto:

Meu aplicativo terá um arquivo de configuração com alguns valores opcionais.
O usuário pode decidir omitir os valores opcionais, mas é uma decisão importante fazer isso.
Então, eu gostaria de registrar um aviso quando alguns valores opcionais estiverem ausentes, caso isso ocorra por erro.
(omitir os valores é perfeitamente bom, então não posso simplesmente lançar uma exceção e parar)

Isso significa que terei uma classe que lê valores de configuração e precisa fazer algo assim (pseudocódigo):

var config = ReadConfigValues("path/to/config.file");

if (config.OptionalValue == null)
{
    logger.Warn("Optional value not set!");
}

Não importa seReadConfigValues está nesta classe ou em outra, não acho que essa classe viole o SRP.

Quando não consigo fazer logoff fora da classe real usando um decorador, existe uma solução melhor do que injetar o logger?

Eu sei que poderia ler o arquivo de configuração na classe interna, mas verifique os valores (e registre o aviso) no decorador. Mas a verificação da IMO do valor é lógica de negócios e não infraestrutura, portanto, para mim, ela pertence à mesma classe em que o arquivo de configuração é lido.

questionAnswers(1)

yourAnswerToTheQuestion