blob: ca19dbcff2ccbbf694c816c6952168cf53f95d58 [file] [log] [blame]
Brian Silvermanf665d692013-02-17 22:11:39 -08001#ifndef AOS_COMMON_LOGGING_LOGGING_IMPL_H_
2#define AOS_COMMON_LOGGING_LOGGING_IMPL_H_
3
4#include <sys/types.h>
5#include <unistd.h>
6#include <stdint.h>
7#include <limits.h>
8#include <string.h>
9#include <stdio.h>
10
11#include <string>
12
13#include "aos/common/logging/logging.h"
14#include "aos/common/type_traits.h"
15#include "aos/common/mutex.h"
16
17// This file has all of the logging implementation. It can't be #included by C
18// code like logging.h can.
Brian Silverman1a572cc2013-03-05 19:58:01 -080019// It is useful for the rest of the logging implementation and other C++ code
20// that needs to do special things with logging.
Brian Silvermanb0893882014-02-10 14:48:30 -080021//
22// It is implemented in logging_impl.cc and logging_interface.cc. They are
23// separate so that code used by logging_impl.cc can link in
24// logging_interface.cc to use logging.
Brian Silvermanf665d692013-02-17 22:11:39 -080025
26namespace aos {
27namespace logging {
28
29// Unless explicitly stated otherwise, format must always be a string constant,
30// args are printf-style arguments for format, and ap is a va_list of args.
Brian Silverman1a572cc2013-03-05 19:58:01 -080031// The validity of format and args together will be checked at compile time
Brian Silvermanf665d692013-02-17 22:11:39 -080032// using a gcc function attribute.
33
34// The struct that the code uses for making logging calls.
35// Packed so that it ends up the same under both linux and vxworks.
36struct __attribute__((packed)) LogMessage {
37#ifdef __VXWORKS__
38 static_assert(sizeof(pid_t) == sizeof(int),
39 "we use task IDs (aka ints) and pid_t interchangeably");
40#endif
41 // Actually the task ID (aka a pointer to the TCB) on the cRIO.
42 pid_t source;
43 static_assert(sizeof(source) == 4, "that's how they get printed");
44 // Per task/thread.
45 uint16_t sequence;
46 log_level level;
47 int32_t seconds, nseconds;
48 char name[100];
49 char message[LOG_MESSAGE_LEN];
50};
51static_assert(shm_ok<LogMessage>::value, "it's going in a queue");
52
53// Returns left > right. LOG_UNKNOWN is most important.
54static inline bool log_gt_important(log_level left, log_level right) {
55 if (left == ERROR) left = 3;
56 if (right == ERROR) right = 3;
57 return left > right;
58}
59
60// Returns a string representing level or "unknown".
61static inline const char *log_str(log_level level) {
62#define DECL_LEVEL(name, value) if (level == name) return #name;
63 DECL_LEVELS;
64#undef DECL_LEVEL
65 return "unknown";
66}
67// Returns the log level represented by str or LOG_UNKNOWN.
68static inline log_level str_log(const char *str) {
69#define DECL_LEVEL(name, value) if (!strcmp(str, #name)) return name;
70 DECL_LEVELS;
71#undef DECL_LEVEL
72 return LOG_UNKNOWN;
73}
74
75// Takes a message and logs it. It will set everything up and then call DoLog
76// for the current LogImplementation.
77void VLog(log_level level, const char *format, va_list ap);
78// Adds to the saved up message.
79void VCork(int line, const char *format, va_list ap);
80// Actually logs the saved up message.
81void VUnCork(int line, log_level level, const char *file,
82 const char *format, va_list ap);
83
84// Will call VLog with the given arguments for the next logger in the chain.
85void LogNext(log_level level, const char *format, ...)
86 __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 2, 3)));
87
88// Represents a system that can actually take log messages and do something
89// useful with them.
90// All of the code (transitively too!) in the DoLog here can make
91// normal LOG and LOG_DYNAMIC calls but can NOT call LOG_CORK/LOG_UNCORK. These
92// calls will not result in DoLog recursing. However, implementations must be
93// safe to call from multiple threads/tasks at the same time. Also, any other
94// overriden methods may end up logging through a given implementation's DoLog.
95class LogImplementation {
96 public:
97 LogImplementation() : next_(NULL) {}
98
99 // The one that this one's implementation logs to.
100 // NULL means that there is no next one.
101 LogImplementation *next() { return next_; }
102 // Virtual in case a subclass wants to perform checks. There will be a valid
103 // logger other than this one available while this is called.
104 virtual void set_next(LogImplementation *next) { next_ = next; }
105
106 private:
107 // Actually logs the given message. Implementations should somehow create a
108 // LogMessage and then call internal::FillInMessage.
109 virtual void DoLog(log_level level, const char *format, va_list ap) = 0;
110
111 // Function of this class so that it can access DoLog.
112 // Levels is how many LogImplementations to not use off the stack.
113 static void DoVLog(log_level, const char *format, va_list ap, int levels);
114 // Friends so that they can access DoVLog.
115 friend void VLog(log_level, const char *, va_list);
116 friend void LogNext(log_level, const char *, ...);
117
118 LogImplementation *next_;
119};
120
Brian Silverman1e8ddfe2013-12-19 16:20:53 -0800121// A log implementation that dumps all messages to a C stdio stream.
122class StreamLogImplementation : public LogImplementation {
123 public:
124 StreamLogImplementation(FILE *stream);
125
126 private:
127 virtual void DoLog(log_level level, const char *format, va_list ap);
128
129 FILE *const stream_;
130};
131
Brian Silvermanf665d692013-02-17 22:11:39 -0800132// Adds another implementation to the stack of implementations in this
133// task/thread.
134// Any tasks/threads created after this call will also use this implementation.
135// The cutoff is when the state in a given task/thread is created (either lazily
136// when needed or by calling Load()).
137// The logging system takes ownership of implementation. It will delete it if
138// necessary, so it must be created with new.
139void AddImplementation(LogImplementation *implementation);
140
141// Must be called at least once per process/load before anything else is
142// called. This function is safe to call multiple times from multiple
143// tasks/threads.
144void Init();
145
146// Forces all of the state that is usually lazily created when first needed to
147// be created when called. Cleanup() will delete it.
148void Load();
149// Resets all information in this task/thread to its initial state.
150// NOTE: This is not the opposite of Init(). The state that this deletes is
151// lazily created when needed. It is actually the opposite of Load().
152void Cleanup();
153
154// This is where all of the code that is only used by actual LogImplementations
155// goes.
156namespace internal {
157
Brian Silvermanb0893882014-02-10 14:48:30 -0800158extern LogImplementation *global_top_implementation;
159
Brian Silvermanf665d692013-02-17 22:11:39 -0800160// An separate instance of this class is accessible from each task/thread.
Brian Silverman1a572cc2013-03-05 19:58:01 -0800161// NOTE: It will get deleted in the child of a fork.
Brian Silvermanf665d692013-02-17 22:11:39 -0800162struct Context {
163 Context();
164
165 // Gets the Context object for this task/thread. Will create one the first
166 // time it is called.
167 //
168 // The implementation for each platform will lazily instantiate a new instance
169 // and then initialize name the first time.
170 // IMPORTANT: The implementation of this can not use logging.
171 static Context *Get();
172 // Deletes the Context object for this task/thread so that the next Get() is
173 // called it will create a new one.
174 // It is valid to call this when Get() has never been called.
175 static void Delete();
176
177 // Which one to log to right now.
178 // Will be NULL if there is no logging implementation to use right now.
179 LogImplementation *implementation;
180
Brian Silvermanab6615c2013-03-05 20:29:29 -0800181 // A name representing this task/(process and thread).
182 // strlen(name.c_str()) must be <= sizeof(LogMessage::name).
183 ::std::string name;
Brian Silvermanf665d692013-02-17 22:11:39 -0800184
185 // What to assign LogMessage::source to in this task/thread.
186 pid_t source;
187
188 // The sequence value to send out with the next message.
189 uint16_t sequence;
190
191 // Contains all of the information related to implementing LOG_CORK and
192 // LOG_UNCORK.
193 struct {
194 char message[LOG_MESSAGE_LEN];
195 int line_min, line_max;
196 // Sets the data up to record a new series of corked logs.
197 void Reset() {
198 message[0] = '\0'; // make strlen of it 0
199 line_min = INT_MAX;
200 line_max = -1;
201 function = NULL;
202 }
203 // The function that the calls are in.
204 // REMEMBER: While the compiler/linker will probably optimize all of the
205 // identical strings to point to the same data, it might not, so using == to
206 // compare this with another value is a bad idea.
207 const char *function;
208 } cork_data;
209};
210
211// Fills in *message according to the given inputs. Used for implementing
212// LogImplementation::DoLog.
213void FillInMessage(log_level level, const char *format, va_list ap,
214 LogMessage *message);
215
216// Prints message to output.
217void PrintMessage(FILE *output, const LogMessage &message);
218
Brian Silvermanb0893882014-02-10 14:48:30 -0800219// Prints format (with ap) into output and correctly deals with the result
220// being too long etc.
221void ExecuteFormat(char *output, size_t output_size, const char *format,
222 va_list ap);
223
Brian Silvermanf665d692013-02-17 22:11:39 -0800224} // namespace internal
225} // namespace logging
226} // namespace aos
227
228#endif // AOS_COMMON_LOGGING_LOGGING_IMPL_H_