=for html <a href="https://travis-ci.org/jddurand/c-genericLogger"><img src="https://travis-ci.org/jddurand/c-genericLogger.svg?branch=master" alt="Travis CI build status" height="18"></a> <a href="https://ci.appveyor.com/project/jddurand/c-genericlogger"><img src="https://ci.appveyor.com/api/projects/status/github/jddurand/c-genericLogger?branch=master&svg=true" alt="AppVeyor CI build status" height="18"></a> <a href="https://badge.fury.io/gh/jddurand%2Fc-genericLogger"><img src="https://badge.fury.io/gh/jddurand%2Fc-genericLogger.svg" alt="GitHub version" height="18"></a> <a href="http://opensource.org/licenses/MIT" rel="nofollow noreferrer"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License MIT" height="18"></a>

=head1 NAME

genericLogger - generic logging interface

=head1 SYNOPSIS

  #include <genericLogger.h>

  /* ---------------------------------- */
  /* User-defined logger implementation */
  /* ---------------------------------- */
  typedef void (*genericLoggerCallback_t)(void *userDatavp,
                                          genericLoggerLevel_t logLeveli,
                                          const char *msgs);

  /* ----------------------- */
  /* Creation, Cloning, Free */
  /* ----------------------- */
  genericLogger_t *genericLogger_newp  (genericLoggerCallback_t logCallbackp,
                                        void *userDatavp,
                                        genericLoggerLevel_t genericLoggerLeveli);
  genericLogger_t *genericLogger_clonep(genericLogger_t *genericLoggerp);
  void             genericLogger_freev (genericLogger_t **genericLoggerpp);

  /* ------- */
  /* Logging */
  /* ------- */
  void genericLogger_logv  (genericLogger_t *genericLoggerp,
                            genericLoggerLevel_t genericLoggerLeveli,
                            const char *fmts,
                            ...);
  void genericLogger_logapv(genericLogger_t *genericLoggerp,
                            genericLoggerLevel_t genericLoggerLeveli,
                            const char *fmts,
                            va_list ap);

  /* ---------------- */
  /* Level management */
  /* ---------------- */
  genericLoggerLevel_t genericLogger_logLevel_seti(genericLogger_t *genericLoggerp,
                                                   genericLoggerLevel_t logLeveli);
  genericLoggerLevel_t genericLogger_logLevel_geti(genericLogger_t *genericLoggerp);

  /* ------------------ */
  /* Context management */
  /* ------------------ */
  void *genericLogger_userDatavp_setp(genericLogger_t *genericLoggerp,
                                      void *userDatavp);
  void *genericLogger_userDatavp_getp(genericLogger_t *genericLoggerp);

=head1 DESCRIPTION

genericLogger is like a portable printf() with level filtering capability, designed to propagate already-formatted messages to user-defined log implementations.

=head1 METHODS

=head2 genericLogger_newp

  typedef void    (*genericLoggerCallback_t)(void *userDatavp,
                    genericLoggerLevel_t logLeveli,
                    const char *msgs);
  genericLogger_t  *genericLogger_newp(genericLoggerCallback_t logCallbackp,
                                       void *userDatavp,
                                       genericLoggerLevel_t genericLoggerLeveli);

Creates and return a generic logger.

The argument C<logCallbackp> may be NULL, or a function pointer to a logging implementation. Such callback will be called with the argument userDatavp, untouched (one call that the callback context), the level, and a pre-formatted message.

The argument C<genericLoggerLeveli> may be one of

=over

=item GENERICLOGGER_LOGLEVEL_TRACE

=item GENERICLOGGER_LOGLEVEL_DEBUG

=item GENERICLOGGER_LOGLEVEL_INFO

=item GENERICLOGGER_LOGLEVEL_NOTICE

=item GENERICLOGGER_LOGLEVEL_WARNING

=item GENERICLOGGER_LOGLEVEL_ERROR

=item GENERICLOGGER_LOGLEVEL_CRITICAL

=item GENERICLOGGER_LOGLEVEL_ALERT

=item GENERICLOGGER_LOGLEVEL_EMERGENCY

=back

it is guaranteed that C<logCallbackp> will not be called whenever a message have a log level lower than C<genericLoggerLeveli>.

If C<logCallbackp> is NULL, then a default built-in implementation is used, with a B<hardcoded> level set to GENERICLOGGER_LEVEL_TRACE (i.e. it will log everything), a B<hardcoded> format string set to C<%d/%m/%Y %H:%M:%S %9s %s> (i.e. date and time a-la-european style, a string giving the level, and the formatted message), and a B<hardcoded> output to standard error.

Returns NULL on failure, system's errno will indicate the reason.

=head2 genericLogger_clonep

  genericLogger_t *genericLogger_clonep(genericLogger_t *genericLoggerp);

Clone the current generic logger and return a new one. The clone becomes I<independant>, and must be freed using genericLogger_freev().

Returns NULL on failure, system's errno will indicate the reason.

=head2 genericLogger_freev

  void genericLogger_freev(genericLogger_t **genericLoggerpp);

Free the generic logger.

=head2 genericLogger_logv

  void genericLogger_logv(genericLogger_t *genericLoggerp,
                          genericLoggerLevel_t genericLoggerLeveli,
                          const char *fmts,
                          ...);

Format the message using C<fmts> format string and eventual remaining parameters, and send it to the log implementation.

=head2 genericLogger_logapv

  void genericLogger_logapv(genericLogger_t *genericLoggerp,
                            genericLoggerLevel_t genericLoggerLeveli,
                            const char *fmts,
                            va_list ap);

va_list version of genericLogger_logv().

=head2 genericLogger_logLevel_seti

  genericLoggerLevel_t genericLogger_logLevel_seti(genericLogger_t *genericLoggerp,
                                                   genericLoggerLevel_t logLeveli);

Set the log level to C<leveLeveli> and returns previous value.

=head2 genericLogger_logLevel_geti

  genericLoggerLevel_t genericLogger_logLevel_geti(genericLogger_t *genericLoggerp);

Return the current log level.

=head2 genericLogger_userDatavp_setp

  void *genericLogger_userDatavp_setp(genericLogger_t *genericLoggerp,
                                      void *userDatavp);

Set the context to C<userDatavp> and returns previous value.

=head2 genericLogger_userDatavp_getp

  void *genericLogger_userDatavp_getp(genericLogger_t *genericLoggerp);

Return the current context.

=head2 genericLogger_versions

  const char *genericLogger_versions();

Return the version number.

=head1 CONVENIENCE MACROS

=head2 GENERICLOGGER_NEW

maps to genericLogger_newp.

=head2 GENERICLOGGER_CUSTOM

maps to genericLogger_newp with a custom log implementation.

=head2 GENERICLOGGER_CLONE

maps to genericLogger_clonep.

=head2 GENERICLOGGER_XXX

=head2 GENERICLOGGER_XXXF

=head2 GENERICLOGGER_XXXAP

maps to all log levels, where C<XXX> is one of

=over

=item TRACE

=item DEBUG

=item INFO

=item NOTICE

=item WARN

=item ERROR

=item CRITICAL

=item ALERT

=item EMERGENCY

=back

For portability reasons, there are two different versions, depending if there are arguments or not: C<XXX> or C<XXXF>, respectively. The C<XXXAP> is when the argument is a C<va_list>.

The C<XXX> and C<XXXF> macros maps to genericLogger_logv(), while C<XXXAP> maps to genericLogger_logapv(), and they all hardcode the level, so that the programmer do not have to write the later.

=head2 GENERICLOGGER_LEVEL_SET

maps to genericLogger_logLevel_seti.

=head2 GENERICLOGGER_LEVEL_GET

maps to genericLogger_logLevel_geti.

=head1 EXAMPLE

  #include <stdlib.h>
  #include <stdio.h>
  #include <errno.h>
  #include <string.h>
  #include <stdarg.h>
  #include <genericLogger.h>

  typedef struct localStruct { char *where; } localStruct_t;

  static void localLogger(void *userDatavp, genericLoggerLevel_t logLeveli, const char *msgs);
  static void forceTrace(genericLogger_t *loggerp, localStruct_t *localStructp, const char *fmts, ...);

  int main() {
    genericLogger_t *loggerp;
    localStruct_t    localStruct;

    loggerp = GENERICLOGGER_CUSTOM(localLogger, &localStruct, GENERICLOGGER_LOGLEVEL_WARNING);
    if (loggerp == NULL) { perror("GENERICLOGGER_CUSTOM"); exit(1); }

    localStruct.where = "main";
    GENERICLOGGER_TRACE(loggerp, "Nothing is logged");
    {
      genericLogger_t *clonep = GENERICLOGGER_CLONE(loggerp);
      if (clonep == NULL) { perror("GENERICLOGGER_CLONE"); exit(1); }

      GENERICLOGGER_LEVEL_SET(clonep, GENERICLOGGER_LOGLEVEL_WARNING)
      GENERICLOGGER_WARNF(loggerp, "Clone is warning, current level is %d", GENERICLOGGER_LEVEL_GET(loggerp));
      forceTrace(clonep, &localStruct, "Clone is forced to trace");
      GENERICLOGGER_FREE(clonep);
    }
    GENERICLOGGER_TRACE(loggerp, "Parent is still not logging");
    GENERICLOGGER_LEVEL_SET(loggerp, GENERICLOGGER_LOGLEVEL_DEBUG)
    GENERICLOGGER_DEBUGF(loggerp, "Parent is logging, current level is %d", GENERICLOGGER_LEVEL_GET(loggerp));
    forceTrace(loggerp, &localStruct, "Parent is forced to trace");
    GENERICLOGGER_FREE(loggerp);

    return 0;
  }

  static void forceTrace(genericLogger_t *loggerp, localStruct_t *localStructp, const char *fmts, ...) {
    va_list ap;
    char *previousWhere = localStructp->where;
    int previousLevel = GENERICLOGGER_LEVEL_GET(loggerp);

    localStructp->where = "mainap";
    va_start(ap, fmts);

    GENERICLOGGER_LEVEL_SET(loggerp, GENERICLOGGER_LOGLEVEL_TRACE)
    GENERICLOGGER_TRACEAP  (loggerp, fmts, ap);
    GENERICLOGGER_LEVEL_SET(loggerp, previousLevel);

    va_end(ap);
    localStructp->where = previousWhere;
  }

  static void localLogger(void *userDatavp, genericLoggerLevel_t logLeveli, const char *msgs) {
    localStruct_t *localStructp = (localStruct_t *) userDatavp;

    fprintf(stderr, "[%-6s] msgs = %s\n", localStructp->where, msgs);
  }

  /*
  [main  ] msgs = Clone is warning, current level is 4
  [mainap] msgs = Clone is forced to trace
  [main  ] msgs = Parent is logging, current level is 1
  [mainap] msgs = Parent is forced to trace
  */