Как использовать полное имя класса в качестве имени регистратора с NLog?

У меня есть общий класс:

public class GenericClass<A>
{
    private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

    public void Blah(A a)
    {
        Logger.Info("Test log");
    }
}

Используется с простой конфигурацией NLog:

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <targets>
    <target name="console" xsi:type="ColoredConsole" />
  </targets>

  <rules>
    <logger name="*" minlevel="Trace" writeTo="console" />
  </rules>
</nlog>

Я хотел бы, чтобы мой вывод журнала выглядел примерно так:

...|INFO|NLogTests.GenericClass<System.String>|Test log

Вместо этого я вижу следующее:

...|INFO|NLogTests.GenericClass`1|Test log

Как я могу заставить регистратор использовать полное имя типа вместо основного имени, которое отображает только количество общих параметров?


person emertechie    schedule 18.07.2011    source источник


Ответы (2)


Вместо GetCurrentClassLogger вы можете использовать:

Logger log = LogManager.GetLogger(GenericToString(typeof(GenericClass<A>)));
…
string GenericToString(Type type) {
  var typeName = type.Namespace + "." + type.Name;
  var args = type.GetGenericArguments();
  if (args.Count() == 0) return typeName;

  typeName = typeName.Remove(typeName.LastIndexOf("`"));
  return typeName 
           + "<" 
           + string.Join(", ", args.Select(a=>GenericToString(a)))
           + ">";
}

Поместите GenericToString везде, где вы храните случайные служебные функции/методы расширения.

Отказ от ответственности: функция была взломана прямо сейчас и может не работать, привести к возгоранию вашего компьютера или вызвать облысение.

person millimoose    schedule 15.09.2011
comment
Да, я мог бы это сделать, конечно, но я не хотел каждый раз вызывать этот метод (и иметь дополнительную зависимость от сборки общих утилит). Лучшим подходом было бы, если бы NLog сделал это, поскольку я думаю, что текущий вывод не очень полезен. Я понимаю, что может быть эффективным в том, что требуется немного дополнительного отражения, но было бы неплохо, по крайней мере, иметь возможность включить его. - person emertechie; 15.09.2011
comment
@Andrew: хотя встроенная функция может быть неплохой, вам придется попросить об этом разработчиков NLog. Тем временем вы можете воспроизвести анализ трассировки стека (или скопировать и вставить его из своего источника) и создать метод расширения в LogManager. - person millimoose; 16.09.2011
comment
@AndrewSmith, вероятно, вы можете написать метод расширения, использующий дженерики, а также назвать его GetLogger. Внутри вы используете метод Inerdial для создания имени. по-своему вам не нужно ничего запоминать :] - person stijn; 14.01.2012

Если вы используете С# 6 и .NET > 4.5, то вы можете сделать изящный трюк, чтобы использовать CSharpCodeProvider:

public static class LogManager<T>
{
    public static Logger GetLogger()
    {
        return LogManager.GetLogger(typeof(T).GetFriendlyName());
    }
}

public static class TypeExtensions
{
    public static string GetFriendlyName(this Type t)
    {
        using (var provider = new CSharpCodeProvider())
        {
            var typeRef = new CodeTypeReference(t);
            return provider.GetTypeOutput(typeRef);
        }
    }
}

Затем вы можете использовать это расширение следующим образом:

var logger = LogManager<Dictionary<string, int>>.GetLogger();
logger.Info("logger name contains generic params");

И вывод будет:

2016-09-23 13:52:14.6342|INFO|System.Collections.Generic.Dictionary<string, int>|logger name contains generic params
person Valeriu Caraulean    schedule 23.09.2016