Я пишу приложение, в котором ведение журнала является частью моей фактической модели предметной области. Это инструмент автоматизации и пакетной обработки, где конечные пользователи смогут просматривать журналы задания пакетной обработки в реальном приложении, а не только файлы текстовых журналов.
Итак, моя модель домена включает в себя класс LogMessage
:
public sealed class LogMessage
{
public string Message { get; }
public DateTime TimestampUtc { get; }
public LogLevel Level { get; }
}
public enum LogLevel
{
Fatal = 5,
Error = 4,
Warn = 3,
Info = 2,
Debug = 1,
Trace = 0
}
У меня также есть класс Result
, который имеет свойство коллекции LogMessages
. Результаты могут быть сохранены и открыты из файлов с моим приложением конечными пользователями.
public class Result
{
public bool Succeeded {get; set;}
public string StatusMessage {get; set;}
public IList<LogMessage> LogMessages {get; set;}
}
Мое приложение также поддерживает сторонних разработчиков, расширяющих приложение с помощью плагинов, которые также могут писать сообщения журнала. Итак, я определил общий ILogger
интерфейс для разработчиков плагинов.
public interface ILogger
{
void Debug(string message);
void Error(string message);
void Fatal(string message);
void Info(string message);
void Log(LogLevel level, string message);
void Trace(string message);
void Warn(string message);
}
Я предоставляю экземпляр ILogger
для плагинов, который пишет в Result.LogMessages
.
public interface IPlugIn
{
Output DoSomeThing(Input in, ILogger logger);
}
Я, очевидно, также хочу иметь возможность регистрироваться из своего внутреннего кода и в конечном итоге хочу, чтобы Result.LogMessages
содержал смесь моих внутренних сообщений журнала и сообщений журнала от плагинов. Поэтому конечный пользователь, у которого возникли проблемы, может отправить мне файл результатов, который будет содержать журналы отладки как из моего внутреннего кода, так и из любых используемых плагинов.
В настоящее время у меня есть решение, работающее с использованием пользовательской цели NLog.
public class LogResultTarget : NLog.Targets.Target
{
public static Result CurrentTargetResult { get; set; }
protected override void Write(NLog.LogEventInfo logEvent)
{
if (CurrentTargetResult != null)
{
//Convert NLog logEvent to LogMessage
LogLevel level = (LogLevel)Enum.Parse(typeof(LogLevel), logEvent.Level.Name);
LogMessage lm = new LogMessage(logEvent.TimeStamp.ToUniversalTime(), level, logEvent.Message);
CurrentTargetResult.LogMessages.Add(lm);
}
}
protected override void Write(NLog.Common.AsyncLogEventInfo logEvent)
{
Write(logEvent.LogEvent);
}
}
Этот класс пересылает сообщение в Result
, присвоенное статическому свойству LogResultTarget.CurrentTargetResult
. Мой внутренний код записывается в регистраторы NLog, и у меня есть реализация ILogger
, которая также записывает в NLog.Logger
.
Это работает, но кажется действительно хрупким. Если CurrentTargetResult
не установлен правильно или не установлен на ноль, я могу получить сообщения журнала, сохраненные к результатам, к которым они не относятся. Кроме того, поскольку существует только один статический CurrentTargetResult
, я не могу поддерживать одновременную обработку нескольких результатов.
Есть ли другой / лучший способ, которым я мог бы подойти к этому? Или то, что я пытаюсь сделать в корне неправильно?