Merge "libdlog: separate limiter and dynamic config" into tizen
[platform/core/system/dlog.git] / src / libdlog / log.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-
2  * DLOG
3  * Copyright (c) 2005-2008, The Android Open Source Project
4  * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <pthread.h>
20 #include <stdlib.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23
24 #include <dynamic_config.h>
25 #include <logcommon.h>
26 #include "loglimiter.h"
27 #include "logconfig.h"
28 #include <assert.h>
29 #include <unistd.h>
30
31 #define DEFAULT_CONFIG_LIMITER false
32 #define DEFAULT_CONFIG_PLOG 1
33 #define DEFAULT_CONFIG_DEBUGMODE 0
34 #define DEFAULT_CONFIG_LIMITER_APPLY_TO_ALL_BUFFERS 0
35
36 static int __write_to_log_null(log_id_t, log_priority, const char *, const char *);
37
38 /**
39  * @brief Points to a function which writes a log message
40  * @details The function pointed to depends on the backend used
41  * @param[in] log_id ID of the buffer to log to. Belongs to (LOG_ID_INVALID, LOG_ID_MAX) non-inclusive
42  * @param[in] prio Priority of the message.
43  * @param[in] tag The message tag, identifies the sender.
44  * @param[in] msg The contents of the message.
45  * @return Returns the number of bytes written on success and a negative error value on error.
46  * @see __dlog_init_backend
47  */
48 int (*write_to_log)(log_id_t log_id, log_priority prio, const char *tag, const char *msg) = __write_to_log_null;
49 pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
50 extern void __dlog_init_pipe();
51 extern void __dlog_init_android();
52
53 bool limiter;
54 bool dynamic_config;
55
56 static int plog;
57 static int debugmode;
58 static int fatal_assert;
59 static int limiter_apply_to_all_buffers;
60
61 /**
62  * @brief Null handler
63  * @details Ignores a log
64  * @param[in] log_id ID of the buffer to log to. Belongs to (LOG_ID_INVALID, LOG_ID_MAX) non-inclusive
65  * @param[in] prio Priority of the message.
66  * @param[in] tag The message tag, identifies the sender.
67  * @param[in] msg The contents of the message.
68  * @return DLOG_ERROR_NOT_PERMITTED
69  */
70 static int __write_to_log_null(log_id_t log_id, log_priority prio, const char *tag, const char *msg)// LCOV_EXCL_LINE
71 {
72         return DLOG_ERROR_NOT_PERMITTED; // LCOV_EXCL_LINE
73 }
74
75 static void __configure_limiter(struct log_config *config)
76 {
77         assert(config);
78
79         if (!limiter)
80                 return;
81
82         limiter = __log_limiter_create(config);
83 }
84
85 static int __configure_backend(struct log_config *config)
86 {
87         assert(config);
88
89         const char *const backend = log_config_get(config, "backend");
90         if (!backend)
91                 return 0;
92
93         if (!strcmp(backend, "pipe"))
94                 __dlog_init_pipe();
95         else if (!strcmp(backend, "logger"))
96                 __dlog_init_android();
97         else
98                 return 0;
99
100         return 1;
101 }
102
103 static void __configure_parameters(struct log_config *config)
104 {
105         assert(config);
106
107         plog = log_config_get_int(config, "plog", DEFAULT_CONFIG_PLOG);
108         debugmode = log_config_get_int(config, "debugmode", DEFAULT_CONFIG_DEBUGMODE);
109         fatal_assert = access(DEBUGMODE_FILE, F_OK) != -1;
110         limiter = log_config_get_boolean(config, "limiter", DEFAULT_CONFIG_LIMITER);
111         limiter_apply_to_all_buffers = log_config_get_int(config,
112                                                                         "limiter_apply_to_all_buffers",
113                                                                         DEFAULT_CONFIG_LIMITER_APPLY_TO_ALL_BUFFERS);
114 }
115
116 /**
117  * @brief Configure the library
118  * @details Reads relevant config values
119  */
120 static void __configure(void)
121 {
122         struct log_config config;
123
124         if (log_config_read(&config) < 0)
125                 goto failure;
126
127         dynamic_config = __dynamic_config_create(&config);
128
129         __configure_parameters(&config);
130
131         if (!__configure_backend(&config))
132                 goto failure;
133
134         __configure_limiter(&config);
135
136         log_config_free(&config);
137         return;
138
139 failure:
140         log_config_free(&config); // LCOV_EXCL_LINE
141         return;
142 }
143
144 /**
145  * @brief DLog init
146  * @details Initializes the library
147  */
148 static inline void __dlog_init(void)
149 {
150         static int is_initialized = 0;
151
152         if (is_initialized)
153                 return;
154
155         pthread_mutex_lock(&log_init_lock);
156
157         if (!is_initialized) {
158                 __configure();
159                 is_initialized = 1;
160         }
161
162         pthread_mutex_unlock(&log_init_lock);
163 }
164
165 /**
166  * @brief Fatal assertion
167  * @details Conditionally crash the sucka who sent the log
168  * @param[in] prio Priority of the log
169  */
170 static void __dlog_fatal_assert(int prio)
171 {
172         assert(!fatal_assert || (prio != DLOG_FATAL));
173 }
174
175 /**
176  * @brief Check log validity
177  * @details Checks whether the log is valid and eligible for printing
178  * @param[in] log_id The target buffer ID
179  * @param[in] prio The log's priority
180  * @param[in] tag The log's tag
181  * @return 0 on success, else an error code.
182  * @retval DLOG_ERROR_INVALID_PARAMETER Invalid parameter
183  * @retval DLOG_ERROR_NOT_PERMITTED Not permitted
184  */
185 static int dlog_should_log(log_id_t log_id, int prio, const char *tag)
186 {
187         if (!debugmode && prio <= DLOG_DEBUG)
188                 return DLOG_ERROR_INVALID_PARAMETER;
189
190         if (!tag)
191                 return DLOG_ERROR_INVALID_PARAMETER;
192
193         if (log_id == LOG_ID_INVALID || LOG_ID_MAX <= log_id)
194                 return DLOG_ERROR_INVALID_PARAMETER;
195
196         if (log_id != LOG_ID_APPS && !plog)
197                 return DLOG_ERROR_NOT_PERMITTED;
198
199         if (dynamic_config) {
200                 __dynamic_config_update();
201
202                 // LCOV_EXCL_START : disabled feature (limiter)
203                 if (limiter) {
204                         pthread_mutex_lock(&log_init_lock);
205                         int should_log = __log_limiter_pass_log(tag, prio);
206                         pthread_mutex_unlock(&log_init_lock);
207
208                         if (!should_log) {
209                                 return DLOG_ERROR_NOT_PERMITTED;
210                         } else if (should_log < 0) {
211                                 write_to_log(log_id, prio, tag,
212                                                 "Your log has been blocked due to limit of log lines per minute.");
213                                 return DLOG_ERROR_NOT_PERMITTED;
214                         }
215                 }
216                 // LCOV_EXCL_STOP
217         }
218
219         return DLOG_ERROR_NONE;
220 }
221
222 static int __write_to_log(log_id_t log_id, int prio, const char *tag, const char *fmt, va_list ap, bool check_should_log)
223 {
224         char buf[LOG_MAX_PAYLOAD_SIZE];
225
226         __dlog_init();
227
228         /* if limiter_apply_to_all_buffers config variable is set to 1,
229          * check_should_log value does not matter and the entry is always
230          * tested against all conditions, i.e. limiter rules
231          */
232         int ret = (limiter_apply_to_all_buffers ? true : check_should_log) ? dlog_should_log(log_id, prio, tag) : 0;
233         if (ret < 0)
234                 return ret;
235
236         vsnprintf(buf, sizeof buf, fmt, ap);
237
238         return write_to_log(log_id, prio, tag, buf);
239 }
240
241 /**
242  * @brief Print log
243  * @details Print a log line
244  * @param[in] log_id The target buffer ID
245  * @param[in] prio Priority
246  * @param[in] tag tag
247  * @param[in] fmt Format (same as printf)
248  * @param[in] ap Argument list
249  * @return Bytes written, or negative error
250  */
251 int __dlog_vprint(log_id_t log_id, int prio, const char *tag, const char *fmt, va_list ap)
252 {
253         int ret = __write_to_log(log_id, prio, tag, fmt, ap, true);
254         __dlog_fatal_assert(prio);
255
256         return ret;
257 }
258
259 /**
260  * @brief Print log
261  * @details Print a log line
262  * @param[in] log_id The target buffer ID
263  * @param[in] prio Priority
264  * @param[in] tag tag
265  * @param[in] fmt Format (same as printf)
266  * @return Bytes written, or negative error
267  */
268 int __dlog_print(log_id_t log_id, int prio, const char *tag, const char *fmt, ...)
269 {
270         va_list ap;
271
272         va_start(ap, fmt);
273         int ret = __dlog_vprint(log_id, prio, tag, fmt, ap);
274         va_end(ap);
275
276         return ret;
277 }
278
279 int dlog_vprint(log_priority prio, const char *tag, const char *fmt, va_list ap)
280 {
281         return __write_to_log(LOG_ID_APPS, prio, tag, fmt, ap, false);
282 }
283
284 int dlog_print(log_priority prio, const char *tag, const char *fmt, ...)
285 {
286         va_list ap;
287
288         va_start(ap, fmt);
289         int ret = dlog_vprint(prio, tag, fmt, ap);
290         va_end(ap);
291
292         return ret;
293 }
294
295 /**
296  * @brief Finalize DLog
297  * @details Finalizes and deallocates the library
298  * @notes Assumes it has exclusive thread access,
299  *        i.e. no other library function can run in parallel
300  */
301 void __attribute__((destructor)) __dlog_fini(void)
302 {
303         __log_limiter_destroy();
304         __dynamic_config_destroy();
305 }