Line data Source code
1 : /*
2 : * SPDX license identifier: MPL-2.0
3 : *
4 : * Copyright (C) 2024, Mercedes Benz Tech Innovation GmbH
5 : *
6 : * This file is part of GENIVI Project DLT - Diagnostic Log and Trace.
7 : *
8 : * This Source Code Form is subject to the terms of the
9 : * Mozilla Public License (MPL), v. 2.0.
10 : * If a copy of the MPL was not distributed with this file,
11 : * You can obtain one at http://mozilla.org/MPL/2.0/.
12 : *
13 : * For further information see https://www.covesa.global/.
14 : */
15 :
16 : /*!
17 : * \author
18 : * Daniel Weber <daniel.w.weber@mercedes-benz.com>
19 : *
20 : * \copyright Copyright © 2024 Mercedes Benz Tech Innovation GmbH. \n
21 : * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/.
22 : *
23 : * \file dlt_log.c
24 : */
25 :
26 : #include "dlt_log.h"
27 : #include "dlt_common.h"
28 : #include "dlt_multiple_files.h"
29 : #include <syslog.h>
30 : #include <errno.h>
31 : #include <libgen.h> /* dirname */
32 : #include <limits.h> /* for NAME_MAX */
33 : #include <string.h> /* for strlen() */
34 : #include <stdlib.h> /* for calloc(), free() */
35 : #include <stdarg.h> /* va_list, va_start */
36 :
37 : /* internal logging parameters */
38 : static int logging_level = LOG_INFO;
39 : static char logging_filename[NAME_MAX + 1] = "";
40 : DltLoggingMode logging_mode = DLT_LOG_TO_STDERR;
41 : FILE* logging_handle = NULL;
42 :
43 : // use ohandle as an indicator that multiple files logging is active
44 : MultipleFilesRingBuffer multiple_files_ring_buffer = {
45 : .directory = {0},
46 : .filename = {0},
47 : .fileSize = 0,
48 : .maxSize = 0,
49 : .filenameTimestampBased = false,
50 : .filenameBase = {0},
51 : .filenameExt = {0},
52 : .ohandle = -1};
53 :
54 :
55 9 : void dlt_log_set_filename(const char* filename)
56 : {
57 : /* check nullpointer */
58 9 : if (filename == NULL) {
59 1 : dlt_log(LOG_WARNING, "Wrong parameter: filename is NULL\n");
60 1 : return;
61 : }
62 :
63 : strncpy(logging_filename, filename, NAME_MAX);
64 8 : logging_filename[NAME_MAX] = 0;
65 : }
66 :
67 11 : void dlt_log_set_level(int level)
68 : {
69 11 : if ((level < 0) || (level > LOG_DEBUG)) {
70 0 : if (logging_level < LOG_WARNING)
71 0 : logging_level = LOG_WARNING;
72 :
73 0 : dlt_vlog(LOG_WARNING, "Wrong parameter for level: %d\n", level);
74 : } else {
75 11 : logging_level = level;
76 : }
77 11 : }
78 :
79 12 : DltReturnValue dlt_log_init(int mode)
80 : {
81 12 : return dlt_log_init_multiple_logfiles_support((DltLoggingMode)mode, false, 0, 0);
82 : }
83 :
84 :
85 18 : DltReturnValue dlt_log_init_multiple_logfiles_support(
86 : const DltLoggingMode mode, const bool enable_multiple_logfiles, const int logging_file_size,
87 : const int logging_files_max_size)
88 : {
89 18 : if ((mode < DLT_LOG_TO_CONSOLE) || (mode > DLT_LOG_DROPPED)) {
90 0 : dlt_vlog(LOG_WARNING, "Wrong parameter for mode: %d\n", mode);
91 0 : return DLT_RETURN_WRONG_PARAMETER;
92 : }
93 :
94 18 : logging_mode = mode;
95 :
96 18 : if (logging_mode != DLT_LOG_TO_FILE) {
97 : return DLT_RETURN_OK;
98 : }
99 :
100 : DltReturnValue result;
101 7 : if (enable_multiple_logfiles) {
102 4 : dlt_user_printf("configure dlt logging using file limits\n");
103 4 : result = dlt_log_init_multiple_logfiles(logging_file_size, logging_files_max_size);
104 4 : if (result != DLT_RETURN_OK) {
105 1 : dlt_user_printf(
106 : "dlt logging for limits fails with error code=%d, use logging without limits as fallback\n", result);
107 1 : result = dlt_log_init_single_logfile();
108 : }
109 : } else {
110 3 : dlt_user_printf("configure dlt logging without file limits\n");
111 3 : result = dlt_log_init_single_logfile();
112 : }
113 :
114 : return result;
115 : }
116 :
117 4 : DltReturnValue dlt_log_init_single_logfile()
118 : {
119 : /* internal logging to file */
120 4 : errno = 0;
121 4 : logging_handle = fopen(logging_filename, "a");
122 :
123 4 : if (logging_handle == NULL) {
124 0 : dlt_user_printf("Internal log file %s cannot be opened, error: %s\n", logging_filename, strerror(errno));
125 0 : return DLT_RETURN_ERROR;
126 : }
127 : return DLT_RETURN_OK;
128 : }
129 :
130 4 : DltReturnValue dlt_log_init_multiple_logfiles(const int logging_file_size, const int logging_files_max_size)
131 : {
132 : char path_logging_filename[PATH_MAX + 1];
133 : strncpy(path_logging_filename, logging_filename, PATH_MAX);
134 4 : path_logging_filename[PATH_MAX] = 0;
135 :
136 4 : const char* directory = dirname(path_logging_filename);
137 4 : if (directory[0]) {
138 : char basename_logging_filename[NAME_MAX + 1];
139 : strncpy(basename_logging_filename, logging_filename, NAME_MAX);
140 4 : basename_logging_filename[NAME_MAX] = 0;
141 :
142 4 : const char* file_name = basename(basename_logging_filename);
143 : char filename_base[NAME_MAX];
144 4 : if (!dlt_extract_base_name_without_ext(file_name, filename_base, sizeof(filename_base)))
145 : return DLT_RETURN_ERROR;
146 :
147 3 : const char* filename_ext = get_filename_ext(file_name);
148 3 : if (!filename_ext)
149 : return DLT_RETURN_ERROR;
150 :
151 3 : DltReturnValue result = multiple_files_buffer_init(
152 : &multiple_files_ring_buffer, directory, logging_file_size, logging_files_max_size, false, true,
153 : filename_base, filename_ext);
154 :
155 3 : return result;
156 : }
157 :
158 : return DLT_RETURN_ERROR;
159 : }
160 :
161 2852 : int dlt_user_printf(const char* format, ...)
162 : {
163 2852 : if (format == NULL)
164 : return -1;
165 :
166 : va_list args;
167 2852 : va_start(args, format);
168 :
169 : int ret = 0;
170 :
171 2852 : switch (logging_mode) {
172 8 : case DLT_LOG_TO_CONSOLE:
173 : case DLT_LOG_TO_SYSLOG:
174 : case DLT_LOG_TO_FILE:
175 : case DLT_LOG_DROPPED:
176 : default:
177 8 : ret = vfprintf(stdout, format, args);
178 8 : break;
179 2844 : case DLT_LOG_TO_STDERR:
180 2844 : ret = vfprintf(stderr, format, args);
181 2844 : break;
182 : }
183 :
184 2852 : va_end(args);
185 :
186 2852 : return ret;
187 : }
188 :
189 5619 : DltReturnValue dlt_log(int prio, const char* s)
190 : {
191 : static const char asSeverity[LOG_DEBUG + 2][11] = {"EMERGENCY", "ALERT ", "CRITICAL ", "ERROR ", "WARNING ",
192 : "NOTICE ", "INFO ", "DEBUG ", " "};
193 : static const char sFormatString[] = "[%5u.%06u]~DLT~%5d~%s~%s";
194 : struct timespec sTimeSpec;
195 :
196 5619 : if (s == NULL)
197 : return DLT_RETURN_WRONG_PARAMETER;
198 :
199 5618 : if (logging_level < prio)
200 : return DLT_RETURN_OK;
201 :
202 5535 : if ((prio < 0) || (prio > LOG_DEBUG))
203 : prio = LOG_DEBUG + 1;
204 :
205 5535 : clock_gettime(CLOCK_MONOTONIC, &sTimeSpec);
206 :
207 5535 : switch (logging_mode) {
208 118 : case DLT_LOG_TO_CONSOLE:
209 : /* log to stdout */
210 236 : fprintf(
211 118 : stdout, sFormatString, (unsigned int)sTimeSpec.tv_sec, (unsigned int)(sTimeSpec.tv_nsec / 1000), getpid(),
212 118 : asSeverity[prio], s);
213 118 : fflush(stdout);
214 118 : break;
215 5380 : case DLT_LOG_TO_STDERR:
216 : /* log to stderr */
217 10760 : fprintf(
218 5380 : stderr, sFormatString, (unsigned int)sTimeSpec.tv_sec, (unsigned int)(sTimeSpec.tv_nsec / 1000), getpid(),
219 5380 : asSeverity[prio], s);
220 : break;
221 0 : case DLT_LOG_TO_SYSLOG:
222 : /* log to syslog */
223 : #if !defined(__WIN32__) && !defined(_MSC_VER)
224 0 : openlog("DLT", LOG_PID, LOG_DAEMON);
225 0 : syslog(
226 0 : prio, sFormatString, (unsigned int)sTimeSpec.tv_sec, (unsigned int)(sTimeSpec.tv_nsec / 1000), getpid(),
227 0 : asSeverity[prio], s);
228 0 : closelog();
229 : #endif
230 0 : break;
231 32 : case DLT_LOG_TO_FILE:
232 : /* log to file */
233 :
234 32 : if (dlt_is_log_in_multiple_files_active()) {
235 24 : dlt_log_multiple_files_write(
236 12 : sFormatString, (unsigned int)sTimeSpec.tv_sec, (unsigned int)(sTimeSpec.tv_nsec / 1000), getpid(),
237 12 : asSeverity[prio], s);
238 20 : } else if (logging_handle) {
239 40 : fprintf(
240 20 : logging_handle, sFormatString, (unsigned int)sTimeSpec.tv_sec, (unsigned int)(sTimeSpec.tv_nsec / 1000),
241 20 : getpid(), asSeverity[prio], s);
242 20 : fflush(logging_handle);
243 : }
244 :
245 : break;
246 : case DLT_LOG_DROPPED:
247 : default:
248 : break;
249 : }
250 :
251 : return DLT_RETURN_OK;
252 : }
253 :
254 29140 : DltReturnValue dlt_vlog(int prio, const char* format, ...)
255 : {
256 29140 : char outputString[2048] = {0}; /* TODO: what is a reasonable string length here? */
257 :
258 : va_list args;
259 :
260 29140 : if (format == NULL)
261 : return DLT_RETURN_WRONG_PARAMETER;
262 :
263 29140 : if (logging_level < prio)
264 : return DLT_RETURN_OK;
265 :
266 5482 : va_start(args, format);
267 : vsnprintf(outputString, 2047, format, args);
268 5482 : va_end(args);
269 :
270 5482 : dlt_log(prio, outputString);
271 :
272 5482 : return DLT_RETURN_OK;
273 : }
274 :
275 3 : DltReturnValue dlt_vnlog(int prio, size_t size, const char* format, ...)
276 : {
277 : char* outputString = NULL;
278 :
279 : va_list args;
280 :
281 3 : if (format == NULL)
282 : return DLT_RETURN_WRONG_PARAMETER;
283 :
284 3 : if ((logging_level < prio) || (size == 0))
285 : return DLT_RETURN_OK;
286 :
287 3 : if ((outputString = (char*)calloc(size + 1, sizeof(char))) == NULL)
288 : return DLT_RETURN_ERROR;
289 :
290 3 : va_start(args, format);
291 : vsnprintf(outputString, size, format, args);
292 3 : va_end(args);
293 :
294 3 : dlt_log(prio, outputString);
295 :
296 3 : free(outputString);
297 : outputString = NULL;
298 :
299 3 : return DLT_RETURN_OK;
300 : }
301 :
302 12 : void dlt_log_multiple_files_write(const char* format, ...)
303 : {
304 12 : char output_string[2048] = {0};
305 : va_list args;
306 12 : va_start(args, format);
307 : vsnprintf(output_string, 2047, format, args);
308 12 : va_end(args);
309 12 : multiple_files_buffer_write(&multiple_files_ring_buffer, (unsigned char*)output_string, (int)strlen(output_string));
310 12 : }
311 :
312 7 : void dlt_log_free(void)
313 : {
314 7 : if (logging_mode == DLT_LOG_TO_FILE) {
315 5 : if (dlt_is_log_in_multiple_files_active()) {
316 3 : dlt_log_free_multiple_logfiles();
317 : } else {
318 2 : dlt_log_free_single_logfile();
319 : }
320 : }
321 7 : }
322 :
323 2 : void dlt_log_free_single_logfile()
324 : {
325 2 : if (logging_handle != NULL) {
326 2 : fclose(logging_handle);
327 : }
328 2 : }
329 :
330 3 : void dlt_log_free_multiple_logfiles()
331 : {
332 3 : if (DLT_RETURN_ERROR == multiple_files_buffer_free(&multiple_files_ring_buffer))
333 : return;
334 :
335 : // reset indicator of multiple files usage
336 3 : multiple_files_ring_buffer.ohandle = -1;
337 : }
338 :
339 37 : bool dlt_is_log_in_multiple_files_active()
340 : {
341 37 : return multiple_files_ring_buffer.ohandle > -1;
342 : }
|