Log4Net 如何同时使用 ErrorFormat 和 Error

Log4Net how to use both the ErrorFormat and the Error at the same time

log4net中有2个非常不错的函数:

Log.Error("Message", myException);

Log.ErrorFormat("Message {0}", CustomerId);

生产日志记录在输出日志文件中非常漂亮。异常一个很好,因为它很好地打印堆栈跟踪和所有异常详细信息,格式一个很好,因为它允许我指定导致失败的参数。有没有 "best of both worlds" 方法,或者有什么方法可以做到?

Log.ErrorFormatEx("Message {0}", CustomerId, myException)

编辑:我检查了可能重复的问题,但它们的格式很糟糕,不要完全这样问。我在寻找扩展方法或现有方法

您可以创建一个扩展方法:

namespace log4net.Core
{
    public class Log4NetExtensions 
    {    
         public static void ErrorFormatEx(this ILog logger, string format, Exception exception, params object[] args) 
         {
               logger.Error(string.Format(format, args), exception);
         }
    }
}

然后您就可以像使用任何其他 Log4Net 方法一样使用它了:

Log.ErrorFormatEx("Message {0}", exception, CustomerId);

知道这个问题已经得到解答,但只是针对可能会发现此替代方案有用的其他用户。我创建了一个 ILog 接口和一个 Log Class 到 "centralize" 我的 log4net 方法和逻辑。我还为 "Error" 方法创建了多个重载。

ILog.cs

public interface ILog
{
    void Error(Exception exception);
    void Error(string customMessage, Exception exception);
    void Error(string format, Exception exception, params object[] args);
    void Warn(Exception exception);
    void Info(string message);
}

Log.cs

public class Log : ILog
{
    public void Error(Exception exception)
    {
        log4net.ILog logger = log4net.LogManager.GetLogger(exception.TargetSite.DeclaringType);
        logger.Error(exception.GetBaseException().Message, exception);
    }

    public void Error(string customMessage, Exception exception)
    {
        log4net.ILog logger = log4net.LogManager.GetLogger(exception.TargetSite.DeclaringType);
        logger.Error(customMessage, exception);
    }

    public void Error(string format, Exception exception, params object[] args)
    {
        log4net.ILog logger = log4net.LogManager.GetLogger(exception.TargetSite.DeclaringType);
        logger.Error(string.Format(format, args), exception);
    }

    public void Warn(Exception exception)
    {
        log4net.ILog logger = log4net.LogManager.GetLogger(exception.TargetSite.DeclaringType);
        logger.Warn(exception.GetBaseException().Message, exception);
    }

    public void Info(string message)
    {
        log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        logger.Info(message);
    }
}

用法示例

public MyClass DeserializeJsonFile(string path)
{
    try
    {
        using (StreamReader r = new StreamReader(path))
        {
            string json = r.ReadToEnd();
            return JsonConvert.DeserializeObject<MyClass>(json);
        }
    }
    catch (Exception ex)
    {       
        this.log.Error("Error deserializing  jsonfile. FilePath: {0}", ex, path);
        return null;                
    }
}

现在用字符串插值解决了这个问题:

Log.Error($"Message {CustomerId}", myException);

最简单的是使用:

Log.Error($"Message {CustomerId}, {myException}");

就我而言,我需要临时使用两个记录器(Log4Net 和 Serilog)。我没有写出很好的代码,因为解决方案是临时的。下面是我用的。

    public void Error(Exception exception, string messageTemplate, params object[] propertyValues)
    {
        _messageTemplateProcessor.Process(messageTemplate, propertyValues, out var parsedMessage);
        Log.Error(parsedMessage, exception); //Log4Net
        _logger.Error(exception, messageTemplate, propertyValues); //Serilog
    }



using System.Collections.Generic;

namespace CommonServices
{
    class MessageTemplateProcessor
    {
        public void Process(string messageTemplate, object[] messageTemplateParameters, out string parsedMessage)
        {
            var blocks = Parse(messageTemplate);
            parsedMessage = string.Empty;

            var i = 0;
            foreach (var block in blocks)
            {
                if (!block.IsProperty)
                {
                    parsedMessage += block.Text;
                    continue;
                }

                parsedMessage += messageTemplateParameters[i];
                i++;
            }
        }


        private IEnumerable<Block> Parse(string messageTemplate)
        {
            var blocks = new List<Block>();
            
            if (string.IsNullOrEmpty(messageTemplate))
            {
                return blocks;
            }
            
            var stack = new Stack<char>();
            var word = string.Empty;

            foreach (var ch in messageTemplate)
            {
                if (ch == '{' && stack.Count == 0)
                {
                    if (!string.IsNullOrEmpty(word))
                    {
                        blocks.Add(new Block(word, false));
                        word = string.Empty;
                    }
                    
                    stack.Push(ch);
                    word += ch;
                    continue;
                }
                
                word += ch;

                if ((ch != '{' && ch != '}') || (ch == '}' && stack.Count == 0))
                {
                    continue;
                }

                if (ch == '{' && stack.Count > 0)
                {
                    stack.Push(ch);
                    continue;
                }

                if (ch == '}' && stack.Count > 0)
                {
                    if (stack.Count == 1)
                    {
                        blocks.Add(new Block(word, true));
                        word = string.Empty;
                        stack.Pop();
                        continue;
                    }

                    if (stack.Peek() == '{')
                    {
                        stack.Pop();
                    }
                    else
                    {
                        stack.Push(ch);
                    }
                }
            }

            if (!string.IsNullOrEmpty(word))
            {
                blocks.Add(new Block(word, false));
            }


            return blocks;
        }

        class Block
        {
            public Block(string text, bool isProperty)
            {
                Text = text;
                IsProperty = isProperty;
            }
            
            public string Text { get; }
            public bool IsProperty { get; }
        }
    }
}