... но и любой другой логгер.
Традиционно Photon Server SDK поставляется с log4net. Но это не значит что все им должны пользоваться. Пользоваться можно практически любым логгером. Всё что нужно это создать свою сборку-адаптер, которая будет содержать класс прокси и фабрику для него.
Для примера возьмём модный нынче Serilog. Я с ним не знаком, так что возможно что-то будет сделано не лучшим методом или неправильно.
И так приступим.
Первое, что нам нужно это создать сборку для вашего адаптера.
В SDK 4.0 интерфейсы ExitGames.Logging.ILogger и ExitGames.Logging.ILoggerFactory находятся в ExitGamesLibs.dll. В SDK 5.0 их вынесли в ExitGames.Logging.dll. ExitGames.Logging находится в nuget пакете с тем же именем. Эти библиотеки должны быть добавлены в зависимости.
Дальше мы создаём класс-прокси для логгера. Я не будут приводить весь его код, чтобы не раздувать пример.
class SerilogLogger : ILogger
{
private readonly global::Serilog.ILogger logger;
public bool IsDebugEnabled => this.logger.IsEnabled(LogEventLevel.Debug);
// not sure whether this is right implementation
public string Name => this.logger.ToString();
............................................................
public SerilogLogger(global::Serilog.ILogger logger)
{
this.logger = logger;
}
public void Debug(object message)
{
if (message is string str)
{
this.logger.Debug(str);
return;
}
throw new NotSupportedException("only strings are allowed");
}
public void Debug(object message, Exception exception)
{
if (message is string str)
{
this.logger.Debug(exception, str);
return;
}
throw new NotSupportedException("only strings are allowed");
}
public void DebugFormat(string format, params object[] args)
{
this.logger.Debug(format, args);
}
public void DebugFormat(IFormatProvider formatProvider, string format, params object[] args)
{
this.logger.Debug(format, args);
}
......................................................
Следующее, что нам нужно это класс фабрики.
public class SerilogLoggerFactory : ILoggerFactory
{
/// <summary>
/// Provides a static singleton instance for the <see cref="SerilogLoggerFactory"/> class.
/// </summary>
public static readonly SerilogLoggerFactory Instance = new SerilogLoggerFactory();
public ILogger CreateLogger(string name)
{
var serilogLogger = Log.ForContext(Constants.SourceContextPropertyName, name);
return new SerilogLogger(serilogLogger);
}
}
Последний штрих это установка нашей фабрики
ExitGames.Logging.LogManager.SetLoggerFactory(SerilogLoggerFactory.Instance);
Сделать это необходимо перед тем как будут инициализироваться логгеры в Photon.SocketServer.dll. Для этого лучше всего подходит статический конструктор вашего photon-приложения
Что ещё нужно знать про логгинг
Используйте if (log.IsDebugEnabled)
Хотя я соглашусь с тем, что это очевидно, но я сам так не делал, когда только столкнулся с фотоном. Обычно отладочных сообщений в коде много и, если их не накрывать таким if-ом, они могут сильно просадить скорость выполнения. Для Info это уже решать самим. Warning обычно всегда включён поэтому можно этот трюк не использовать.
Защищённое логгирование
Много раз мы наступали на грабли, что какой-нибудь варнинг начинает валиться в лог сотнями сообщений в секунду. Эта ситуация неприемлема по многим причинам и производительность не самая последняя из них. Поэтому изобрели LogCountGuard. Ему задают интервал и количество сообщений, которое он может вывести за этот интервал. Остальные не выводятся, а только копится счётчик пропущенных. Когда интервал времени заканчивается сообщение снова выводится с указанием числа пропущенных до него. Вот тут есть тонкость в реализации. Если интервал закончился, а новые сообщения не поступали, то пропущенные выводится не будут
Для того, чтобы этим инструментом было удобно пользоваться, были добавлены методы расширения. Теперь это всё выглядит следующим образом. Допустим нам надо, чтобы какое-то сообщение не появлялось чаще, чем 10 раз в минут. Необходимо сделать следующее,
// объявление
private static readonly LogCountGuard msgLogGuard = new LogCountGuard(new TimeSpan(0, 0, 6), 1);
// использование
log.Warn(msgLogGuard, "message");
Заключение
В заключении хочется пожелать всем успеха в использовании Photon Server SDK и пусть отсутствие вашего любимого логгера вас не пугает.
lair
Это же на уровне логгера должно делаться вроде как (и Serilog это прекрасно делает, кстати). Если это надо делать в прикладном коде, с логированием что-то фундаментально не так.
shvez Автор
Ну у вас же идёт вызов log.DebugFormat("...", value1, value2, value3). Он же не бесплатно делается. он же собирает параметры в массив, который надо создать.
Всё что может сделать serilog это не делать свою обработку, но стоимость простого вызова метода никто не отменял
lair
Конкретно взятый серилог эту проблему решает так: https://github.com/serilog/serilog/blob/v2.10.0/src/Serilog/Core/Logger.cs#L229
Если инфраструктура фотона так не делает, значит одно из двух: или эта потеря производительности на самом деле не важна (как в
Microsoft.Extensions.Logging
; еще точнее, там это просто сделано другим образом), или они не подумали, а должны были.shvez Автор
ну смотрите, там решение для трёх параметров, 4 уже будут стоить дороже. Так как решение действительно изящное и нужное, я думаю, что его можно будет добавить, но тем не менее в общем случае я бы всё равно использовал предварительную проверку.
В своё время, когда я столкнулся с этим прирост производительности был заметен даже на глаз. Но решение было использовать выше указанную проверку.
lair
Необходимость постоянно писать избыточный код плохо говорит о фреймворке. Посмотрите, как сделано в
M.E.L
.shvez Автор
Спасибо, посмотрю
shvez Автор
Вопрос у меня. Вы используете в логгировании интерполяцию строк?
lair
Нет, конечно. Зачем?
shvez Автор
нахожу это сильно удобнее чем {0}...{1}. Особенно, если надо отредактировать сообщение
lair
Так я и
string.Format("{0}...")
не использую, у меня структурное логгирование.shvez Автор
ну т.е. вы только serilog используете, так?
Где на практики плюсы. Я бегло посомтрел в файл, там в общем-то тоже самое, как если бы я log4net пользовался
lair
Нет, конечно. Еще я использую
Microsoft.Extensions.Logging
.На практике плюсы там, где вы пишете в структурированное хранилище, начиная с JSON-файлов, и заканчивая AWS CloudWatch, Elasticsearch и Seq. А потом при анализе логов просто говорите "ага, хочу все логи от такого-то запроса для такого-то компонента для такого-то токена".
… в обычный текстовый файл, наверное? Нет, там вы плюсов не найдете.
netpilgrim
Интересно почему делегаты не завезли как в NLog
_logger.Info(() => string.Format("msg", myParams));
lair
Потому что еще не факт, что создание (и потенциальный вызов) делегата дешевле, чем создание массива.
shvez Автор
Тут надо понимать, что когда фотон начали разрабатывать, из библиотек, которым можно было бы доверять был только log4net ну и может быть nlog в своих первых версиях. выбрали log4net. Апач, опер сорс, сообщество разработчиков. nlog писал никому неизвестный парнишка. кто ж знал тогда, что log4net перестанет развиваться.
Базовый интерфейс логгера был сделан на основе log4net. И его в общем-то не трогали, потому что свою работу он делал.
ну и согласен с lair, что эффективность такого подхода сомнительна