blob: bdbe585a75899b438c7d2f9d12d390a16472a84c [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>
Brian Silvermand6974f42014-02-14 13:39:21 -080012#include <functional>
Brian Silvermanf665d692013-02-17 22:11:39 -080013
14#include "aos/common/logging/logging.h"
15#include "aos/common/type_traits.h"
16#include "aos/common/mutex.h"
17
Brian Silvermand6974f42014-02-14 13:39:21 -080018namespace aos {
19
20class MessageType;
21
22} // namespace aos
23
Brian Silvermanf665d692013-02-17 22:11:39 -080024// This file has all of the logging implementation. It can't be #included by C
25// code like logging.h can.
Brian Silverman1a572cc2013-03-05 19:58:01 -080026// It is useful for the rest of the logging implementation and other C++ code
27// that needs to do special things with logging.
Brian Silvermanb0893882014-02-10 14:48:30 -080028//
29// It is implemented in logging_impl.cc and logging_interface.cc. They are
30// separate so that code used by logging_impl.cc can link in
31// logging_interface.cc to use logging.
Brian Silvermanf665d692013-02-17 22:11:39 -080032
33namespace aos {
34namespace logging {
35
36// Unless explicitly stated otherwise, format must always be a string constant,
37// args are printf-style arguments for format, and ap is a va_list of args.
Brian Silverman1a572cc2013-03-05 19:58:01 -080038// The validity of format and args together will be checked at compile time
Brian Silvermanf665d692013-02-17 22:11:39 -080039// using a gcc function attribute.
40
41// The struct that the code uses for making logging calls.
42// Packed so that it ends up the same under both linux and vxworks.
43struct __attribute__((packed)) LogMessage {
44#ifdef __VXWORKS__
45 static_assert(sizeof(pid_t) == sizeof(int),
46 "we use task IDs (aka ints) and pid_t interchangeably");
47#endif
48 // Actually the task ID (aka a pointer to the TCB) on the cRIO.
49 pid_t source;
50 static_assert(sizeof(source) == 4, "that's how they get printed");
51 // Per task/thread.
52 uint16_t sequence;
53 log_level level;
54 int32_t seconds, nseconds;
55 char name[100];
56 char message[LOG_MESSAGE_LEN];
57};
58static_assert(shm_ok<LogMessage>::value, "it's going in a queue");
59
60// Returns left > right. LOG_UNKNOWN is most important.
61static inline bool log_gt_important(log_level left, log_level right) {
62 if (left == ERROR) left = 3;
63 if (right == ERROR) right = 3;
64 return left > right;
65}
66
67// Returns a string representing level or "unknown".
68static inline const char *log_str(log_level level) {
69#define DECL_LEVEL(name, value) if (level == name) return #name;
70 DECL_LEVELS;
71#undef DECL_LEVEL
72 return "unknown";
73}
74// Returns the log level represented by str or LOG_UNKNOWN.
75static inline log_level str_log(const char *str) {
76#define DECL_LEVEL(name, value) if (!strcmp(str, #name)) return name;
77 DECL_LEVELS;
78#undef DECL_LEVEL
79 return LOG_UNKNOWN;
80}
81
82// Takes a message and logs it. It will set everything up and then call DoLog
83// for the current LogImplementation.
84void VLog(log_level level, const char *format, va_list ap);
85// Adds to the saved up message.
86void VCork(int line, const char *format, va_list ap);
87// Actually logs the saved up message.
88void VUnCork(int line, log_level level, const char *file,
89 const char *format, va_list ap);
90
91// Will call VLog with the given arguments for the next logger in the chain.
92void LogNext(log_level level, const char *format, ...)
93 __attribute__((format(LOG_PRINTF_FORMAT_TYPE, 2, 3)));
94
Brian Silvermand6974f42014-02-14 13:39:21 -080095// Will take a structure and log it.
96template <class T>
97void DoLogStruct(log_level, const ::std::string &, const T &);
98
Brian Silvermanf665d692013-02-17 22:11:39 -080099// Represents a system that can actually take log messages and do something
100// useful with them.
101// All of the code (transitively too!) in the DoLog here can make
102// normal LOG and LOG_DYNAMIC calls but can NOT call LOG_CORK/LOG_UNCORK. These
103// calls will not result in DoLog recursing. However, implementations must be
104// safe to call from multiple threads/tasks at the same time. Also, any other
105// overriden methods may end up logging through a given implementation's DoLog.
106class LogImplementation {
107 public:
108 LogImplementation() : next_(NULL) {}
109
110 // The one that this one's implementation logs to.
111 // NULL means that there is no next one.
112 LogImplementation *next() { return next_; }
113 // Virtual in case a subclass wants to perform checks. There will be a valid
114 // logger other than this one available while this is called.
115 virtual void set_next(LogImplementation *next) { next_ = next; }
116
117 private:
118 // Actually logs the given message. Implementations should somehow create a
119 // LogMessage and then call internal::FillInMessage.
120 virtual void DoLog(log_level level, const char *format, va_list ap) = 0;
121
Brian Silvermand6974f42014-02-14 13:39:21 -0800122 // Logs the contents of an auto-generated structure. The implementation here
123 // just converts it to a string with PrintMessage and then calls DoLog with
124 // that, however some implementations can be a lot more efficient than that.
125 // size and type are the result of calling Size() and Type() on the type of
126 // the message.
127 // serialize will call Serialize on the message.
128 virtual void LogStruct(log_level level, const ::std::string &message,
129 size_t size, const MessageType *type,
130 const ::std::function<size_t(char *)> &serialize);
131
132 // These functions call similar methods on the "current" LogImplementation or
133 // Die if they can't find one.
134 // levels is how many LogImplementations to not use off the stack.
Brian Silvermanf665d692013-02-17 22:11:39 -0800135 static void DoVLog(log_level, const char *format, va_list ap, int levels);
Brian Silvermand6974f42014-02-14 13:39:21 -0800136 // This one is implemented in queue_logging.cc.
137 static void DoLogStruct(log_level level, const ::std::string &message,
138 size_t size, const MessageType *type,
139 const ::std::function<size_t(char *)> &serialize,
140 int levels);
141
142 // Friends so that they can access the static Do* functions.
Brian Silvermanf665d692013-02-17 22:11:39 -0800143 friend void VLog(log_level, const char *, va_list);
144 friend void LogNext(log_level, const char *, ...);
Brian Silvermand6974f42014-02-14 13:39:21 -0800145 template <class T>
146 friend void DoLogStruct(log_level, const ::std::string &, const T &);
Brian Silvermanf665d692013-02-17 22:11:39 -0800147
148 LogImplementation *next_;
149};
150
Brian Silverman1e8ddfe2013-12-19 16:20:53 -0800151// A log implementation that dumps all messages to a C stdio stream.
152class StreamLogImplementation : public LogImplementation {
153 public:
154 StreamLogImplementation(FILE *stream);
155
156 private:
157 virtual void DoLog(log_level level, const char *format, va_list ap);
158
159 FILE *const stream_;
160};
161
Brian Silvermanf665d692013-02-17 22:11:39 -0800162// Adds another implementation to the stack of implementations in this
163// task/thread.
164// Any tasks/threads created after this call will also use this implementation.
165// The cutoff is when the state in a given task/thread is created (either lazily
166// when needed or by calling Load()).
167// The logging system takes ownership of implementation. It will delete it if
168// necessary, so it must be created with new.
169void AddImplementation(LogImplementation *implementation);
170
171// Must be called at least once per process/load before anything else is
172// called. This function is safe to call multiple times from multiple
173// tasks/threads.
174void Init();
175
176// Forces all of the state that is usually lazily created when first needed to
177// be created when called. Cleanup() will delete it.
178void Load();
179// Resets all information in this task/thread to its initial state.
180// NOTE: This is not the opposite of Init(). The state that this deletes is
181// lazily created when needed. It is actually the opposite of Load().
182void Cleanup();
183
184// This is where all of the code that is only used by actual LogImplementations
185// goes.
186namespace internal {
187
Brian Silvermanb0893882014-02-10 14:48:30 -0800188extern LogImplementation *global_top_implementation;
189
Brian Silvermanf665d692013-02-17 22:11:39 -0800190// An separate instance of this class is accessible from each task/thread.
Brian Silverman1a572cc2013-03-05 19:58:01 -0800191// NOTE: It will get deleted in the child of a fork.
Brian Silvermanf665d692013-02-17 22:11:39 -0800192struct Context {
193 Context();
194
195 // Gets the Context object for this task/thread. Will create one the first
196 // time it is called.
197 //
198 // The implementation for each platform will lazily instantiate a new instance
199 // and then initialize name the first time.
200 // IMPORTANT: The implementation of this can not use logging.
201 static Context *Get();
202 // Deletes the Context object for this task/thread so that the next Get() is
203 // called it will create a new one.
204 // It is valid to call this when Get() has never been called.
205 static void Delete();
206
207 // Which one to log to right now.
208 // Will be NULL if there is no logging implementation to use right now.
209 LogImplementation *implementation;
210
Brian Silvermanab6615c2013-03-05 20:29:29 -0800211 // A name representing this task/(process and thread).
212 // strlen(name.c_str()) must be <= sizeof(LogMessage::name).
213 ::std::string name;
Brian Silvermanf665d692013-02-17 22:11:39 -0800214
215 // What to assign LogMessage::source to in this task/thread.
216 pid_t source;
217
218 // The sequence value to send out with the next message.
219 uint16_t sequence;
220
221 // Contains all of the information related to implementing LOG_CORK and
222 // LOG_UNCORK.
223 struct {
224 char message[LOG_MESSAGE_LEN];
225 int line_min, line_max;
226 // Sets the data up to record a new series of corked logs.
227 void Reset() {
228 message[0] = '\0'; // make strlen of it 0
229 line_min = INT_MAX;
230 line_max = -1;
231 function = NULL;
232 }
233 // The function that the calls are in.
234 // REMEMBER: While the compiler/linker will probably optimize all of the
235 // identical strings to point to the same data, it might not, so using == to
236 // compare this with another value is a bad idea.
237 const char *function;
238 } cork_data;
239};
240
241// Fills in *message according to the given inputs. Used for implementing
242// LogImplementation::DoLog.
243void FillInMessage(log_level level, const char *format, va_list ap,
244 LogMessage *message);
245
246// Prints message to output.
247void PrintMessage(FILE *output, const LogMessage &message);
248
Brian Silvermanb0893882014-02-10 14:48:30 -0800249// Prints format (with ap) into output and correctly deals with the result
250// being too long etc.
251void ExecuteFormat(char *output, size_t output_size, const char *format,
252 va_list ap);
253
Brian Silvermand6974f42014-02-14 13:39:21 -0800254// Runs the given function with the current LogImplementation (handles switching
255// it out while running function etc).
256void RunWithCurrentImplementation(
257 int levels, ::std::function<void(LogImplementation *)> function);
258
Brian Silvermanf665d692013-02-17 22:11:39 -0800259} // namespace internal
260} // namespace logging
261} // namespace aos
262
263#endif // AOS_COMMON_LOGGING_LOGGING_IMPL_H_