source: mainline/uspace/srv/logger/logs.c

Last change on this file was a7a16a2f, checked in by Jiri Svoboda <jiri@…>, 5 months ago

Log files should have .txt extension.

  • Property mode set to 100644
File size: 8.4 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
3 * Copyright (c) 2012 Vojtech Horky
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup logger
31 * @{
32 */
33#include <assert.h>
34#include <errno.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <str.h>
38#include "logger.h"
39
40static FIBRIL_MUTEX_INITIALIZE(log_list_guard);
41static LIST_INITIALIZE(log_list);
42
43static logger_log_t *find_log_by_name_and_parent_no_list_lock(const char *name, logger_log_t *parent)
44{
45 list_foreach(log_list, link, logger_log_t, log) {
46 if ((parent == log->parent) && (str_cmp(log->name, name) == 0))
47 return log;
48 }
49
50 return NULL;
51}
52
53static errno_t create_dest(const char *name, logger_dest_t **dest)
54{
55 logger_dest_t *result = malloc(sizeof(logger_dest_t));
56 if (result == NULL)
57 return ENOMEM;
58 if (asprintf(&result->filename, "/log/%s.txt", name) < 0) {
59 free(result);
60 return ENOMEM;
61 }
62 result->logfile = NULL;
63 fibril_mutex_initialize(&result->guard);
64 *dest = result;
65 return EOK;
66}
67
68static logger_log_t *create_log_no_locking(const char *name, logger_log_t *parent)
69{
70 logger_log_t *result = calloc(1, sizeof(logger_log_t));
71 if (result == NULL)
72 return NULL;
73
74 result->name = str_dup(name);
75 if (result->name == NULL)
76 goto error;
77
78 /*
79 * Notice that we create new dest as the last
80 * operation that can fail and thus there is no code
81 * to deallocate dest.
82 */
83 if (parent == NULL) {
84 result->full_name = str_dup(name);
85 if (result->full_name == NULL)
86 goto error;
87 errno_t rc = create_dest(name, &result->dest);
88 if (rc != EOK)
89 goto error;
90 } else {
91 if (asprintf(&result->full_name, "%s/%s",
92 parent->full_name, name) < 0)
93 goto error;
94 result->dest = parent->dest;
95 }
96
97 /* Following initializations cannot fail. */
98 result->logged_level = LOG_LEVEL_USE_DEFAULT;
99 fibril_mutex_initialize(&result->guard);
100 link_initialize(&result->link);
101 result->parent = parent;
102
103 return result;
104
105error:
106 free(result->name);
107 free(result->full_name);
108 free(result);
109 return NULL;
110
111}
112
113logger_log_t *find_or_create_log_and_lock(const char *name, sysarg_t parent_id)
114{
115 logger_log_t *result = NULL;
116 logger_log_t *parent = (logger_log_t *) parent_id;
117
118 fibril_mutex_lock(&log_list_guard);
119
120 result = find_log_by_name_and_parent_no_list_lock(name, parent);
121 if (result == NULL) {
122 result = create_log_no_locking(name, parent);
123 if (result == NULL)
124 goto leave;
125 list_append(&result->link, &log_list);
126 if (result->parent != NULL) {
127 fibril_mutex_lock(&result->parent->guard);
128 result->parent->ref_counter++;
129 fibril_mutex_unlock(&result->parent->guard);
130 }
131 }
132
133 fibril_mutex_lock(&result->guard);
134
135leave:
136 fibril_mutex_unlock(&log_list_guard);
137
138 return result;
139}
140
141logger_log_t *find_log_by_name_and_lock(const char *name)
142{
143 logger_log_t *result = NULL;
144
145 fibril_mutex_lock(&log_list_guard);
146 list_foreach(log_list, link, logger_log_t, log) {
147 if (str_cmp(log->full_name, name) == 0) {
148 fibril_mutex_lock(&log->guard);
149 result = log;
150 break;
151 }
152 }
153 fibril_mutex_unlock(&log_list_guard);
154
155 return result;
156}
157
158logger_log_t *find_log_by_id_and_lock(sysarg_t id)
159{
160 logger_log_t *result = NULL;
161
162 fibril_mutex_lock(&log_list_guard);
163 list_foreach(log_list, link, logger_log_t, log) {
164 if ((sysarg_t) log == id) {
165 fibril_mutex_lock(&log->guard);
166 result = log;
167 break;
168 }
169 }
170 fibril_mutex_unlock(&log_list_guard);
171
172 return result;
173}
174
175static log_level_t get_actual_log_level(logger_log_t *log)
176{
177 /* Find recursively proper log level. */
178 if (log->logged_level == LOG_LEVEL_USE_DEFAULT) {
179 if (log->parent == NULL)
180 return get_default_logging_level();
181 else
182 return get_actual_log_level(log->parent);
183 }
184 return log->logged_level;
185}
186
187bool shall_log_message(logger_log_t *log, log_level_t level)
188{
189 fibril_mutex_lock(&log_list_guard);
190 bool result = level <= get_actual_log_level(log);
191 fibril_mutex_unlock(&log_list_guard);
192 return result;
193}
194
195void log_unlock(logger_log_t *log)
196{
197 assert(fibril_mutex_is_locked(&log->guard));
198 fibril_mutex_unlock(&log->guard);
199}
200
201/** Decreases reference counter on the log and destroy the log if
202 * necessary.
203 *
204 * Precondition: log is locked.
205 *
206 * @param log Log to release from using by the caller.
207 */
208void log_release(logger_log_t *log)
209{
210 assert(fibril_mutex_is_locked(&log->guard));
211 assert(log->ref_counter > 0);
212
213 /* We are definitely not the last ones. */
214 if (log->ref_counter > 1) {
215 log->ref_counter--;
216 fibril_mutex_unlock(&log->guard);
217 return;
218 }
219
220 /*
221 * To prevent deadlock, we need to get the list lock first.
222 * Deadlock scenario:
223 * Us: LOCKED(log), want to LOCK(list)
224 * Someone else calls find_log_by_name_and_lock(log->fullname) ->
225 * LOCKED(list), wants to LOCK(log)
226 */
227 fibril_mutex_unlock(&log->guard);
228
229 /* Ensuring correct locking order. */
230 fibril_mutex_lock(&log_list_guard);
231 /*
232 * The reference must be still valid because we have not decreased
233 * the reference counter.
234 */
235 fibril_mutex_lock(&log->guard);
236 assert(log->ref_counter > 0);
237 log->ref_counter--;
238
239 if (log->ref_counter > 0) {
240 /*
241 * Meanwhile, someone else increased the ref counter.
242 * No big deal, we just return immediatelly.
243 */
244 fibril_mutex_unlock(&log->guard);
245 fibril_mutex_unlock(&log_list_guard);
246 return;
247 }
248
249 /*
250 * Here we are on a destroy path. We need to
251 * - remove ourselves from the list
252 * - decrease reference of the parent (if not top-level log)
253 * - we must do that after we relaase list lock to prevent
254 * deadlock with ourselves
255 * - destroy dest (if top-level log)
256 */
257 assert(log->ref_counter == 0);
258
259 list_remove(&log->link);
260 fibril_mutex_unlock(&log_list_guard);
261 fibril_mutex_unlock(&log->guard);
262
263 if (log->parent == NULL) {
264 /*
265 * Due to lazy file opening in write_to_log(),
266 * it is possible that no file was actually opened.
267 */
268 if (log->dest->logfile != NULL) {
269 fclose(log->dest->logfile);
270 }
271 free(log->dest->filename);
272 free(log->dest);
273 } else {
274 fibril_mutex_lock(&log->parent->guard);
275 log_release(log->parent);
276 }
277
278 logger_log("Destroyed log %s.\n", log->full_name);
279
280 free(log->name);
281 free(log->full_name);
282
283 free(log);
284}
285
286void write_to_log(logger_log_t *log, log_level_t level, const char *message)
287{
288 assert(fibril_mutex_is_locked(&log->guard));
289 assert(log->dest != NULL);
290 fibril_mutex_lock(&log->dest->guard);
291 if (log->dest->logfile == NULL)
292 log->dest->logfile = fopen(log->dest->filename, "a");
293
294 if (log->dest->logfile != NULL) {
295 fprintf(log->dest->logfile, "[%s] %s: %s\n",
296 log->full_name, log_level_str(level),
297 (const char *) message);
298 fflush(log->dest->logfile);
299 }
300
301 fibril_mutex_unlock(&log->dest->guard);
302}
303
304void registered_logs_init(logger_registered_logs_t *logs)
305{
306 logs->logs_count = 0;
307}
308
309bool register_log(logger_registered_logs_t *logs, logger_log_t *new_log)
310{
311 if (logs->logs_count >= MAX_REFERENCED_LOGS_PER_CLIENT) {
312 return false;
313 }
314
315 assert(fibril_mutex_is_locked(&new_log->guard));
316 new_log->ref_counter++;
317
318 logs->logs[logs->logs_count] = new_log;
319 logs->logs_count++;
320
321 return true;
322}
323
324void unregister_logs(logger_registered_logs_t *logs)
325{
326 for (size_t i = 0; i < logs->logs_count; i++) {
327 logger_log_t *log = logs->logs[i];
328 fibril_mutex_lock(&log->guard);
329 log_release(log);
330 }
331}
332
333/**
334 * @}
335 */
Note: See TracBrowser for help on using the repository browser.