1

C# does not support C/C++ macros. There are many Q&A related to this in StackOverflow but I couldn't find answer to specific question.

Let say I am using code like one suggested at http://logging.apache.org/log4net/release/faq.html:

if(log.IsDebugEnabled) 
{
    log.Debug("Entry number: " + i + " is " + entry[i]);
}

This way to enable/disable logging has obvious drawbacks. In C++ I would use a macro like LOG(expr) to improve code readability and to have better control on 'if(log.IsDebugEnabled)' part of code (which is probably repeated thousand times in a project).

How can I achieve LOG(expr) - like result in C#?

Thanks!

Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
mvidelgauz
  • 2,176
  • 1
  • 16
  • 23
  • 1
    You can try aspect oriented programming see [PostSharp](http://www.sharpcrafters.com/solutions/logging#). – Rafal Oct 25 '12 at 08:06
  • Move IsDebugEnabled test to the logger Debug function. – Alex F Oct 25 '12 at 08:08
  • 2
    And just because it might not be obvious from reading your question, what you're looking for is a way to toggle the logger on and off **at runtime**, and to ensure that the argument to `log.Debug` is not evaluated *at all* if logging is disabled, correct? – jalf Oct 25 '12 at 08:11
  • AOP in itself won't solve the problem of not executing computational expensive expressions when e.g., Debug is disabled. – Polyfun Oct 25 '12 at 08:39

6 Answers6

3

You can't.... Because C# doesn't have macros!

Of course you could do something like this instead:

Define log function which takes a lambda

void Log(Func<string> f) {
  if(log.IsDebugEnabled) 
  {
    log.Debug(f());
  }
}

and call it like this when you want to log something:

Log(()=>"Entry number: " + i + " is " + entry[i]);

This way, the argument is only evaluated if logging is enabled, which I assume is what you wanted to achieve.

(My C# is a bit rusty, so the syntax might be slightly off)

jalf
  • 243,077
  • 51
  • 345
  • 550
  • 3
    To prevent the argument from being evaluated if logging is disabled. Suppose you want to log the result of a very expensive function call. That very expensive function shouldn't be called if logging is disabled. – jalf Oct 25 '12 at 08:12
  • Ah ... I noticed the part of the documentation he is talking about now. _"If you are worried about speed"_ ... I didn't realize he was worried about speed. – Steven Jeuris Oct 25 '12 at 08:14
  • Yeah, the question isn't entirely clear, but as I read it, he wants to be able to avoid evaluating the log expression entirely if logging is disabled, *and* have the ability to enable/disable logging at runtime – jalf Oct 25 '12 at 08:17
  • Should the call not be Log(()=>"Entry number: " + i + " is " + entry[i]); – ilias Oct 25 '12 at 08:31
  • @ilias Nah, it should be nameOfObject.Log(()=>"Entry number: " + i + " is " + entry[i]); because it isn't static. – Theraot Oct 25 '12 at 08:34
  • Oops, fixed so the function at least has the same name when it's defined and called. Thanks @ilias – jalf Oct 25 '12 at 08:44
  • Thank you jalf for your answer! It is very useful reminder about lamdas for me! ))). And probably the best solution! – mvidelgauz Oct 25 '12 at 19:43
1

You can define it as a method and mark it as debugger conditional:

internal static SomeClass
{
    [Conditional("DEBUG")] 
    internal static void Log(string expr)
    {
     //...
    }
}

The compiler will remove this method and any call or reference to this method if the compiler constant "DEBUG" is not defined. Yes, it works with others too. It is similar to:

internal static SomeClass
{
    #if DEBUG
    internal static void Log(string expr)
    {
     //...
    }
    #endif
}

But using that will not remove the references to this method, so you would have to wrap those too.


Yes, this only allows to disable log at compile time. jalf has a better approach to handle this at runtime.

Community
  • 1
  • 1
Theraot
  • 31,890
  • 5
  • 57
  • 86
  • Still relevant information since the OP's question isn't that clear. – Steven Jeuris Oct 25 '12 at 08:28
  • Thank you Theraot! I didn't know about [Conditional("DEBUG")] and I'll try it. Will it eliminate unnecessarily code that constructs string argument? Jalf, I understand that this is a different approach, but also can be useful if I want to disable logging at compile time, right? – mvidelgauz Oct 25 '12 at 19:47
  • @mvidelgauz yes, anything you pass to a method with the Conditonal attribute will be removed by the compiler if it is only used there. This is done even without code optimization. PS: why not use string.Format? – Theraot Oct 25 '12 at 21:38
1

Use new log4net XXXXFormat methods instead. They are implemented this way:

public virtual void DebugFormat(string format, params object[] args)
{
    if (this.IsDebugEnabled)
    {
       this.Logger.Log(declaringType, m_levelDebug, String.Format(format, args));
    }
}

Also you will have power of String.Format instead of simple concatenation of strings

log.DebugFormat("Entry number: {0} is {1}", i, entry[i]);
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • Thanks, this also can be a solution, although I dislike format. In my C++ code that uses log4cxx I use stream operations ( << ). So in C# my preference probably will be + (at the end of story I need to construct a single string to be written to logger) – mvidelgauz Oct 25 '12 at 19:53
  • 1
    @mvidelgauz in C# using `+` produces many strings in memory. I.e. `a + b + c + d` will produce `ab`, `abc`, and `abcd` strings. With `String.Format` working with strings is efficient and no additional strings created – Sergey Berezovskiy Oct 25 '12 at 23:03
0

As an idea, you can create class with one static method, which will accept string expression.

besworland
  • 747
  • 2
  • 7
  • 17
0

... part of code (which is probably repeated thousand times in a project).

Whenever you are thinking something like that, the first thing that should pop up in your head is encapsulate, encapsulate, encapsulate!

You could simply create your own Logger class which has a IsDebugEnabled property and your own Log method.

void Log( string message )
{
  if ( IsDebugEnabled ) 
  {
     log.Debug( message );
  }
}

Probably better to look around for existing logger libraries, which probably already support all the functionality you need.

E.g. log4net

Steven Jeuris
  • 18,274
  • 9
  • 70
  • 161
  • 3
    As I understand the question, this only solves half of the problem: he wants to be able to toggle logging at runtime (which your example allows), but *also* be able to avoid evaluating `message` at all if logging is disabled. (for example, say I call your function like this: `Log(expensiveFunction())`, and I don't want `expensiveFunction` to be called at all if logging is disabled. :) – jalf Oct 25 '12 at 08:21
  • 1
    Good point, I'll just keep this answer around here as yours already addresses that part. :) I'm sure the OP has plenty of information available now. – Steven Jeuris Oct 25 '12 at 08:23
-1

Aspect-oriented programming is what you want, because things like logging and caching has little to do with your business logic. So they should be separated.

There are many frameworks available, take a look at postsharp(http://www.sharpcrafters.com/), or snap (http://www.simpleaspects.com/). If you use one of the supported dependency injection framework, maybe snap is good choice.

UPDATE: if you are using log4net, you should be able to turn it off from the config file, <log4net threshold="OFF" /> So you should have a debug config and a release config

user1736525
  • 1,119
  • 1
  • 10
  • 15
  • AOP is relevant, but not sure whether it would help out the OP in his particular scenario, then we'd need more information. – Steven Jeuris Oct 25 '12 at 08:30
  • Yeah, you are right, but having LOG() everywhere is just ugly and distracting. I would refactor and use aop. – user1736525 Oct 25 '12 at 08:52