关于C#:如何打印方法名称和行号并有条件地禁用NSLog?

How to print out the method name and line number and conditionally disable NSLog?

我正在Xcode中做一个关于调试的演示,希望获得有关有效使用nslog的更多信息。

特别是,我有两个问题:

  • 有没有一种方法可以轻松地nslog当前方法的名称/行号?
  • 在编译发布代码之前,是否有一种方法可以轻松地"禁用"所有nslog?


下面是一些关于nslog的有用宏,我经常使用:

1
2
3
4
5
6
7
8
#ifdef DEBUG
#   define DLog(fmt, ...) NSLog((@"%s [Line %d]" fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#   define DLog(...)
#endif

// ALog always displays output regardless of the DEBUG setting
#define ALog(fmt, ...) NSLog((@"%s [Line %d]" fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)

dlog宏仅用于在设置调试变量时输出(调试配置的项目C标志中有-ddebug)。

ALOG将始终输出文本(如常规nslog)。

输出(例如alog(@"Hello World"))如下:

1
-[LibraryController awakeFromNib] [Line 364] Hello world


我从上面取了DLogALog,添加了ULog,它发出了UIAlertView的信息。

总结:

  • 只有在设置调试变量时,DLog才会像NSLog那样输出。
  • ALog总是像NSLog那样输出。
  • 只有在设置了调试变量时,ULog才会显示UIAlertView。< BR>
1
2
3
4
5
6
7
8
9
10
11
12
#ifdef DEBUG
#   define DLog(fmt, ...) NSLog((@"%s [Line %d]" fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#   define DLog(...)
#endif
#define ALog(fmt, ...) NSLog((@"%s [Line %d]" fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#ifdef DEBUG
#   define ULog(fmt, ...)  { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:@"%s
 [Line %d]", __PRETTY_FUNCTION__, __LINE__] message:[NSString stringWithFormat:fmt, ##__VA_ARGS__]  delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alert show]; }
#else
#   define ULog(...)
#endif

这就是它的样子:

Debug UIAlertView

+ 1迪埃德里克


1
NSLog(@"%s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__);

输出文件名、行号和函数名:

1
/proj/cocoa/cdcli/cdcli.m 121 managedObjectContext managedObjectContext

C++中的EDOCX1 0表示有名称的EDOCX1,1表示良好的函数名,在可可中看起来相同。

我不确定禁用nslog的正确方法是什么,我做到了:

1
#define NSLog

没有日志输出出现,但是我不知道这是否有任何副作用。


这里有一个我们使用的调试常量的大集合。享受。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// Uncomment the defitions to show additional info.

//  #define DEBUG

//  #define DEBUGWHERE_SHOWFULLINFO

//  #define DEBUG_SHOWLINES
//  #define DEBUG_SHOWFULLPATH
//  #define DEBUG_SHOWSEPARATORS
//  #define DEBUG_SHOWFULLINFO


// Definition of DEBUG functions. Only work if DEBUG is defined.
#ifdef DEBUG

    #define debug_separator() NSLog( @"────────────────────────────────────────────────────────────────────────────" );

    #ifdef DEBUG_SHOWSEPARATORS
        #define debug_showSeparators() debug_separator();
    #else
        #define debug_showSeparators()
    #endif

    /// /// /// ////// /////

    #ifdef DEBUG_SHOWFULLPATH
        #define debug_whereFull() debug_showSeparators(); NSLog(@"Line:%d : %s : %s", __LINE__,__FILE__,__FUNCTION__); debug_showSeparators();
    #else
        #define debug_whereFull() debug_showSeparators(); NSLog(@"Line:%d : %s : %s", __LINE__,[ [ [ [NSString alloc] initWithBytes:__FILE__ length:strlen(__FILE__) encoding:NSUTF8StringEncoding] lastPathComponent] UTF8String ] ,__FUNCTION__); debug_showSeparators();
    #endif

    /// /// /// ////// /////

    #define debugExt(args,...) debug_separator(); debug_whereFull(); NSLog( args, ##__VA_ARGS__); debug_separator();

    /// /// /// ////// ///// Debug Print Macros

    #ifdef DEBUG_SHOWFULLINFO
        #define debug(args,...) debugExt(args, ##__VA_ARGS__);
    #else
        #ifdef DEBUG_SHOWLINES
            #define debug(args,...) debug_showSeparators(); NSLog([ NSString stringWithFormat:@"Line:%d : %@", __LINE__, args ], ##__VA_ARGS__); debug_showSeparators();
        #else
            #define debug(args,...) debug_showSeparators(); NSLog(args, ##__VA_ARGS__); debug_showSeparators();
        #endif
    #endif

    /// /// /// ////// ///// Debug Specific Types

    #define debug_object( arg ) debug( @"Object: %@", arg );
    #define debug_int( arg ) debug( @"integer: %i", arg );
    #define debug_float( arg ) debug( @"float: %f", arg );
    #define debug_rect( arg ) debug( @"CGRect ( %f, %f, %f, %f)", arg.origin.x, arg.origin.y, arg.size.width, arg.size.height );
    #define debug_point( arg ) debug( @"CGPoint ( %f, %f )", arg.x, arg.y );
    #define debug_bool( arg )   debug( @"Boolean: %@", ( arg == YES ? @"YES" : @"NO" ) );

    /// /// /// ////// ///// Debug Where Macros

    #ifdef DEBUGWHERE_SHOWFULLINFO
        #define debug_where() debug_whereFull();
    #else
        #define debug_where() debug(@"%s",__FUNCTION__);
    #endif

    #define debug_where_separators() debug_separator(); debug_where(); debug_separator();

    /// /// /// ////// /////

#else
    #define debug(args,...)
    #define debug_separator()  
    #define debug_where()  
    #define debug_where_separators()  
    #define debug_whereFull()  
    #define debugExt(args,...)
    #define debug_object( arg )
    #define debug_int( arg )
    #define debug_rect( arg )  
    #define debug_bool( arg )  
    #define debug_point( arg )
    #define debug_float( arg )
#endif

有一个没有答案的新把戏。你可以用printf代替NSLog。这将为您提供一个干净的日志:

NSLog你可以得到这样的结果:

1
2011-11-03 13:43:55.632 myApp[3739:207] Hello Word

但是有了printf你只得到:

1
Hello World

使用此代码

1
2
3
4
5
6
#ifdef DEBUG
    #define NSLog(FORMAT, ...) fprintf(stderr,"%s
", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
    #define NSLog(...) {}              
#endif

我对这个问题的回答可能会有所帮助,看起来就像迪德里克做的那样。您还可能希望用自己的自定义日志类的静态实例替换对NSLog()的调用,这样您就可以为调试/警告/错误消息添加优先级标志,向文件或数据库以及控制台发送消息,或者几乎任何您能想到的事情。

1
2
3
4
5
6
7
8
9
10
11
#define DEBUG_MODE

#ifdef DEBUG_MODE
    #define DebugLog( s, ... ) NSLog( @"<%p %@:(%d)> %@", self,
              [[NSString stringWithUTF8String:__FILE__] lastPathComponent],
              __LINE__,
              [NSString stringWithFormat:(s),
              ##__VA_ARGS__] )
#else
    #define DebugLog( s, ... )
#endif


禁用所有nslog,对于对宏过敏的人,您也可以编译以下内容:

1
2
3
4
5
6
7
8
9
10
void SJLog(NSString *format,...)
{
    if(LOG)
    {  
        va_list args;
        va_start(args,format);
        NSLogv(format, args);
        va_end(args);
    }
}

而且,使用它就像nslog一样:

1
SJLog(@"bye bye NSLogs !");

从这个博客:http://whackylabs.com/rants/?P=134


可以很容易地将现有nslog更改为显示从中调用它们的行号和类。在前缀文件中添加一行代码:

1
#define NSLog(__FORMAT__, ...) NSLog((@"%s [Line %d]" __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)


为了补充以上的答案,在某些情况下,特别是在调试时,使用nslog替换可能非常有用。例如,去掉每一行上的所有日期和进程名/id信息可以使输出更可读、更快速地引导。

下面的链接提供了很多有用的弹药,可以使简单的日志记录变得更好。

http://cocaheads.byu.edu/wiki/a-different-nslog


例如,这很简单

-(void)applicationWillEnterForeground:(UIApplication *)application {

1
    NSLog(@"%s", __PRETTY_FUNCTION__);

}

输出:-[应用程序委派应用程序将进入前台:]


在以上答案的基础上,下面是我剽窃并提出的问题。还添加了内存日志记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#import <mach/mach.h>

#ifdef DEBUG
#   define DebugLog(fmt, ...) NSLog((@"%s(%d)" fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#   define DebugLog(...)
#endif


#define AlwaysLog(fmt, ...) NSLog((@"%s(%d)" fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);


#ifdef DEBUG
#   define AlertLog(fmt, ...)  { \
    UIAlertView *alert = [[UIAlertView alloc] \
            initWithTitle : [NSString stringWithFormat:@"%s(Line: %d)", __PRETTY_FUNCTION__, __LINE__]\
                  message : [NSString stringWithFormat : fmt, ##__VA_ARGS__]\
                 delegate : nil\
        cancelButtonTitle : @"Ok"\
        otherButtonTitles : nil];\
    [alert show];\
}

#else
#   define AlertLog(...)
#endif



#ifdef DEBUG
#   define DPFLog NSLog(@"%s(%d)", __PRETTY_FUNCTION__, __LINE__);//Debug Pretty Function Log
#else
#   define DPFLog
#endif


#ifdef DEBUG
#   define MemoryLog {\
    struct task_basic_info info;\
    mach_msg_type_number_t size = sizeof(info);\
    kern_return_t e = task_info(mach_task_self(),\
                                   TASK_BASIC_INFO,\
                                   (task_info_t)&info,\
                                   &size);\
    if(KERN_SUCCESS == e) {\
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; \
        [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; \
        DebugLog(@"%@ bytes", [formatter stringFromNumber:[NSNumber numberWithInteger:info.resident_size]]);\
    } else {\
        DebugLog(@"Error with task_info(): %s", mach_error_string(e));\
    }\
}

#else
#   define MemoryLog
#endif


新添加到DLOG。不要完全从已发布的应用程序中删除调试,只需禁用它。当用户遇到需要调试的问题时,只需说明如何在已发布的应用程序中启用调试,并通过电子邮件请求日志数据。

短版本:创建全局变量(是的,懒惰和简单的解决方案),然后这样修改dlog:

1
2
BOOL myDebugEnabled = FALSE;
#define DLog(fmt, ...) if (myDebugEnabled) NSLog((@"%s [Line %d]" fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);

在Jomnius-Ilesson-Ilearned上给出了更长的答案:如何在发布的应用程序中进行动态调试登录


有一段时间,我一直在使用一个从上面几个采用宏的网站。我的重点是在控制台中登录,重点是控制和过滤的详细信息;如果您不介意很多日志行,但希望轻松地打开和关闭它们的批,那么您可能会发现这很有用。

首先,我可以选择用上面@rodrigo描述的printf替换nslog。

1
2
3
4
5
6
#define NSLOG_DROPCHAFF//comment out to get usual date/time ,etc:2011-11-03 13:43:55.632 myApp[3739:207] Hello Word

#ifdef NSLOG_DROPCHAFF
#define NSLog(FORMAT, ...) printf("%s
", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#endif

接下来,我打开或关闭登录。

1
2
3
#ifdef DEBUG
#define LOG_CATEGORY_DETAIL// comment out to turn all conditional logging off while keeping other DEBUG features
#endif

在主块中,定义与应用程序中的模块对应的各种类别。还要定义一个日志记录级别,超过该级别将不调用日志记录调用。然后定义nslog输出的各种味道

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifdef LOG_CATEGORY_DETAIL

    //define the categories using bitwise leftshift operators
    #define kLogGCD (1<<0)
    #define kLogCoreCreate (1<<1)
    #define kLogModel (1<<2)
    #define kLogVC (1<<3)
    #define kLogFile (1<<4)
    //etc

    //add the categories that should be logged...
    #define kLOGIFcategory kLogModel+kLogVC+kLogCoreCreate

    //...and the maximum detailLevel to report (use -1 to override the category switch)
    #define kLOGIFdetailLTEQ 4

    // output looks like this:"-[AppDelegate myMethod] log string..."
    #   define myLog(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%s" format), __PRETTY_FUNCTION__, ##__VA_ARGS__);}

    // output also shows line number:"-[AppDelegate myMethod][l17]  log string..."
    #   define myLogLine(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%s[l%i]" format), __PRETTY_FUNCTION__,__LINE__ ,##__VA_ARGS__);}

    // output very simple:" log string..."
    #   define myLogSimple(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"" format), ##__VA_ARGS__);}

    //as myLog but only shows method name:"myMethod: log string..."
    // (Doesn't work in C-functions)
    #   define myLog_cmd(category,detailLevel,format,...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%@:" format), NSStringFromSelector(_cmd), ##__VA_ARGS__);}

    //as myLogLine but only shows method name:"myMethod>l17: log string..."
    #   define myLog_cmdLine(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%@>l%i:" format), NSStringFromSelector(_cmd),__LINE__ , ##__VA_ARGS__);}

    //or define your own...
   // # define myLogEAGLcontext(category,detailLevel,format, ...) if(detailLevel<0 || ((category&kLOGIFcategory)&&detailLevel<= kLOGIFdetailLTEQ)) {NSLog((@"%s>l%i (ctx:%@)" format), __PRETTY_FUNCTION__,__LINE__ ,[EAGLContext currentContext], ##__VA_ARGS__);}

#else
    #   define myLog_cmd(...)
    #   define myLog_cmdLine(...)
    #   define myLog(...)
    #   define myLogLine(...)
    #   define myLogSimple(...)
    //#   define myLogEAGLcontext(...)
#endif

因此,对于klogifcategory和klogifdetailteq的当前设置,调用

1
myLogLine(kLogVC, 2, @"%@",self);

会打印但不会

1
myLogLine(kLogGCD, 2, @"%@",self);//GCD not being printed

也不会

1
myLogLine(kLogGCD, 12, @"%@",self);//level too high

如果要覆盖单个日志调用的设置,请使用负级别:

1
myLogLine(kLogGCD, -2, @"%@",self);//now printed even tho' GCD category not active.

我发现每行输入的几个额外字符都是值得的。

  • 打开或关闭整个注释类别(例如,仅报告标记为model的调用)
  • 报告更高级别号码或最重要的电话号码更低的细节
  • 我相信很多人会觉得这有点过分,但只是以防万一,有人发现这符合他们的目的。