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