1 /* Simple Plugin API
2  *
3  * Copyright © 2018 Wim Taymans
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #ifndef SPA_LOG_H
26 #define SPA_LOG_H
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 
32 #include <stdarg.h>
33 
34 #include <spa/utils/type.h>
35 #include <spa/utils/defs.h>
36 #include <spa/utils/hook.h>
37 
38 /** \defgroup spa_log Log
39  * Logging interface
40  */
41 
42 /**
43  * \addtogroup spa_log
44  * \{
45  */
46 
47 /** The default log topic. Redefine this in your code to
48  * allow for the spa_log_* macros to work correctly, e.g:
49  *
50  * \code{.c}
51  * struct spa_log_topic *mylogger;
52  * #undef SPA_LOG_TOPIC_DEFAULT
53  * #define SPA_LOG_TOPIC_DEFAULT mylogger
54  * \endcode
55  */
56 #define SPA_LOG_TOPIC_DEFAULT NULL
57 
58 enum spa_log_level {
59 	SPA_LOG_LEVEL_NONE = 0,
60 	SPA_LOG_LEVEL_ERROR,
61 	SPA_LOG_LEVEL_WARN,
62 	SPA_LOG_LEVEL_INFO,
63 	SPA_LOG_LEVEL_DEBUG,
64 	SPA_LOG_LEVEL_TRACE,
65 };
66 
67 /**
68  * The Log interface
69  */
70 #define SPA_TYPE_INTERFACE_Log	SPA_TYPE_INFO_INTERFACE_BASE "Log"
71 
72 
73 struct spa_log {
74 	/** the version of this log. This can be used to expand this
75 	 * structure in the future */
76 #define SPA_VERSION_LOG		0
77 	struct spa_interface iface;
78 	/**
79 	 * Logging level, everything above this level is not logged
80 	 */
81 	enum spa_log_level level;
82 };
83 
84 /**
85  * \struct spa_log_topic
86  *
87  * Identifier for a topic. Topics are string-based filters that logically
88  * group messages together. An implementation may decide to filter different
89  * topics on different levels, for example the "protocol" topic may require
90  * debug level TRACE while the "core" topic defaults to debug level INFO.
91  *
92  * spa_log_topics require a spa_log_methods version of 1 or higher.
93  */
94 struct spa_log_topic {
95 #define SPA_VERSION_LOG_TOPIC	0
96 	/** the version of this topic. This can be used to expand this
97 	 * structure in the future */
98 	uint32_t version;
99 	/** The string identifier for the topic */
100 	const char *topic;
101 	/** Logging level set for this topic */
102 	enum spa_log_level level;
103 	/** False if this topic follows the \ref spa_log level */
104 	bool has_custom_level;
105 };
106 
107 struct spa_log_methods {
108 #define SPA_VERSION_LOG_METHODS		1
109 	uint32_t version;
110 	/**
111 	 * Log a message with the given log level.
112 	 *
113 	 * \note If compiled with this header, this function is only called
114 	 * for implementations of version 0. For versions 1 and above, see
115 	 * logt() instead.
116 	 *
117 	 * \param log a spa_log
118 	 * \param level a spa_log_level
119 	 * \param file the file name
120 	 * \param line the line number
121 	 * \param func the function name
122 	 * \param fmt printf style format
123 	 * \param ... format arguments
124 	 */
125 	void (*log) (void *object,
126 		     enum spa_log_level level,
127 		     const char *file,
128 		     int line,
129 		     const char *func,
130 		     const char *fmt, ...) SPA_PRINTF_FUNC(6, 7);
131 
132 	/**
133 	 * Log a message with the given log level.
134 	 *
135 	 * \note If compiled with this header, this function is only called
136 	 * for implementations of version 0. For versions 1 and above, see
137 	 * logtv() instead.
138 	 *
139 	 * \param log a spa_log
140 	 * \param level a spa_log_level
141 	 * \param file the file name
142 	 * \param line the line number
143 	 * \param func the function name
144 	 * \param fmt printf style format
145 	 * \param args format arguments
146 	 */
147 	void (*logv) (void *object,
148 		      enum spa_log_level level,
149 		      const char *file,
150 		      int line,
151 		      const char *func,
152 		      const char *fmt,
153 		      va_list args) SPA_PRINTF_FUNC(6, 0);
154 	/**
155 	 * Log a message with the given log level for the given topic.
156 	 *
157 	 * \note Callers that do not use topic-based logging (version 0), the \a
158 	 * topic is NULL
159 	 *
160 	 * \param log a spa_log
161 	 * \param level a spa_log_level
162 	 * \param topic the topic for this message, may be NULL
163 	 * \param file the file name
164 	 * \param line the line number
165 	 * \param func the function name
166 	 * \param fmt printf style format
167 	 * \param ... format arguments
168 	 *
169 	 * \since 1
170 	 */
171 	void (*logt) (void *object,
172 		     enum spa_log_level level,
173 		     const struct spa_log_topic *topic,
174 		     const char *file,
175 		     int line,
176 		     const char *func,
177 		     const char *fmt, ...) SPA_PRINTF_FUNC(7, 8);
178 
179 	/**
180 	 * Log a message with the given log level for the given topic.
181 	 *
182 	 * \note For callers that do not use topic-based logging (version 0),
183 	 * the \a topic is NULL
184 	 *
185 	 * \param log a spa_log
186 	 * \param level a spa_log_level
187 	 * \param topic the topic for this message, may be NULL
188 	 * \param file the file name
189 	 * \param line the line number
190 	 * \param func the function name
191 	 * \param fmt printf style format
192 	 * \param args format arguments
193 	 *
194 	 * \since 1
195 	 */
196 	void (*logtv) (void *object,
197 		      enum spa_log_level level,
198 		      const struct spa_log_topic *topic,
199 		      const char *file,
200 		      int line,
201 		      const char *func,
202 		      const char *fmt,
203 		      va_list args) SPA_PRINTF_FUNC(7, 0);
204 
205 	/**
206 	 * Initializes a \ref spa_log_topic to the correct logging level.
207 	 *
208 	 * \since 1
209 	 */
210 	void (*topic_init) (void *object, struct spa_log_topic *topic);
211 };
212 
213 
214 #define SPA_LOG_TOPIC(v, t) \
215    (struct spa_log_topic){ .version = v, .topic = (t)}
216 
217 #define spa_log_topic_init(l, topic)				\
218 do {								\
219 	struct spa_log *_l = l;					\
220 	if (SPA_LIKELY(_l)) {					\
221 		struct spa_interface *_if = &_l->iface;		\
222 		spa_interface_call(_if, struct spa_log_methods,	\
223 				topic_init, 1, topic);		\
224 	}							\
225 } while(0)
226 
227 /* Unused, left for backwards compat */
228 #define spa_log_level_enabled(l,lev) ((l) && (l)->level >= (lev))
229 
230 #define spa_log_level_topic_enabled(l,topic,lev)		\
231 ({								\
232 	struct spa_log *_log = l;				\
233 	enum spa_log_level _lev = _log ? _log->level : SPA_LOG_LEVEL_NONE;		\
234 	struct spa_log_topic *_t = (struct spa_log_topic *)topic; \
235 	if (_t && _t->has_custom_level)							\
236 		_lev = _t->level;				\
237 	_lev >= lev;						\
238 })
239 
240 /* Transparently calls to version 0 log if v1 is not supported */
241 #define spa_log_logt(l,lev,topic,...)					\
242 ({									\
243 	struct spa_log *_l = l;						\
244 	struct spa_interface *_if = &_l->iface;				\
245 	if (SPA_UNLIKELY(spa_log_level_topic_enabled(_l, topic, lev))) { \
246 		if (!spa_interface_call(_if,				\
247 				struct spa_log_methods, logt, 1,	\
248 				lev, topic,				\
249 				__VA_ARGS__))				\
250 		    spa_interface_call(_if,				\
251 				struct spa_log_methods, log, 0,		\
252 				lev, __VA_ARGS__);			\
253 	}								\
254 })
255 
256 /* Transparently calls to version 0 logv if v1 is not supported */
257 #define spa_log_logtv(l,lev,topic,...)					\
258 ({									\
259 	struct spa_log *_l = l;						\
260 	struct spa_interface *_if = &_l->iface;				\
261 	if (SPA_UNLIKELY(spa_log_level_topic_enabled(_l, topic, lev))) { \
262 		if (!spa_interface_call(_if,				\
263 				struct spa_log_methods, logtv, 1,	\
264 				lev, topic,				\
265 				__VA_ARGS__))				\
266 		    spa_interface_call(_if,				\
267 				struct spa_log_methods, logv, 0,	\
268 				lev, __VA_ARGS__);			\
269 	}								\
270 })
271 
272 #define spa_log_log(l,lev,...)					\
273 	spa_log_logt(l,lev,SPA_LOG_TOPIC_DEFAULT,__VA_ARGS__)
274 
275 #define spa_log_logv(l,lev,...)					\
276 	spa_log_logtv(l,lev,SPA_LOG_TOPIC_DEFAULT,__VA_ARGS__)
277 
278 #define spa_log_error(l,...)	spa_log_log(l,SPA_LOG_LEVEL_ERROR,__FILE__,__LINE__,__func__,__VA_ARGS__)
279 #define spa_log_warn(l,...)	spa_log_log(l,SPA_LOG_LEVEL_WARN,__FILE__,__LINE__,__func__,__VA_ARGS__)
280 #define spa_log_info(l,...)	spa_log_log(l,SPA_LOG_LEVEL_INFO,__FILE__,__LINE__,__func__,__VA_ARGS__)
281 #define spa_log_debug(l,...)	spa_log_log(l,SPA_LOG_LEVEL_DEBUG,__FILE__,__LINE__,__func__,__VA_ARGS__)
282 #define spa_log_trace(l,...)	spa_log_log(l,SPA_LOG_LEVEL_TRACE,__FILE__,__LINE__,__func__,__VA_ARGS__)
283 
284 #define spa_logt_error(l,t,...)	spa_log_logt(l,SPA_LOG_LEVEL_ERROR,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
285 #define spa_logt_warn(l,t,...)	spa_log_logt(l,SPA_LOG_LEVEL_WARN,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
286 #define spa_logt_info(l,t,...)	spa_log_logt(l,SPA_LOG_LEVEL_INFO,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
287 #define spa_logt_debug(l,t,...)	spa_log_logt(l,SPA_LOG_LEVEL_DEBUG,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
288 #define spa_logt_trace(l,t,...)	spa_log_logt(l,SPA_LOG_LEVEL_TRACE,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
289 
290 #ifndef FASTPATH
291 #define spa_log_trace_fp(l,...)	spa_log_log(l,SPA_LOG_LEVEL_TRACE,__FILE__,__LINE__,__func__,__VA_ARGS__)
292 #else
293 #define spa_log_trace_fp(l,...)
294 #endif
295 
296 /** \fn spa_log_error */
297 
298 /** keys can be given when initializing the logger handle */
299 #define SPA_KEY_LOG_LEVEL		"log.level"		/**< the default log level */
300 #define SPA_KEY_LOG_COLORS		"log.colors"		/**< enable colors in the logger */
301 #define SPA_KEY_LOG_FILE		"log.file"		/**< log to the specified file instead of
302 								  *  stderr. */
303 #define SPA_KEY_LOG_TIMESTAMP		"log.timestamp"		/**< log timestamps */
304 #define SPA_KEY_LOG_LINE		"log.line"		/**< log file and line numbers */
305 #define SPA_KEY_LOG_PATTERNS		"log.patterns"		/**< Spa:String:JSON array of [ {"pattern" : level}, ... ] */
306 
307 /**
308  * \}
309  */
310 
311 #ifdef __cplusplus
312 }  /* extern "C" */
313 #endif
314 #endif /* SPA_LOG_H */
315