Каков наиболее эффективный способ регистрации имени класса и метода?

В основном я хочу написать клиентский регистратор/трассировщик, который также регистрирует имя класса и метода метода, вызывающего регистратор/трассировщик. Это должно быть быстро, чтобы не влияло на производительность приложения, строго типизировано и чисто. У кого-нибудь есть хорошие идеи?

Вот несколько, которые у меня были, но я беспокоюсь о производительности из-за отражения:

  • StackTrace/StackFrame (слишком много накладных расходов?)

  • MethodInfo.GetCurrentMethod() (все еще слишком медленный? И не очень чистый)

  • передать метод в качестве делегата (С# может сделать это неявно, и у меня есть доступ к MethodInfo, но не очень чистый)

Я ценю любые комментарии.

ОБНОВЛЕНИЕ:

Мне понравилась чистота StackFrame, поэтому я задал более конкретный вопрос здесь относительно производительности StackFrame и Я получил действительно хороший ответ, включая тесты производительности. Получается, что MethodInfo.GetCurrentMethod() является самым быстрым (занимает около 1 микросекунды на моем компьютере), а new StackFrame(1) может быть запрошен по запросу (занимает около 15-20 микросекунд на моем компьютере). Я выбросил метод как опцию делегата, так как это слишком беспорядочно, когда метод имеет несколько параметров.

ЗАКЛЮЧЕНИЕ:

Я рассмотрел все варианты, и лучше всего написать собственный подключаемый модуль для PostSharp, который вводит имя метода в виде строки в MSIL во время компиляции при применении к нему пользовательского атрибута, такого как [Trace]. На мой взгляд, это самый быстрый и ремонтопригодный вариант. Это даже позволяет гораздо больше вещей, таких как передача имен параметров и аргументов без какого-либо отражения, а также автоматический захват и регистрация исключений. Этот мой связанный вопрос для получения дополнительной информации.


person Community    schedule 28.08.2009    source источник
comment
могу я спросить, почему вы не хотите использовать log4net?   -  person nWorx    schedule 29.08.2009
comment
Я не люблю слишком полагаться на другие компоненты. А также в случае с log4net это слишком сложно.   -  person    schedule 29.08.2009
comment
Германн, в нашей отрасли этот синдром обычно называют не изобретенным здесь синдромом, и его лучше избегать. Вот ссылка на Википедию с дополнительной информацией об этом недуге: en.wikipedia.org/wiki/Not_Invented_Here   -  person Anderson Imes    schedule 01.09.2009
comment
Я знаю об этом синдроме, но в моем случае для этого есть практические причины. Меня слишком много раз кусали, используя другие компоненты, и через некоторое время я понял, что они просто не отвечают всем требованиям.   -  person    schedule 01.09.2009
comment
Кстати, у Джоэла Спольски есть отличная статья в защиту синдрома «не здесь изобретено»: joelonsoftware.com/articles/ туман0000000007.html   -  person    schedule 01.09.2009
comment
Компания Джоэла Спольски также изобрела собственный язык программирования. Я не уверен, что это хороший пример. Вам действительно следует подумать об использовании письменной библиотеки. Ведение журнала — это решаемая проблема, и я думаю, что изобретать велосипед — это, вероятно, не лучшее использование вашего времени. Просто мои 2 цента... примите это за то, что оно стоит.   -  person Anderson Imes    schedule 01.09.2009
comment
Кроме того, вы заметите, что его аргумент заключается в том, что вы должны делать это, если это является основой вашего бизнеса. Если ваш бизнес занимается лесозаготовками (например, если бы ваша компания называлась We Log Good, Inc.), я бы сказал, действуйте.   -  person Anderson Imes    schedule 01.09.2009
comment
Я думаю, что аргумент, что я думаю, что я могу сделать это лучше, чем кто-либо другой, применим ко мне в отношении статьи Джоэла ;-). На самом деле, рассматривая все варианты, я выбираю PostSharp и собственное ведение журнала в стиле АОП. Мало того, что это быстрее, чем что-либо еще (введение информации во время компиляции), я могу регистрировать информацию, которую другие решения не могут (например, значения параметров, переданные в методы, вход и выход метода), и это очень чисто и легко поддерживать, поскольку АОП использовал. И хотя это может занять день или два, в долгосрочной перспективе оно того стоит, поскольку ведение журнала используется повсеместно.   -  person    schedule 02.09.2009


Ответы (6)


Проще говоря, самый быстрый способ — сделать это статически во время компиляции:

public void DoSomething()
{
    TraceCall("DoSomething");
} 

public void TraceCall(string methodName)
{
    if (!tracingEnabled) { return; }
    // Log the call
}

Тогда вопрос по сути такой же, как и ваш предыдущий вопрос; как лучше всего это сделать в ремонтопригодном и точном фасте? Варианты времени выполнения, которые мы обсуждали ранее, оказывают относительно серьезное влияние на производительность, но компромисс заключается в том, что их можно легко реализовать.

Как упоминалось в предыдущей теме, это начинает склоняться к аспектно-ориентированному программированию; PostSharp — одна из таких библиотек, которая может помочь найти баланс между статически скомпилированной производительностью и простым обслуживанием.

person STW    schedule 28.08.2009
comment
Ты прав. Я надеюсь, что для реализации этого с помощью PostSharp не потребуется слишком много усилий. Я думаю, это зависит от потребностей каждого человека, но я бы не назвал 20 микросекунд серьезным ударом по производительности. - person ; 29.08.2009
comment
@Hermann: если вы не пишете что-то, что должно обрабатывать пропускную способность Google, 20 мс совершенно незначительны. Тесты, которые мы провели в другом вопросе, показали, что если вам нужно инициировать вызовы журналов для 100 000 методов, это добавит — самое большее — 1,5 секунды. Несмотря на то, что некоторые методы значительно быстрее других, все они в конечном счете являются маргинальными. - person STW; 29.08.2009

Самый быстрый способ сделать это во время компиляции, это правда, но самый удобный способ сделать это в .NET — использовать CallerMemberName, CallerFilePath и другие атрибуты службы компилятора, представленные в .NET 4.5:

public void TraceMethodCall([CallerMemberName]string methodName)
{
}
person Sergey Baranchenkov    schedule 19.02.2016

Когда вы уже находитесь внутри метода (или если вы перехватываете метод), я не думаю, что вы можете добиться большего успеха, чем MethodInfo.GetCurrentMethod, который не слишком быстр, если вы хотите оставаться типобезопасным.

Поскольку вы хотите сделать это для ведения журнала, не рассматривали ли вы возможность сделать это в асинхронной операции, чтобы не замедлять основной поток?

В зависимости от нагрузки на ваше приложение, это может помочь или не помочь.

person Mark Seemann    schedule 28.08.2009
comment
Да, я тоже думал об этом. Но что происходит, когда приложение занято? Разве это не отложит ведение журнала и не будет ли оно потеряно при сбое приложения? - person ; 28.08.2009
comment
Зависит от того, как вы реализуете асинхронную операцию. Если просто закинуть в ThreadPool, то да, вы правы, и порядок не гарантируется. Однако вы также можете записать его в MSMQ, что обеспечит упорядоченное и надежное ведение журнала. - person Mark Seemann; 28.08.2009
comment
Параметр async только облегчит фактическую запись в журнал (что все еще может быть полезно). Фактический вызов GetCurrentMethod по-прежнему приведет к снижению производительности основного потока. - person STW; 29.08.2009
comment
@Yoooder: Да, это то, что я имел в виду под зависимостью от уже загруженного вашего приложения. IIRC, MethodInfo.GetCurrentMethod в основном использует ЦП, поэтому, если общее узкое место находится где-то еще (например, дисковый ввод-вывод), это поможет. Если ЦП является узким местом, то его не будет, но тогда я не могу придумать ничего, что могло бы... - person Mark Seemann; 29.08.2009

Лучший способ сделать это — во время компиляции: C++ традиционно использовал символы препроцессора ____FILE____ и ____LINE____ в макросах для этого. К сожалению, в настоящее время компилятор C# не предлагает макросы или эквиваленты ____FILE____ и ____LINE____.

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

person Stu Mackellar    schedule 28.08.2009

Я думаю, что Log4PostSharp отлично справится с этой задачей.

person Gael Fraiteur    schedule 28.08.2009
comment
Я не хочу использовать другой регистратор, особенно не основанный на log4net, но мне _действительно) нравится идея внедрения кода MSIL. Я должен исследовать, могу ли я использовать это как-то. - person ; 28.08.2009
comment
Чтение ответа Стю - это займет ок. один час на разработку эквивалента FILE и LINE с использованием PostSharp (или TYPE и METHOD). Ну, мне 1 час. - person Gael Fraiteur; 28.08.2009
comment
Я посмотрю на PostSharp. Спасибо, Гаэль! - person ; 28.08.2009
comment
Если правильно настроить, то не так уж и медленно. Но вы можете пострадать, если у вас куча мелких проектов. PostSharp лучше подходит для больших проектов (поскольку накладные расходы на каждый проект значительны). Обязательно используйте 1.5 RTM, установленную с помощью установщика. - person Gael Fraiteur; 28.08.2009
comment
Просмотрев все варианты, я выберу собственный плагин для PostSharp. Спасибо всем! - person ; 31.08.2009

Если вас беспокоит скорость, фактическое сообщение журнала должно быть создано с использованием отслеживания событий для Окна. ETW — это высокоскоростная библиотека ведения журналов, реализованная в виде драйвера, доступного как в режиме ядра, так и в пользовательском режиме. Трассировку можно динамически включать и выключать. Ведение журнала добавляет очень мало накладных расходов для приложения. Я использовал ETW для реализации трассировки в серверных приложениях с высокой пропускной способностью. Проверьте NTrace.

person Steve Severance    schedule 29.08.2009