8

In log4net there are 2 very nice functions:

Log.Error("Message", myException);

and

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

The produce log records in the output log file very nicely. The exception one is nice because it prints a stack trace and all the exception details nicely, and the Format one is nice because it allows me to specifiy the parameters that caused the failure. Is there a "best of both worlds" method, or some way to make it?

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

Edit: I went through the possible duplicate questions, but they are badly formatted and don't ask it exactly like this.. I was looking for an extension method or existing method

user230910
  • 2,353
  • 2
  • 28
  • 50
  • 4
    You could use `Log.Error(string.Format("Message {0}", id), myException);` – Stefan May 07 '15 at 06:50
  • possible duplicate of [Using Log4Net.ErrorFormat how can I log the stacktrace?](http://stackoverflow.com/a/21319160/318508) – molnarm May 07 '15 at 06:51
  • 1
    I think the Format methods are sort of obsolete with the new string interpolation feature in C# 6.0. – anhoppe Mar 03 '16 at 15:10
  • yeah that is 100% true now, never use them anymore – user230910 Mar 04 '16 at 07:35
  • 1
    The `...Format()` methods are still worth using at levels lower than error as it means that the string is not actually built if there's no logger watching at that level... i.e. if you're writing a lot of messages to .DebugFormat then in production you can turn logging off for the debug level and your application doesn't have to build all the strings. Code: http://svn.apache.org/viewvc/logging/log4net/trunk/src/Core/LogImpl.cs?view=markup – Rob Church Jun 30 '16 at 14:04

5 Answers5

8

You could create an extension method:

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);
         }
    }
}

Then you can use it just like you would use any other Log4Net method:

Log.ErrorFormatEx("Message {0}", exception, CustomerId);
Loetn
  • 3,832
  • 25
  • 41
  • 3
    You should also add a logger.IsErrorEnabled clause so that you compose the string only if needed – smiron Aug 16 '16 at 10:45
4

Know this has been answered already, but just for other users who might find this alternative useful. I created a ILog Interface and a Log Class to "centralize" my log4net methods and logic. Also I created multiple overloads for the "Error" method.

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);
    }
}

Example usage

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;                
    }
}
jpgrassi
  • 5,482
  • 2
  • 36
  • 55
3

Simplest is to use:

Log.Error($"Message {CustomerId}, {myException}");
Akarsha
  • 163
  • 1
  • 7
2

This is now solved with string interpolation:

Log.Error($"Message {CustomerId}", myException);
nwinkler
  • 52,665
  • 21
  • 154
  • 168
  • 4
    except it costs cpu on the interpolation, where the format doesn't do the work if it is not being logged – user230910 Feb 18 '18 at 23:53
  • If this is a concern, you can of course wrap the logging within a check if the level is enabled - using if (Log.IsErrorEnabled) { /* error log messages */ }. – Mark Philpott Feb 20 '18 at 09:20
0

In my case, I needed to temporarily use two loggers (Log4Net & Serilog). I didn't write very good code, because the solution is temporary. Below is what I used.

    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; }
        }
    }
}