summaryrefslogtreecommitdiff
path: root/src/logger/logger.hpp
blob: a99648c70e41cabc2976d95276cf2789f61a0b47 (plain)
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#pragma once


/**
 * Singleton used in logger macros to write into files or stdout, with
 * various levels of severity.
 * Only the macros should be used.
 * @class Logger
 */

#include <memory>
#include <iostream>
#include <fstream>
#include <sstream>

#define debug_lvl 0
#define info_lvl 1
#define warning_lvl 2
#define error_lvl 3

#include "biboumi.h"
#ifdef SYSTEMD_FOUND
#define SD_JOURNAL_SUPPRESS_LOCATION
# include <systemd/sd-daemon.h>
# include <systemd/sd-journal.h>
#else
# define SD_DEBUG    "[DEBUG]: "
# define SD_INFO     "[INFO]: "
# define SD_WARNING  "[WARNING]: "
# define SD_ERR      "[ERROR]: "
# define LOG_ERR 3
# define LOG_WARNING 4
# define LOG_INFO 6
# define LOG_DEBUG 7
#endif

// Macro defined to get the filename instead of the full path. But if it is
// not properly defined by the build system, we fallback to __FILE__
#ifndef __FILENAME__
# define __FILENAME__ __FILE__
#endif


/**
 * A buffer, used to construct an ostream that does nothing
 * when we output data in it
 */
class NullBuffer: public std::streambuf
{
 public:
  int overflow(int c) { return c; }
};

class Logger
{
public:
  static std::unique_ptr<Logger>& instance();
  std::ostream& get_stream(const int);
  Logger(const int log_level, const std::string& log_file);
  Logger(const int log_level);

  Logger(const Logger&) = delete;
  Logger& operator=(const Logger&) = delete;
  Logger(Logger&&) = delete;
  Logger& operator=(Logger&&) = delete;

#ifdef SYSTEMD_FOUND
  bool use_stdout() const
  {
    return this->stream.rdbuf() == std::cout.rdbuf();
  }

  bool use_systemd{false};
#endif

private:
  const int log_level;
  std::ofstream ofstream{};
  std::ostream stream;

  NullBuffer null_buffer;
  std::ostream null_stream;
};

namespace logging_details
{
  template <typename T>
  void log(std::ostream& os, const T& arg)
  {
    os << arg << std::endl;
  }

  template <typename T, typename... U>
  void log(std::ostream& os, const T& first, U&&... rest)
  {
    os << first;
    log(os, std::forward<U>(rest)...);
  }

  template <typename... U>
  void do_logging(const int level, int syslog_level, const char* src_file, int line, U&&... args)
  {
  #ifdef SYSTEMD_FOUND
    if (Logger::instance()->use_systemd)
      {
        (void)level;
        std::ostringstream os;
        log(os, std::forward<U>(args)...);
        sd_journal_send("MESSAGE=%s", os.str().data(),
                        "PRIORITY=%i", syslog_level,
                        "CODE_FILE=%s", src_file,
                        "CODE_LINE=%i", line,
                        nullptr);
      }
    else
      {
  #endif
        (void)syslog_level;
        static const char* priority_names[] = {"DEBUG", "INFO", "WARNING", "ERROR"};
        auto& os = Logger::instance()->get_stream(level);
        os << '[' << priority_names[level] << "]: " << src_file << ':' << line << ":\t";
        log(os, std::forward<U>(args)...);
#ifdef SYSTEMD_FOUND
      }
#endif
  }
}

#define log_debug(...) logging_details::do_logging(debug_lvl, LOG_DEBUG, __FILENAME__, __LINE__, __VA_ARGS__)

#define log_info(...) logging_details::do_logging(info_lvl, LOG_INFO, __FILENAME__, __LINE__, __VA_ARGS__)

#define log_warning(...) logging_details::do_logging(warning_lvl, LOG_WARNING, __FILENAME__, __LINE__, __VA_ARGS__)

#define log_error(...) logging_details::do_logging(error_lvl, LOG_ERR, __FILENAME__, __LINE__, __VA_ARGS__)