I'm writing an embedded application in C in Linux user-space.
I need to add the file name and function name to syslog string.
how can i do that ?
note: to better explain my self, i need to define a macro that fills in the file name and func name..
what i did was the follwing:
#define LOG(prio, ...) my_log(__FILE__, __LINE__, __func__, prio, __VA_ARGS__)
and the problem is in the implementation of my_log:
void my_log(const char *file, int line, const char *func, int prio, const char *fmt, ...)
{
//how to add the "filename:line, funcname: " string to the fmt string
va_start(args, full_fmt);
vsyslog(prio, full_fmt, args);
va_end(args);
}
syslog accepts a printf like format string. You are probably referring to something along the lines of:
syslog(1, "%s:%d: something bad happened", __FILE__, __LINE__);
__FILE__ automatically expands to a string containing the current source file. __LINE__ similarly expands to the current line in the current file.
Related
I am trying to create a log function which will print file name, function name, line number and error msg.
Is there a way to create macro for a small function which only takes the log type ,msg and macro value will add FILE, func, LINE and call the actual function
it might be something like this:
#define func(int type,const char *msg, ...) \
func(int type,char *__FILE__,char *__func__,char *__LINE__,const *msg,...)
If you want create a macro that during invocation replaces or adds some parameters, you don't have to write it as a prototype, but, because C preprocessor is a simple text replacement processor, you can write it as the invocation itself.
So your macro definition becomes:
#define func(type, msg, ...) \
func(type, __FILE__, __func__, __LINE__, msg, __VA_ARGS__)
The C preprocessor assign to the symbol __VA_ARGS__ the sequence of parameters, included the colon, that starts from elipsis (...) on.
Now using the macro as in the example below:
func(MYTYPE, "Info function called with stack: '%s' size %ld", bIsPrivilegedStack(stack) ? "Privileged" : "User", StackSize);
Will translate in:
func(MYTYPE, __FILE__, __func__, __LINE__, "Infofunction called with stack: '%s' size %ld", bIsPrivilegedStack(stack) ? "Privileged" : "User", StackSize);
The function prototype isn't in the macro and should be write once only, preferably contained in an header file, and appear in the compiling process before any invocation. It will contain all parameters types as in:
void func(int type, char *__FILE__, char *__func__, int _LINE__, const *msg, ...);
Note: the preprocessor symbol __LINE__ is defined as an int, not a char *.
Your file layout will be more or less:
//Prototype
void func(int type, char * file, char *fnc, int line, const *msg, ...);
//Macro definition
#define func(type, msg, ...) \
func(type, __FILE__, __func__, __LINE__, msg, __VA_ARGS__)
//Usage
void foo(void)
{
.....
func(MYTYPE, "Info function called with stack: '%s' size %ld", bIsPrivilegedStack(stack) ? "Privileged" : "User", StackSize);
.....
}
I have two macros:
#define LogFunction(str) fprintf(stdout, "%s: %s\n",__FUNCTION__,(str))
#define LogPrintf(f_, ...) fprintf(stdout, (f_), ##__VA_ARGS__)
So i can use them this way:
void MyFunction()
{
int N=4;
LogFunction("START"); // Outputs "MyFunction: START"
LogPrintf("N=%d\n", N); // Outputs "N=4"
}
What I would like to change is to
add FUNCTION at the start of the LogPrintf as it is in LogFunction
add "\n" at the end of the LogPrintf without having to remember to put it in myself
so in the end i could have just one macro for my outputs.
I've tried to understand if Appending to __VA_ARGS__ could've been useful, but i admit that i've not understood if it is related to my case :(
Thanks.
why not doing it in 3 steps?
#define LogPrintf(f_, ...) do { fprintf(stdout, "%s: ",__FUNCTION__); \
fprintf(stdout, (f_), ##__VA_ARGS__); \
fprintf(stdout,"\n"); } while(0)
this does 3 prints, but at least it's simple and does what you want. the do while(0) trick makes sure this is one only block (when using if without braces) and requires semicolon.
If you're willing to rely on the first argument to LogPrintf being a string literal, then you should be able to use string concatenation to achieve your objective:
// Assumes f_ always corresponds to a string literal:
#define LogPrintf(f_, ...) fprintf(stdout, "%s: " f_ "\n", __FUNCTION__, ##__VA_ARGS__)
Note, however, that in standard C, the LogPrintf macro requires at least two arguments, and the ## has no place. I keep it here only because you use it in your original code.
If you must accept format string expressions other than string literals, however, then your simplest alternative is to perform multiple I/O calls, as another answer also suggests:
#define LogPrintf(f_, ...) do { \
fprintf(stdout, "%s: ", __FUNCTION__); \
fprintf(stdout, (f_), ##__VA_ARGS__); \
fputc('\n', stdout); \
} while (0)
Note that in this case, the macro expands to a statement (sans trailing semicolon), whereas in the other, the macro expands to an expression. If you want the return value(s) of any of the I/O functions, then you'll have to make special provisions for that in this case.
If that doesn't work for you either, then the ultimate alternative is to write and use a helper function, as was suggested in comments:
#define LogPrintf(f_, ...) log_printf_impl(stdout, __FUNCTION__, (f_), ##__VA_ARGS__)
int log_printf_impl(FILE *f, const char *func, const char *fmt, ...) {
static const char prefix[] = "%s: ";
size_t flen = strlen(fmt);
va_list args;
int result = -1;
char *aug_fmt = malloc(sizeof(prefix) + strlen(fmt) + 1);
if (aug_fmt) {
va_start(args, fmt);
sprintf(aug_fmt, "%s%s\n", prefix, fmt);
result = vfprintf(f, aug_fmt, func, args);
va_end(args);
free(aug_fmt);
}
return result;
}
I need to change a implementation of Macro(LOGGING_MACRO)
printf into syslog.
Macro Usage :
LOGGING_MACRO(5,("Value of x,y=%d,%d\n",x,y));
Def1 :
#define LOGGING_MACRO(loglevel,str) printf str;
Def2 :
#define LOGGING_MACRO(loglevel,str) syslog(loglevel,str);
Note : I cannot change the macro format :(
Def2 throws error as syslog will not accept 'str'(2nd arg) with front & back braces. [But working fine in Def1 with printf ]
Kindly suggest how to remove the 1st & last braces from 'str' inside the macro before passing 'str' to syslog.
Example below will work in single thread application:
char* custom_log(const char *fmt, ...) {
static char outputString[200]; //Adjust your size for maximal log value
va_list args;
va_start(args, fmt);
vsprintf(outputString, fmt, args);
va_end(args);
return outputString;
}
Then modify your macro to:
#define LOGGING_MACRO(loglevel,str) syslog(loglevel, custom_log str)
Remember, this works only in single-thread mode and make sure, custom_log function is visible where custom_log function is called.
For multi-thread, you might update it like this:
#define LOGGING_MACRO(loglevel,str) do {\
mutex_lock(); /*Lock your mutex for debug print*/ \
syslog(loglevel, custom_log str); /*Debug*/ \
mutex_unlock(); /*Unlock mutex*/ \
} while (0)
Keep in mind that you have to write mutex_lock and mutex_unlock functions for your requirements in your system to lock only debug functions between multi threads.
I have many functions and while entering each function, I am calling a Macro FUNC_ENTRY, which is in turn calling the default logging macro (LOGGING).
To be specific, this is my FUNC_ENTRY macro:
#define FUNC_ENTRY LOGGING(ONE, "Entry");
My LOGGING macro is defined as below:
#define LOGGING(prio, my_var, ...) \
{\
char priority[6];\
bzero(level,4); \
if (prio == ONE) { sprintf(priority,"ONE");} \
if (prio == TWO) { sprintf(priority,"TWO");} \
if (prio == THREE) { sprintf(priority,"THREE");} \
fprintf(fp,"%s: %s: %s: %d: %s:%s|\n",__DATE__, __TIME__, __FILE__,__LINE__, __func__,level);\
fflush(fp); \
fprintf(fp,my_var,##__VA_ARGS__);\
fflush(fp); \
fprintf(fp,"\n");\
fflush(fp); \
}
OK, now my question is, whenever I enter any function, my LOGGING macro should print "Entry to xyz function". As of now, it only prints "Entry". Any clue how to achieve this?
The way I am calling the FUNC_ENTRY macro is as below.
Suppose I have a function xyz;
void xyz {
FUNC_ENTRY;
/*other statements*/
}
There are a number of issues:
That's two too many fflush(fp) calls for most purposes, unless you think you often write buggy code.
'Tis odd that you zero level, declare and set priority, but print level which is still all zeros.
That fragment is big enough to warrant a function.
You don't want the semi-colon after #define FUNCENTRY.
'Tis also odd that you think you should get Entry to xyz function when you don't include to or function in the strings in the macro.
It also seems pointless reporting the time and date when the file was compiled in your error messages (which is what __DATE__ and __TIME__ are for).
Fixing some of those issues leads to:
#define FUNC_ENTRY LOGGING(ONE, "Entry to %s function\n", __func__)
#define LOGGING(prio, my_var, ...) \
{\
char priority[6];\
memset(priority, '\0', sizeof(priority)); \
if (prio == ONE) { sprintf(priority, "ERR");} \
if (prio == TWO) { sprintf(priority, "TWO");} \
if (prio == THREE) { sprintf(priority, "THREE");} \
fprintf(fp,"%s: %s: %s: %d: %s:%s|\n", __DATE__, __TIME__, __FILE__, __LINE__, __func__, priority);\
fprintf(fp, my_var, ##__VA_ARGS__); \
fprintf(fp,"\n"); \
fflush(fp); \
}
I note that , ##__VA_ARGS__ is a GCC extension over standard C. The bzero() function was marked obsolescent in POSIX 2004 and is missing from POSIX 2008; it is best not to use it.
It is not clear, but judging from the code, you have a global (or, at least, file scope) variable FILE *fp; which is initialized to the log file you want to write to. Ick; and likewise yuck!
Were it my code (and I wanted the Entry to xyz function message spelled thus), I'd probably use:
extern void err_logger(int level, int line, char const *file, char const *func, ...);
and
#define FUNC_ENTRY LOGGING(ONE, "Entry to %s function\n", __func__)
#define LOGGING(prio, ...) \
err_logger(level, __LINE__, __FILE__, __func__, __VA_ARGS)
And the implementation of err_logger() might be:
void err_logger(int level, int line, char const *file, char const *func, ...)
{
char *priority = "";
if (level == ONE) priority = "ERR";
if (level == TWO) priority = "TWO";
if (level == THREE) priority = "THREE";
time_t t = time(0);
char date_time[32];
strftime(date_time, sizeof(date_time), "%Y-%m-%d %H:%M:%S", gmtime(&t));
fprintf(logfp, "%s: %s: %d: %s: %s|\n", date_time, file, line, func, priority);
va_list args;
va_start(args, func);
char *fmt = va_arg(args, char *);
vfprintf(logfp, fmt, args);
va_end(args);
fflush(logfp);
}
Where logfp would be the log file stream and would be private to the file that holds the source for err_logger().
I've not compiled that code; there could be minor errors in it. It does, however, give you the general idea of what to do and how to do it — I think.
Something along these lines:
#include <stdio.h>
#define T printf("Entering %s\n", __func__)
int main(void)
{
T;
return 0;
}
Unfortunately it looks like you're not going to be able to concatenate that to your string at compile time (see here: Can I substitute __func__ into an identifier name in a C macro?). There may be some other compiler specific or C++ specific tricks but otherwise you will need to build your string at run time.
I'd like to create a proxy to fprintf, like so:
void raise_exception(char *filename, int line, char *format_string, ...) {
fprintf(stderr, "Exception in `%s`:%d!\n", filename, line);
fprintf(stderr, format_string, ...);
exit(EXIT_FAILURE);
}
But what do I put in the place of the second ellipsis? Is it at all possible?
I'd like to simplify it even a bit more like so:
#define RAISE(...) raise_exception(__FILE__, __LINE__, ...)
But I don't think this would work either.
Any ideas? Thanks!
UPDATE
Straight from Wikipedia:
Variable-argument macros were introduced in the ISO/IEC 9899:1999 (C99)
So the define that would do it should look like so:
#define RAISE(...) raise_exception(__FILE__, __LINE__, __VA_ARGS__)
#include <stdarg.h>
void raise_exception(char *filename, int line, char *format_string, ...)
{
va_list args;
fprintf(stderr, "Exception in `%s`:%d!\n", filename, line);
va_start(args, format_string);
vfprintf(stderr, format_string, args);
va_end(args);
exit(EXIT_FAILURE);
}
Use vfprintf instead.
Please see this question:
Passing variable number of arguments around
Your exact example -- of wrapping printf -- is used as an example in the discussion here:
http://www.swig.org/Doc1.3/Varargs.html