root/src/apps/haikudepot/model/Logger.h
/*
 * Copyright 2017-2022, Andrew Lindesay <apl@lindesay.co.nz>.
 * All rights reserved. Distributed under the terms of the MIT License.
 */
#ifndef LOGGER_H
#define LOGGER_H

#include <String.h>
#include <File.h>
#include <Path.h>

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>


#define MILLIS_IN_DAY 86400000L

#define HDLOGLEVELCHAR(L) ( \
        L == LOG_LEVEL_INFO ? 'I' \
        : L == LOG_LEVEL_DEBUG ? 'D' \
        : L == LOG_LEVEL_TRACE ? 'T' \
        : L == LOG_LEVEL_ERROR ? 'E' \
        : '?')

// These macros allow for standardized logging to be output.
// The use of macros in this way means that the use of the log is concise where
// it is used and also because the macro unwraps to a block contained with a
// condition statement, if the log level is not sufficient to trigger the log
// line then there is no computational cost to running over the log space.  This
// is because the arguments will not be evaluated.  Avoiding all of the
// conditional clauses in the code to prevent this otherwise would be
// cumbersome.

// The time element @<time> is presented in milliseconds on the current day.

#define HDLOGPREFIX(L) printf("@%08" B_PRId64 " {%c} <t:%" B_PRIu32 "> ", \
        ((system_time() / 1000L) % MILLIS_IN_DAY), \
        HDLOGLEVELCHAR(L), \
        Logger::CurrentThreadIndicator() \
);

#define HDLOG(L, M...) do { if (Logger::IsLevelEnabled(L)) { \
        HDLOGPREFIX(L) \
        printf(M); \
        putchar('\n'); \
} } while (0)

#define HDINFO(M...) HDLOG(LOG_LEVEL_INFO, M)
#define HDDEBUG(M...) HDLOG(LOG_LEVEL_DEBUG, M)
#define HDTRACE(M...) HDLOG(LOG_LEVEL_TRACE, M)
#define HDERROR(M...) HDLOG(LOG_LEVEL_ERROR, M)

#define HDFATAL(M...) do { \
        printf("{!} (failed @ %s:%d) ", __FILE__, __LINE__); \
        printf(M); \
        putchar('\n'); \
        exit(EXIT_FAILURE); \
} while (0)

typedef enum log_level {
        LOG_LEVEL_OFF           = 1,
        LOG_LEVEL_ERROR         = 2,
        LOG_LEVEL_INFO          = 3,
        LOG_LEVEL_DEBUG         = 4,
        LOG_LEVEL_TRACE         = 5
} log_level;


class Logger {
public:
        static  log_level                       Level();
        static  void                            SetLevel(log_level value);
        static  bool                            SetLevelByName(const char *name);

        static  const char*                     NameForLevel(log_level value);

        static  bool                            IsLevelEnabled(log_level value);
        static  bool                            IsInfoEnabled();
        static  bool                            IsDebugEnabled();
        static  bool                            IsTraceEnabled();

        static  uint32                          CurrentThreadIndicator();

private:
        static  log_level                       fLevel;
};


#endif // LOGGER_H