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 17 : void dlt_log_set_filename(const char *filename)
56 : {
57 : /* check nullpointer */
58 17 : 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 16 : logging_filename[NAME_MAX] = 0;
65 : }
66 :
67 19 : void dlt_log_set_level(int level)
68 : {
69 19 : 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 : }
75 : else {
76 19 : logging_level = level;
77 : }
78 19 : }
79 :
80 12 : void dlt_log_init(int mode)
81 : {
82 12 : dlt_log_init_multiple_logfiles_support((DltLoggingMode)mode, false, 0, 0);
83 12 : }
84 :
85 :
86 26 : DltReturnValue dlt_log_init_multiple_logfiles_support(const DltLoggingMode mode, const bool enable_multiple_logfiles,
87 : const int logging_file_size, const int logging_files_max_size)
88 : {
89 26 : 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 26 : logging_mode = mode;
95 :
96 26 : 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("dlt logging for limits fails with error code=%d, use logging without limits as fallback\n", result);
106 1 : result = dlt_log_init_single_logfile();
107 : }
108 : } else {
109 3 : dlt_user_printf("configure dlt logging without file limits\n");
110 3 : result = dlt_log_init_single_logfile();
111 : }
112 :
113 : return result;
114 : }
115 :
116 4 : DltReturnValue dlt_log_init_single_logfile()
117 : {
118 : /* internal logging to file */
119 4 : errno = 0;
120 4 : logging_handle = fopen(logging_filename, "a");
121 :
122 4 : if (logging_handle == NULL) {
123 0 : dlt_user_printf("Internal log file %s cannot be opened, error: %s\n", logging_filename, strerror(errno));
124 0 : return DLT_RETURN_ERROR;
125 : }
126 : return DLT_RETURN_OK;
127 : }
128 :
129 4 : DltReturnValue dlt_log_init_multiple_logfiles(const int logging_file_size, const int logging_files_max_size)
130 : {
131 : char path_logging_filename[PATH_MAX + 1];
132 : strncpy(path_logging_filename, logging_filename, PATH_MAX);
133 4 : path_logging_filename[PATH_MAX] = 0;
134 :
135 4 : const char *directory = dirname(path_logging_filename);
136 4 : if (directory[0]) {
137 : char basename_logging_filename[NAME_MAX + 1];
138 : strncpy(basename_logging_filename, logging_filename, NAME_MAX);
139 4 : basename_logging_filename[NAME_MAX] = 0;
140 :
141 4 : const char *file_name = basename(basename_logging_filename);
142 : char filename_base[NAME_MAX];
143 4 : if (!dlt_extract_base_name_without_ext(file_name, filename_base, sizeof(filename_base))) return DLT_RETURN_ERROR;
144 :
145 3 : const char *filename_ext = get_filename_ext(file_name);
146 3 : if (!filename_ext) return DLT_RETURN_ERROR;
147 :
148 3 : DltReturnValue result = multiple_files_buffer_init(
149 : &multiple_files_ring_buffer,
150 : directory,
151 : logging_file_size,
152 : logging_files_max_size,
153 : false,
154 : true,
155 : filename_base,
156 : filename_ext);
157 :
158 3 : return result;
159 : }
160 :
161 : return DLT_RETURN_ERROR;
162 : }
163 :
164 2852 : int dlt_user_printf(const char *format, ...)
165 : {
166 2852 : if (format == NULL) return -1;
167 :
168 : va_list args;
169 2852 : va_start(args, format);
170 :
171 : int ret = 0;
172 :
173 2852 : switch (logging_mode) {
174 8 : case DLT_LOG_TO_CONSOLE:
175 : case DLT_LOG_TO_SYSLOG:
176 : case DLT_LOG_TO_FILE:
177 : case DLT_LOG_DROPPED:
178 : default:
179 8 : ret = vfprintf(stdout, format, args);
180 8 : break;
181 2844 : case DLT_LOG_TO_STDERR:
182 2844 : ret = vfprintf(stderr, format, args);
183 2844 : break;
184 : }
185 :
186 2852 : va_end(args);
187 :
188 2852 : return ret;
189 : }
190 :
191 25156 : DltReturnValue dlt_log(int prio, const char *s)
192 : {
193 : static const char asSeverity[LOG_DEBUG +
194 : 2][11] =
195 : { "EMERGENCY", "ALERT ", "CRITICAL ", "ERROR ", "WARNING ", "NOTICE ", "INFO ", "DEBUG ",
196 : " " };
197 : static const char sFormatString[] = "[%5u.%06u]~DLT~%5d~%s~%s";
198 : struct timespec sTimeSpec;
199 :
200 25156 : if (s == NULL)
201 : return DLT_RETURN_WRONG_PARAMETER;
202 :
203 25155 : if (logging_level < prio)
204 : return DLT_RETURN_OK;
205 :
206 24238 : if ((prio < 0) || (prio > LOG_DEBUG))
207 : prio = LOG_DEBUG + 1;
208 :
209 24238 : clock_gettime(CLOCK_MONOTONIC, &sTimeSpec);
210 :
211 24238 : switch (logging_mode) {
212 527 : case DLT_LOG_TO_CONSOLE:
213 : /* log to stdout */
214 1054 : fprintf(stdout, sFormatString,
215 527 : (unsigned int)sTimeSpec.tv_sec,
216 527 : (unsigned int)(sTimeSpec.tv_nsec / 1000),
217 : getpid(),
218 527 : asSeverity[prio],
219 : s);
220 527 : fflush(stdout);
221 527 : break;
222 23674 : case DLT_LOG_TO_STDERR:
223 : /* log to stderr */
224 47348 : fprintf(stderr, sFormatString,
225 23674 : (unsigned int)sTimeSpec.tv_sec,
226 23674 : (unsigned int)(sTimeSpec.tv_nsec / 1000),
227 : getpid(),
228 23674 : asSeverity[prio],
229 : s);
230 : break;
231 0 : case DLT_LOG_TO_SYSLOG:
232 : /* log to syslog */
233 : #if !defined (__WIN32__) && !defined(_MSC_VER)
234 0 : openlog("DLT", LOG_PID, LOG_DAEMON);
235 0 : syslog(prio,
236 : sFormatString,
237 0 : (unsigned int)sTimeSpec.tv_sec,
238 0 : (unsigned int)(sTimeSpec.tv_nsec / 1000),
239 : getpid(),
240 0 : asSeverity[prio],
241 : s);
242 0 : closelog();
243 : #endif
244 0 : break;
245 32 : case DLT_LOG_TO_FILE:
246 : /* log to file */
247 :
248 32 : if (dlt_is_log_in_multiple_files_active()) {
249 24 : dlt_log_multiple_files_write(sFormatString, (unsigned int)sTimeSpec.tv_sec,
250 12 : (unsigned int)(sTimeSpec.tv_nsec / 1000), getpid(), asSeverity[prio], s);
251 : }
252 20 : else if (logging_handle) {
253 40 : fprintf(logging_handle, sFormatString, (unsigned int)sTimeSpec.tv_sec,
254 20 : (unsigned int)(sTimeSpec.tv_nsec / 1000), getpid(), asSeverity[prio], s);
255 20 : fflush(logging_handle);
256 : }
257 :
258 : break;
259 : case DLT_LOG_DROPPED:
260 : default:
261 : break;
262 : }
263 :
264 : return DLT_RETURN_OK;
265 : }
266 :
267 39050 : DltReturnValue dlt_vlog(int prio, const char *format, ...)
268 : {
269 39050 : char outputString[2048] = { 0 }; /* TODO: what is a reasonable string length here? */
270 :
271 : va_list args;
272 :
273 39050 : if (format == NULL)
274 : return DLT_RETURN_WRONG_PARAMETER;
275 :
276 39050 : if (logging_level < prio)
277 : return DLT_RETURN_OK;
278 :
279 5695 : va_start(args, format);
280 : vsnprintf(outputString, 2047, format, args);
281 5695 : va_end(args);
282 :
283 5695 : dlt_log(prio, outputString);
284 :
285 5695 : return DLT_RETURN_OK;
286 : }
287 :
288 18469 : DltReturnValue dlt_vnlog(int prio, size_t size, const char *format, ...)
289 : {
290 : char *outputString = NULL;
291 :
292 : va_list args;
293 :
294 18469 : if (format == NULL)
295 : return DLT_RETURN_WRONG_PARAMETER;
296 :
297 18469 : if ((logging_level < prio) || (size == 0))
298 : return DLT_RETURN_OK;
299 :
300 18469 : if ((outputString = (char *)calloc(size + 1, sizeof(char))) == NULL)
301 : return DLT_RETURN_ERROR;
302 :
303 18469 : va_start(args, format);
304 : vsnprintf(outputString, size, format, args);
305 18469 : va_end(args);
306 :
307 18469 : dlt_log(prio, outputString);
308 :
309 18469 : free(outputString);
310 : outputString = NULL;
311 :
312 18469 : return DLT_RETURN_OK;
313 : }
314 :
315 12 : void dlt_log_multiple_files_write(const char* format, ...)
316 : {
317 12 : char output_string[2048] = { 0 };
318 : va_list args;
319 12 : va_start (args, format);
320 : vsnprintf(output_string, 2047, format, args);
321 12 : va_end (args);
322 12 : multiple_files_buffer_write(&multiple_files_ring_buffer, (unsigned char*)output_string, strlen(output_string));
323 12 : }
324 :
325 15 : void dlt_log_free(void)
326 : {
327 15 : if (logging_mode == DLT_LOG_TO_FILE) {
328 5 : if (dlt_is_log_in_multiple_files_active()) {
329 3 : dlt_log_free_multiple_logfiles();
330 : } else {
331 2 : dlt_log_free_single_logfile();
332 : }
333 : }
334 15 : }
335 :
336 2 : void dlt_log_free_single_logfile()
337 : {
338 2 : if (logging_handle != NULL) {
339 2 : fclose(logging_handle);
340 : }
341 2 : }
342 :
343 3 : void dlt_log_free_multiple_logfiles()
344 : {
345 3 : if (DLT_RETURN_ERROR == multiple_files_buffer_free(&multiple_files_ring_buffer)) return;
346 :
347 : // reset indicator of multiple files usage
348 3 : multiple_files_ring_buffer.ohandle = -1;
349 : }
350 :
351 37 : bool dlt_is_log_in_multiple_files_active()
352 : {
353 37 : return multiple_files_ring_buffer.ohandle > -1;
354 : }
|