aboutsummaryrefslogtreecommitdiffstats
path: root/vespalog/src/vespa/log/log.h
blob: edec06dae5e9037add570c6dae42607a0e66772d (plain) (blame)
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once

#include <cstdarg>
#include <memory>
#include <new>         // for placement new
#include <sys/types.h> // for pid_t

/**
 * If this macro is defined, the regular LOG calls will go through the
 * buffered logger, using the whole messages as tokens.
 *
 * If this macro is not defined, only LOGB calls will go through log buffer
 */
//#define VESPA_LOG_USELOGBUFFERFORREGULARLOG 1

// Used to use anonymous namespaces, but they fail miserably in gdb 5.3

#define LOG_SETUP(...)                          \
static ns_log::Logger ns_log_logger(__VA_ARGS__)  // NOLINT

#define LOG_SETUP_INDIRECT(x, id)                   \
static ns_log::Logger *ns_log_indirect_logger=nullptr; \
static bool logInitialised = false;                 \
static const char *logName = x;                     \
static const char *indirectRcsId = id

#define LOG_WOULD_LOG(level) ns_log_logger.wants(ns_log::Logger::level)
#define LOG_WOULD_VLOG(level) ns_log_logger.wants(level)
#define LOG_INDIRECT_WOULD_LOG(levelName) \
    ns_log_indirect_logger->wants(ns_log::Logger::levelName)

// Define LOG if not using log buffer. Otherwise log buffer will define them
#ifndef VESPA_LOG_USELOGBUFFERFORREGULARLOG
#define LOG(level, ...)                                       \
do {                                                          \
    if (__builtin_expect(LOG_WOULD_LOG(level), false)) {      \
        ns_log_logger.doLog(ns_log::Logger::level,            \
                            __FILE__, __LINE__, __VA_ARGS__); \
    }                                                         \
} while (false)

#define VLOG(level, ...)                                      \
do {                                                          \
    if (__builtin_expect(LOG_WOULD_VLOG(level), false)) {     \
        ns_log_logger.doLog(level,                            \
                            __FILE__, __LINE__, __VA_ARGS__); \
    }                                                         \
} while (false)
#endif

// Must use placement new in the following definition, since the variable
// "logger" must be a valid logger object DURING the construction of the
// logger object itself.
#define LOG_INDIRECT(level, ...)                                        \
do {                                                                    \
    if (!logInitialised) {                                              \
        logInitialised = true;                                          \
        ns_log_indirect_logger =                                        \
                static_cast<Logger *>(                                  \
                        malloc(sizeof *ns_log_indirect_logger));        \
        new (ns_log_indirect_logger) Logger(logName, indirectRcsId);    \
    }                                                                   \
    if (LOG_INDIRECT_WOULD_LOG(level)) {                                \
        ns_log_indirect_logger->doLog(ns_log::Logger::level,            \
                                      __FILE__, __LINE__, __VA_ARGS__); \
    }                                                                   \
} while (false)


#define EV_STARTING(name)                    \
do {                                         \
    if (LOG_WOULD_LOG(event)) {              \
        ns_log_logger.doEventStarting(name); \
    }                                        \
} while (false)

#define EV_STOPPING(name,why)                     \
do {                                              \
    if (LOG_WOULD_LOG(event)) {                   \
        ns_log_logger.doEventStopping(name, why); \
    }                                             \
} while (false)

#define EV_STARTED(name)                        \
do {                                            \
    if (LOG_WOULD_LOG(event)) {                 \
        ns_log_logger.doEventStarted(name);     \
    }                                           \
} while (false)

#define EV_STOPPED(name,pid,exitcode)           \
do {                                            \
    if (LOG_WOULD_LOG(event)) {                 \
        ns_log_logger.doEventStopped(name, pid, \
                                     exitcode); \
    }                                           \
} while (false)

#define EV_CRASH(name,pid,signal)                      \
do {                                                   \
    if (LOG_WOULD_LOG(event)) {                        \
        ns_log_logger.doEventCrash(name, pid, signal); \
    }                                                  \
} while (false)

#define EV_PROGRESS(name, ...)                      \
do {                                                \
    if (LOG_WOULD_LOG(event)) {                     \
        ns_log_logger.doEventProgress(name,         \
                                      __VA_ARGS__); \
    }                                               \
} while (false)

#define EV_COUNT(name,value)                     \
    do {                                         \
    if (LOG_WOULD_LOG(event)) {                  \
        ns_log_logger.doEventCount(name, value); \
    }                                            \
} while (false)

#define EV_VALUE(name,value)                     \
do {                                             \
    if (LOG_WOULD_LOG(event)) {                  \
        ns_log_logger.doEventValue(name, value); \
    }                                            \
} while (false)

#define EV_STATE(name,value)                     \
do {                                             \
    if (LOG_WOULD_LOG(event)) {                  \
        ns_log_logger.doEventState(name, value); \
    }                                            \
} while (false)

namespace ns_log {

class LogTarget;
class ControlFile;
struct Timer;

class Logger {
public:
    enum LogLevel {
        fatal, error, warning, config, info, event, debug, spam, NUM_LOGLEVELS
    };
    static const char *logLevelNames[];
    static enum LogLevel parseLevel(const char *lname);

    /**
     * Set by unit tests to avoid needing to match different pids. Will be
     * false by default.
     */
    static bool fakePid;

private:
    unsigned int *_logLevels;

    static char _prefix[64];
    static LogTarget *_target;
    char _rcsId[256];
    static int _numInstances;

    static char _controlName[1024];
    static char _hostname[1024];
    static char _serviceName[1024];
    static const char _hexdigit[17];
    static ControlFile *_controlFile;

    static void setTarget();

    char _appendix[256];

    std::unique_ptr<Timer> _timer;

    void ensurePrefix(const char *name);
    void ensureHostname();
    void ensureControlName();
    void ensureServiceName();

    int tryLog(int sizeofPayload, LogLevel level, const char *file, int line,
               const char *fmt, va_list args);
public:
    ~Logger();
    explicit Logger(const char *name, const char *rcsId = nullptr);
    Logger(const Logger &) = delete;
    Logger & operator=(const Logger &) = delete;
    int setRcsId(const char *rcsId);
    static const char *levelName(LogLevel level);

    inline bool wants(LogLevel level);
    void doLog(LogLevel level, const char *file, int line,
               const char *fmt, ...) __attribute__((format(printf,5,6)));
    /**
     * The log buffer creates timestamp and creates the log message itself.
     * Thus the core log functionality has been moved here to avoid doing it
     * twice. doLogCore is called from doLog and from the log buffer.
     *
     * @param timestamp Time in microseconds.
     */
    void doLogCore(const Timer &, LogLevel level,
                   const char *file, int line, const char *msg, size_t msgSize);
    void doEventStarting(const char *name);
    void doEventStopping(const char *name, const char *why);
    void doEventStarted(const char *name);
    void doEventStopped(const char *name, pid_t pid, int exitCode);
    void doEventCrash(const char *name, pid_t pid, int signal);
    void doEventProgress(const char *name, double value, double total = 0);
    void doEventCount(const char *name, uint64_t value);
    void doEventValue(const char *name, double value);
    void doEventState(const char *name, const char *value);

    // Only for unit testing
    void setTimer(std::unique_ptr<Timer> timer);

    // Only for internal use
    static LogTarget *getCurrentTarget();
};


#define CHARS_TO_UINT(a,b,c,d)                                          \
(static_cast<unsigned char>(a)                                          \
 | static_cast<unsigned int>(static_cast<unsigned char>(b) << 8u)       \
 | static_cast<unsigned int>(static_cast<unsigned char>(c) << 16u)      \
 | static_cast<unsigned int>(static_cast<unsigned char>(d) << 24u))

inline bool Logger::wants(LogLevel level)
{
    return _logLevels[level] == CHARS_TO_UINT(' ', ' ', 'O', 'N');
}

[[noreturn]] extern void log_assert_fail(const char *assertion, const char *file, uint32_t line);

[[noreturn]] extern void log_abort(const char *message, const char *file, uint32_t line);

} // end namespace log

//======================================//
// LOG_ASSERT and LOG_ABORT definitions //
//======================================//

#ifndef __STRING
#define __STRING(x) #x
#endif

#define LOG_ABORT(msg) \
  (ns_log::log_abort(msg, __FILE__, __LINE__))

#ifndef NDEBUG
#define LOG_ASSERT(expr) \
  ((void) ((expr) ? 0 : \
           (ns_log::log_assert_fail(__STRING(expr), \
                                    __FILE__, __LINE__), 0)))
#else
#define LOG_ASSERT(expr)
#endif // #ifndef NDEBUG