diff options
| author | Willem Jan Palenstijn <wjp@usecode.org> | 2015-03-20 15:32:25 +0100 | 
|---|---|---|
| committer | Willem Jan Palenstijn <wjp@usecode.org> | 2015-03-20 15:32:25 +0100 | 
| commit | 8d9ddc51076ce0da3d0a4216b1bc495783f3733c (patch) | |
| tree | 200d43d0356f052fe23da463330e43b37138386e /include | |
| parent | 2d55fd38b4a8ef5076c0591ae4147f81c1107ee3 (diff) | |
| parent | 9ba78fcfa3dec88928df33be26821e3fd5a10727 (diff) | |
Merge pull request #30 from dmpelt/logging
New logging API
Diffstat (limited to 'include')
| -rw-r--r-- | include/astra/Logger.h | 72 | ||||
| -rw-r--r-- | include/astra/Logging.h | 159 | ||||
| -rw-r--r-- | include/astra/clog.h | 693 | 
3 files changed, 852 insertions, 72 deletions
| diff --git a/include/astra/Logger.h b/include/astra/Logger.h deleted file mode 100644 index 34fd364..0000000 --- a/include/astra/Logger.h +++ /dev/null @@ -1,72 +0,0 @@ -/* ------------------------------------------------------------------------ -Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp -           2014-2015, CWI, Amsterdam - -Contact: astra@uantwerpen.be -Website: http://sf.net/projects/astra-toolbox - -This file is part of the ASTRA Toolbox. - - -The ASTRA Toolbox is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -The ASTRA Toolbox is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>. - ------------------------------------------------------------------------ -$Id$ -*/ - -#ifndef _INC_ASTRA_LOGGER -#define _INC_ASTRA_LOGGER - -#include <cstdio> - -namespace astra -{ - -/** - *  This is the first stab at a decent logger. If the file "astra_logger.txt", it will be replaced - *  with the text sent to this logger. If the file doesn't exist when the app starts, nothing is written. - */ -class CLogger -{ -	static std::FILE * m_pOutFile; -	static bool m_bInitialized; - -	static void _assureIsInitialized(); - -	CLogger(); - -public: - -	/** -	 * Writes a line to the log file (newline is added). Ignored if logging is turned off. -	 * -	 * @param _text char pointer to text in line -	 */ -	static void writeLine(const char * _text); - -	/** -	 * Formats and writes a CUDA error to the log file. Ignored if logging is turned off. -	 * -	 * @param _fileName filename where error occurred (typically __FILE__) -	 * @param _line line in file (typically __LINE__) -	 * @param _errString string describing the error, can be output of cudaGetErrorString -	 */ -	static void writeTerminalCUDAError(const char * _fileName, int _iLine, const char * _errString); -}; - -} - -#endif /* _INC_ASTRA_LOGGER */ - diff --git a/include/astra/Logging.h b/include/astra/Logging.h new file mode 100644 index 0000000..8e19ea4 --- /dev/null +++ b/include/astra/Logging.h @@ -0,0 +1,159 @@ +/* +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp +           2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>. + +----------------------------------------------------------------------- +$Id$ +*/ + +#ifndef _INC_ASTRA_LOGGING +#define _INC_ASTRA_LOGGING + +#include "astra/Globals.h" + +#define ASTRA_DEBUG(...) astra::CLogger::debug(__FILE__,__LINE__, __VA_ARGS__) +#define ASTRA_INFO(...) astra::CLogger::info(__FILE__,__LINE__, __VA_ARGS__) +#define ASTRA_WARN(...) astra::CLogger::warn(__FILE__,__LINE__, __VA_ARGS__) +#define ASTRA_ERROR(...) astra::CLogger::error(__FILE__,__LINE__, __VA_ARGS__) + +namespace astra +{ + +enum log_level { +    LOG_DEBUG, +    LOG_INFO, +    LOG_WARN, +    LOG_ERROR +}; + +class _AstraExport CLogger +{ +	CLogger(); +  ~CLogger(); +  static bool m_bEnabledFile; +  static bool m_bEnabledScreen; +  static bool m_bFileProvided; +  static bool m_bInitialized; +  static void _assureIsInitialized(); +  static void _setLevel(int id, log_level m_eLevel); + +public: + +	/** +	 * Writes a line to the log file (newline is added). Ignored if logging is turned off. +	 * +	 * @param sfile +   * The name of the source file making this log call (e.g. __FILE__). +   * +   * @param sline +   * The line number of the call in the source code (e.g. __LINE__). +   * +   * @param id +   * The id of the logger to write to. +   * +   * @param fmt +   * The format string for the message (printf formatting). +   * +   * @param ... +   * Any additional format arguments. +	 */ +  static void debug(const char *sfile, int sline, const char *fmt, ...); +  static void info(const char *sfile, int sline, const char *fmt, ...); +  static void warn(const char *sfile, int sline, const char *fmt, ...); +  static void error(const char *sfile, int sline, const char *fmt, ...); + +  /** +	 * Sets the file to log to, with logging level. +   * +   * @param filename +   * File to log to. +	 * +	 * @param m_eLevel +   * Logging level (LOG_DEBUG, LOG_WARN, LOG_INFO, LOG_ERROR). +   * +	 */ +  static void setOutputFile(const char *filename, log_level m_eLevel); + +  /** +	 * Sets the screen to log to, with logging level. +   * +   * @param screen_fd +   * Screen file descriptor (1 for stdout, 2 for stderr) +	 * +	 * @param m_eLevel +   * Logging level (LOG_DEBUG, LOG_WARN, LOG_INFO, LOG_ERROR). +   * +	 */ +  static void setOutputScreen(int fd, log_level m_eLevel); +   +  /** +   * Set the format string for log messages.  Here are the substitutions you may +   * use: +   * +   *     %f: Source file name generating the log call. +   *     %n: Source line number where the log call was made. +   *     %m: The message text sent to the logger (after printf formatting). +   *     %d: The current date, formatted using the logger's date format. +   *     %t: The current time, formatted using the logger's time format. +   *     %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR"). +   *     %%: A literal percent sign. +   * +   * The default format string is "%d %t %f(%n): %l: %m\n". +   * +   * @param fmt +   * The new format string, which must be less than 256 bytes. +   * You probably will want to end this with a newline (\n). +   * +   */ +  static void setFormatFile(const char *fmt); +  static void setFormatScreen(const char *fmt); + + +  /** +   * Enable logging. +   * +   */ +  static void enable(); +  static void enableScreen(); +  static void enableFile(); + +  /** +   * Disable logging. +   * +   */ +  static void disable(); +  static void disableScreen(); +  static void disableFile(); + +  /** +   * Set callback function for logging to screen. +   * @return whether callback was set succesfully. +   * +   */ +  static bool setCallbackScreen(void (*cb)(const char *msg, size_t len)); + +}; + +} + +#endif /* _INC_ASTRA_LOGGING */ diff --git a/include/astra/clog.h b/include/astra/clog.h new file mode 100644 index 0000000..6ffb660 --- /dev/null +++ b/include/astra/clog.h @@ -0,0 +1,693 @@ +/* clog: Extremely simple logger for C. + * + * Features: + * - Implemented purely as a single header file. + * - Create multiple loggers. + * - Four log levels (debug, info, warn, error). + * - Custom formats. + * - Fast. + * + * Dependencies: + * - Should conform to C89, C++98 (but requires vsnprintf, unfortunately). + * - POSIX environment. + * + * USAGE: + * + * Include this header in any file that wishes to write to logger(s).  In + * exactly one file (per executable), define CLOG_MAIN first (e.g. in your + * main .c file). + * + *     #define CLOG_MAIN + *     #include "clog.h" + * + * This will define the actual objects that all the other units will use. + * + * Loggers are identified by integers (0 - 15).  It's expected that you'll + * create meaningful constants and then refer to the loggers as such. + * + * Example: + * + *  const int MY_LOGGER = 0; + * + *  int main() { + *      int r; + *      r = clog_init_path(MY_LOGGER, "my_log.txt"); + *      if (r != 0) { + *          fprintf(stderr, "Logger initialization failed.\n"); + *          return 1; + *      } + *      clog_info(CLOG(MY_LOGGER), "Hello, world!"); + *      clog_free(MY_LOGGER); + *      return 0; + *  } + * + * The CLOG macro used in the call to clog_info is a helper that passes the + * __FILE__ and __LINE__ parameters for you, so you don't have to type them + * every time. (It could be prettier with variadic macros, but that requires + * C99 or C++11 to be standards compliant.) + * + * Errors encountered by clog will be printed to stderr.  You can suppress + * these by defining a macro called CLOG_SILENT before including clog.h. + * + * License: Do whatever you want. It would be nice if you contribute + * improvements as pull requests here: + * + *   https://github.com/mmueller/clog + * + * Copyright 2013 Mike Mueller <mike@subfocal.net>. + * + * As is; no warranty is provided; use at your own risk. + */ + +#ifndef __CLOG_H__ +#define __CLOG_H__ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#ifndef _MSC_VER +#include <unistd.h> +#else +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <io.h> +#define open _open +#define close _close +#define write _write +#define snprintf _snprintf +#endif + +/* Number of loggers that can be defined. */ +#define CLOG_MAX_LOGGERS 16 + +/* Format strings cannot be longer than this. */ +#define CLOG_FORMAT_LENGTH 256 + +/* Formatted times and dates should be less than this length. If they are not, + * they will not appear in the log. */ +#define CLOG_DATETIME_LENGTH 256 + +/* Default format strings. */ +#define CLOG_DEFAULT_FORMAT "%d %t %f(%n): %l: %m\n" +#define CLOG_DEFAULT_DATE_FORMAT "%Y-%m-%d" +#define CLOG_DEFAULT_TIME_FORMAT "%H:%M:%S" + +#ifdef __cplusplus +extern "C" { +#endif + +enum clog_level { +    CLOG_DEBUG, +    CLOG_INFO, +    CLOG_WARN, +    CLOG_ERROR +}; + +struct clog; + +/** + * Create a new logger writing to the given file path.  The file will always + * be opened in append mode. + * + * @param id + * A constant integer between 0 and 15 that uniquely identifies this logger. + * + * @param path + * Path to the file where log messages will be written. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_init_path(int id, const char *const path); + +/** + * Create a new logger writing to a file descriptor. + * + * @param id + * A constant integer between 0 and 15 that uniquely identifies this logger. + * + * @param fd + * The file descriptor where log messages will be written. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_init_fd(int id, int fd); + +/** + * Destroy (clean up) a logger.  You should do this at the end of execution, + * or when you are done using the logger. + * + * @param id + * The id of the logger to destroy. + */ +void clog_free(int id); + +#define CLOG(id) __FILE__, __LINE__, id + +/** + * Log functions (one per level).  Call these to write messages to the log + * file.  The first three arguments can be replaced with a call to the CLOG + * macro defined above, e.g.: + * + *     clog_debug(CLOG(MY_LOGGER_ID), "This is a log message."); + * + * @param sfile + * The name of the source file making this log call (e.g. __FILE__). + * + * @param sline + * The line number of the call in the source code (e.g. __LINE__). + * + * @param id + * The id of the logger to write to. + * + * @param fmt + * The format string for the message (printf formatting). + * + * @param ... + * Any additional format arguments. + */ +void clog_debug(const char *sfile, int sline, int id, const char *fmt, va_list ap); +void clog_info(const char *sfile, int sline, int id, const char *fmt, va_list ap); +void clog_warn(const char *sfile, int sline, int id, const char *fmt, va_list ap); +void clog_error(const char *sfile, int sline, int id, const char *fmt, va_list ap); + +/** + * Set the minimum level of messages that should be written to the log. + * Messages below this level will not be written.  By default, loggers are + * created with level == CLOG_DEBUG. + * + * @param id + * The identifier of the logger. + * + * @param level + * The new minimum log level. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_level(int id, enum clog_level level); + +/** + * Set the format string used for times.  See strftime(3) for how this string + * should be defined.  The default format string is CLOG_DEFAULT_TIME_FORMAT. + * + * @param fmt + * The new format string, which must be less than CLOG_FORMAT_LENGTH bytes. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_time_fmt(int id, const char *fmt); + +/** + * Set the format string used for dates.  See strftime(3) for how this string + * should be defined.  The default format string is CLOG_DEFAULT_DATE_FORMAT. + * + * @param fmt + * The new format string, which must be less than CLOG_FORMAT_LENGTH bytes. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_date_fmt(int id, const char *fmt); + +/** + * Set the format string for log messages.  Here are the substitutions you may + * use: + * + *     %f: Source file name generating the log call. + *     %n: Source line number where the log call was made. + *     %m: The message text sent to the logger (after printf formatting). + *     %d: The current date, formatted using the logger's date format. + *     %t: The current time, formatted using the logger's time format. + *     %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR"). + *     %%: A literal percent sign. + * + * The default format string is CLOG_DEFAULT_FORMAT. + * + * @param fmt + * The new format string, which must be less than CLOG_FORMAT_LENGTH bytes. + * You probably will want to end this with a newline (\n). + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_fmt(int id, const char *fmt); + +/** + * Set the callback function. + * + * @param cb + * The new callback function. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_cb(int id, void (*cb)(const char *msg, size_t len)); + +/** + * Set the file descriptor. + * + * @param id + * The identifier of the logger. + * + * @param fd + * The new file descriptor. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_fd(int id, int fd); + + +/* + * No need to read below this point. + */ + +/** + * The C logger structure. + */ +struct clog { + +    /* The current level of this logger. Messages below it will be dropped. */ +    enum clog_level level; + +    /* The file being written. */ +    int fd; + +    /* The format specifier. */ +    char fmt[CLOG_FORMAT_LENGTH]; + +    /* Date format */ +    char date_fmt[CLOG_FORMAT_LENGTH]; + +    /* Time format */ +    char time_fmt[CLOG_FORMAT_LENGTH]; + +    /* Tracks whether the fd needs to be closed eventually. */ +    int opened; + +    /* Callback function for each log message. */ +    void (*cb)(const char *msg, size_t len); +}; + +void _clog_err(const char *fmt, ...); + +#ifdef CLOG_MAIN +struct clog *_clog_loggers[CLOG_MAX_LOGGERS] = { 0 }; +#else +extern struct clog *_clog_loggers[CLOG_MAX_LOGGERS]; +#endif + +#ifdef CLOG_MAIN + +const char *const CLOG_LEVEL_NAMES[] = { +    "Debug", +    "Info", +    "Warning", +    "Error", +}; + +int +clog_init_path(int id, const char *const path) +{ +    int fd = open(path, O_CREAT | O_WRONLY | O_APPEND, 0666); +    if (fd == -1) { +        _clog_err("Unable to open %s: %s\n", path, strerror(errno)); +        return 1; +    } +    if (clog_init_fd(id, fd)) { +        close(fd); +        return 1; +    } +    _clog_loggers[id]->opened = 1; +    return 0; +} + +int +clog_init_fd(int id, int fd) +{ +    struct clog *logger; + +    if (_clog_loggers[id] != NULL) { +        _clog_err("Logger %d already initialized.\n", id); +        return 1; +    } + +    logger = (struct clog *) malloc(sizeof(struct clog)); +    if (logger == NULL) { +        _clog_err("Failed to allocate logger: %s\n", strerror(errno)); +        return 1; +    } + +    logger->level = CLOG_DEBUG; +    logger->fd = fd; +    logger->opened = 0; +    strcpy(logger->fmt, CLOG_DEFAULT_FORMAT); +    strcpy(logger->date_fmt, CLOG_DEFAULT_DATE_FORMAT); +    strcpy(logger->time_fmt, CLOG_DEFAULT_TIME_FORMAT); +    logger->cb = NULL; + +    _clog_loggers[id] = logger; +    return 0; +} + +void +clog_free(int id) +{ +    if (_clog_loggers[id]) { +        if (_clog_loggers[id]->opened) { +            close(_clog_loggers[id]->fd); +        } +        free(_clog_loggers[id]); +        _clog_loggers[id]=NULL; +    } +} + +int +clog_set_level(int id, enum clog_level level) +{ +    if (_clog_loggers[id] == NULL) { +        return 1; +    } +    if ((unsigned) level > CLOG_ERROR) { +        return 1; +    } +    _clog_loggers[id]->level = level; +    return 0; +} + +int +clog_set_fd(int id, int fd) +{ +    if (_clog_loggers[id] == NULL) { +        return 1; +    } +    _clog_loggers[id]->fd = fd; +    return 0; +} + +int +clog_set_time_fmt(int id, const char *fmt) +{ +    struct clog *logger = _clog_loggers[id]; +    if (logger == NULL) { +        _clog_err("clog_set_time_fmt: No such logger: %d\n", id); +        return 1; +    } +    if (strlen(fmt) >= CLOG_FORMAT_LENGTH) { +        _clog_err("clog_set_time_fmt: Format specifier too long.\n"); +        return 1; +    } +    strcpy(logger->time_fmt, fmt); +    return 0; +} + +int +clog_set_date_fmt(int id, const char *fmt) +{ +    struct clog *logger = _clog_loggers[id]; +    if (logger == NULL) { +        _clog_err("clog_set_date_fmt: No such logger: %d\n", id); +        return 1; +    } +    if (strlen(fmt) >= CLOG_FORMAT_LENGTH) { +        _clog_err("clog_set_date_fmt: Format specifier too long.\n"); +        return 1; +    } +    strcpy(logger->date_fmt, fmt); +    return 0; +} + +int +clog_set_fmt(int id, const char *fmt) +{ +    struct clog *logger = _clog_loggers[id]; +    if (logger == NULL) { +        _clog_err("clog_set_fmt: No such logger: %d\n", id); +        return 1; +    } +    if (strlen(fmt) >= CLOG_FORMAT_LENGTH) { +        _clog_err("clog_set_fmt: Format specifier too long.\n"); +        return 1; +    } +    strcpy(logger->fmt, fmt); +    return 0; +} + +int +clog_set_cb(int id, void (*cb)(const char *msg, size_t len)) +{ +    struct clog *logger = _clog_loggers[id]; +    if (logger == NULL) { +        _clog_err("clog_set_cb: No such logger: %d\n", id); +        return 1; +    } +    logger->cb = cb; +    return 0; +} + +/* Internal functions */ + +size_t +_clog_append_str(char **dst, char *orig_buf, const char *src, size_t cur_size) +{ +    size_t new_size = cur_size; + +    while (strlen(*dst) + strlen(src) >= new_size) { +        new_size *= 2; +    } +    if (new_size != cur_size) { +        if (*dst == orig_buf) { +            *dst = (char *) malloc(new_size); +            strcpy(*dst, orig_buf); +        } else { +            *dst = (char *) realloc(*dst, new_size); +        } +    } + +    strcat(*dst, src); +    return new_size; +} + +size_t +_clog_append_int(char **dst, char *orig_buf, long int d, size_t cur_size) +{ +    char buf[40]; /* Enough for 128-bit decimal */ +    if (snprintf(buf, 40, "%ld", d) >= 40) { +        return cur_size; +    } +    return _clog_append_str(dst, orig_buf, buf, cur_size); +} + +size_t +_clog_append_time(char **dst, char *orig_buf, struct tm *lt, +                  const char *fmt, size_t cur_size) +{ +    char buf[CLOG_DATETIME_LENGTH]; +    size_t result = strftime(buf, CLOG_DATETIME_LENGTH, fmt, lt); + +    if (result > 0) { +        return _clog_append_str(dst, orig_buf, buf, cur_size); +    } + +    return cur_size; +} + +const char * +_clog_basename(const char *path) +{ +    const char *slash = strrchr(path, '/'); +    if (slash) { +        path = slash + 1; +    } +#ifdef _WIN32 +    slash = strrchr(path, '\\'); +    if (slash) { +        path = slash + 1; +    } +#endif +    return path; +} + +char * +_clog_format(const struct clog *logger, char buf[], size_t buf_size, +             const char *sfile, int sline, const char *level, +             const char *message) +{ +    size_t cur_size = buf_size; +    char *result = buf; +    enum { NORMAL, SUBST } state = NORMAL; +    size_t fmtlen = strlen(logger->fmt); +    size_t i; +    time_t t = time(NULL); +    struct tm *lt = localtime(&t); + +    sfile = _clog_basename(sfile); +    result[0] = 0; +    for (i = 0; i < fmtlen; ++i) { +        if (state == NORMAL) { +            if (logger->fmt[i] == '%') { +                state = SUBST; +            } else { +                char str[2] = { 0 }; +                str[0] = logger->fmt[i]; +                cur_size = _clog_append_str(&result, buf, str, cur_size); +            } +        } else { +            switch (logger->fmt[i]) { +                case '%': +                    cur_size = _clog_append_str(&result, buf, "%", cur_size); +                    break; +                case 't': +                    cur_size = _clog_append_time(&result, buf, lt, +                                                 logger->time_fmt, cur_size); +                    break; +                case 'd': +                    cur_size = _clog_append_time(&result, buf, lt, +                                                 logger->date_fmt, cur_size); +                    break; +                case 'l': +                    cur_size = _clog_append_str(&result, buf, level, cur_size); +                    break; +                case 'n': +                    cur_size = _clog_append_int(&result, buf, sline, cur_size); +                    break; +                case 'f': +                    cur_size = _clog_append_str(&result, buf, sfile, cur_size); +                    break; +                case 'm': +                    cur_size = _clog_append_str(&result, buf, message, +                                                cur_size); +                    break; +            } +            state = NORMAL; +        } +    } + +    return result; +} + +void +_clog_log(const char *sfile, int sline, enum clog_level level, +          int id, const char *fmt, va_list ap) +{ +    /* For speed: Use a stack buffer until message exceeds 4096, then switch +     * to dynamically allocated.  This should greatly reduce the number of +     * memory allocations (and subsequent fragmentation). */ +    char buf[4096]; +    size_t buf_size = 4096; +    char *dynbuf = buf; +    char *message; +    int result; +    struct clog *logger = _clog_loggers[id]; + +    if (!logger) { +        _clog_err("No such logger: %d\n", id); +        return; +    } + +    if (level < logger->level) { +        return; +    } + +    /* Format the message text with the argument list. */ +    result = vsnprintf(dynbuf, buf_size, fmt, ap); +    if ((size_t) result >= buf_size) { +        buf_size = result + 1; +        dynbuf = (char *) malloc(buf_size); +        result = vsnprintf(dynbuf, buf_size, fmt, ap); +        if ((size_t) result >= buf_size) { +            /* Formatting failed -- too large */ +            _clog_err("Formatting failed (1).\n"); +            free(dynbuf); +            return; +        } +    } + +    /* Format according to log format and write to log */ +    { +        char message_buf[4096]; +        message = _clog_format(logger, message_buf, 4096, sfile, sline, +                               CLOG_LEVEL_NAMES[level], dynbuf); +        if (!message) { +            _clog_err("Formatting failed (2).\n"); +            if (dynbuf != buf) { +                free(dynbuf); +            } +            return; +        } +        result = write(logger->fd, message, strlen(message)); +        if (logger->cb) logger->cb(message,strlen(message)); +        if (result == -1) { +            _clog_err("Unable to write to log file: %s\n", strerror(errno)); +        } +        if (message != message_buf) { +            free(message); +        } +        if (dynbuf != buf) { +            free(dynbuf); +        } +#ifndef _MSC_VER +        fsync(logger->fd); +#else +        HANDLE h = (HANDLE) _get_osfhandle(logger->fd); +        if (h != INVALID_HANDLE_VALUE) { +            // This call will fail on a console fd, but that's ok. +            FlushFileBuffers(h); +        } +#endif +    } +} + +void +clog_debug(const char *sfile, int sline, int id, const char *fmt, va_list ap) +{ +    _clog_log(sfile, sline, CLOG_DEBUG, id, fmt, ap); +} + +void +clog_info(const char *sfile, int sline, int id, const char *fmt, va_list ap) +{ +    _clog_log(sfile, sline, CLOG_INFO, id, fmt, ap); +} + +void +clog_warn(const char *sfile, int sline, int id, const char *fmt, va_list ap) +{ +    _clog_log(sfile, sline, CLOG_WARN, id, fmt, ap); +} + +void +clog_error(const char *sfile, int sline, int id, const char *fmt, va_list ap) +{ +    _clog_log(sfile, sline, CLOG_ERROR, id, fmt, ap); +} + +void +_clog_err(const char *fmt, ...) +{ +#ifdef CLOG_SILENT +    (void) fmt; +#else +    va_list ap; + +    va_start(ap, fmt); +    vfprintf(stderr, fmt, ap); +#endif +} + +#endif /* CLOG_MAIN */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __CLOG_H__ */ | 
