/src/open62541_15/plugins/ua_log_stdout.c
Line | Count | Source |
1 | | /* This work is licensed under a Creative Commons CCZero 1.0 Universal License. |
2 | | * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. |
3 | | * |
4 | | * Copyright 2016-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
5 | | * Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA |
6 | | */ |
7 | | |
8 | | #include <open62541/plugin/log_stdout.h> |
9 | | #include <open62541/types.h> |
10 | | |
11 | | #include <stdio.h> |
12 | | |
13 | | #include "mp_printf.h" |
14 | | |
15 | | /* ANSI escape sequences for color output taken from here: |
16 | | * https://stackoverflow.com/questions/3219393/stdlib-and-colored-output-in-c*/ |
17 | | |
18 | | #ifdef UA_ARCHITECTURE_POSIX |
19 | | # define ANSI_COLOR_RED "\x1b[31m" |
20 | | # define ANSI_COLOR_GREEN "\x1b[32m" |
21 | | # define ANSI_COLOR_YELLOW "\x1b[33m" |
22 | | /* # define ANSI_COLOR_BLUE "\x1b[34m" */ |
23 | | # define ANSI_COLOR_MAGENTA "\x1b[35m" |
24 | | /* # define ANSI_COLOR_CYAN "\x1b[36m" */ |
25 | | # define ANSI_COLOR_RESET "\x1b[0m" |
26 | | #else |
27 | | # define ANSI_COLOR_RED "" |
28 | | # define ANSI_COLOR_GREEN "" |
29 | | # define ANSI_COLOR_YELLOW "" |
30 | | /* # define ANSI_COLOR_BLUE "" */ |
31 | | # define ANSI_COLOR_MAGENTA "" |
32 | | /* # define ANSI_COLOR_CYAN "" */ |
33 | | # define ANSI_COLOR_RESET "" |
34 | | #endif |
35 | | |
36 | | static |
37 | | const char *logLevelNames[6] = {"trace", "debug", |
38 | | ANSI_COLOR_GREEN "info", |
39 | | ANSI_COLOR_YELLOW "warn", |
40 | | ANSI_COLOR_RED "error", |
41 | | ANSI_COLOR_MAGENTA "fatal"}; |
42 | | static const char * |
43 | | logCategoryNames[UA_LOGCATEGORIES] = |
44 | | {"network", "channel", "session", "server", "client", |
45 | | "application", "security", "eventloop", "pubsub", "discovery"}; |
46 | | |
47 | | static const char * |
48 | 272 | logCategoryName(UA_LogCategory category) { |
49 | 272 | if((UA_UInt32)category >= UA_LOGCATEGORIES) |
50 | 0 | return "unknown"; |
51 | 272 | return logCategoryNames[category]; |
52 | 272 | } |
53 | | |
54 | | /* Protect crosstalk during logging via global lock. Use a spinlock as we cannot |
55 | | * statically initialize a global lock across all platforms. */ |
56 | | #if UA_MULTITHREADING >= 100 |
57 | | static void *logSpinLock = NULL; |
58 | 272 | static UA_INLINE void spinLock(void) { |
59 | 272 | while(UA_atomic_cmpxchg(&logSpinLock, NULL, (void*)0x1) != NULL) {} |
60 | 272 | } |
61 | 536 | static UA_INLINE void spinUnLock(void) { |
62 | 536 | UA_atomic_xchg(&logSpinLock, NULL); |
63 | 536 | } |
64 | | #endif |
65 | | |
66 | | #ifdef __clang__ |
67 | | __attribute__((__format__(__printf__, 4 , 0))) |
68 | | #endif |
69 | | static void |
70 | | UA_Log_Stdout_log(void *context, UA_LogLevel level, UA_LogCategory category, |
71 | 536 | const char *msg, va_list args) { |
72 | | /* MinLevel encoded in the context pointer */ |
73 | 536 | UA_LogLevel minLevel = (UA_LogLevel)(uintptr_t)context; |
74 | 536 | if(minLevel > level) |
75 | 0 | return; |
76 | | |
77 | 536 | UA_Int64 tOffset = UA_DateTime_localTimeUtcOffset(); |
78 | 536 | UA_DateTimeStruct dts = UA_DateTime_toStruct(UA_DateTime_now() + tOffset); |
79 | | |
80 | 536 | int logLevelSlot = ((int)level / 100) - 1; |
81 | 536 | if(logLevelSlot < 0 || logLevelSlot > 5) |
82 | 0 | logLevelSlot = 5; /* Set to fatal if the level is outside the range */ |
83 | | |
84 | | /* Lock */ |
85 | 536 | #if UA_MULTITHREADING >= 100 |
86 | 536 | spinLock(); |
87 | 536 | #endif |
88 | | |
89 | 536 | #define STDOUT_LOGBUFSIZE 512 |
90 | 536 | char logbuf[STDOUT_LOGBUFSIZE]; |
91 | | |
92 | | /* Log */ |
93 | 536 | printf("[%04u-%02u-%02u %02u:%02u:%02u.%03u (UTC%+05d)] %s/%s" ANSI_COLOR_RESET "\t", |
94 | 536 | dts.year, dts.month, dts.day, dts.hour, dts.min, dts.sec, dts.milliSec, |
95 | 536 | (int)(tOffset / UA_DATETIME_SEC / 36), logLevelNames[logLevelSlot], |
96 | 536 | logCategoryName(category)); |
97 | 536 | mp_vsnprintf(logbuf, STDOUT_LOGBUFSIZE, msg, args); |
98 | 536 | printf("%s\n", logbuf); |
99 | 536 | fflush(stdout); |
100 | | |
101 | | /* Unlock */ |
102 | 536 | #if UA_MULTITHREADING >= 100 |
103 | 536 | spinUnLock(); |
104 | 536 | #endif |
105 | 536 | } |
106 | | |
107 | | static void |
108 | 20.6k | UA_Log_Stdout_clear(UA_Logger *logger) { |
109 | 20.6k | UA_free(logger); |
110 | 20.6k | } |
111 | | |
112 | | const UA_Logger UA_Log_Stdout_ = {UA_Log_Stdout_log, NULL, NULL}; |
113 | | const UA_Logger *UA_Log_Stdout = &UA_Log_Stdout_; |
114 | | |
115 | | UA_Logger |
116 | 20.6k | UA_Log_Stdout_withLevel(UA_LogLevel minlevel) { |
117 | 20.6k | UA_Logger logger = |
118 | 20.6k | {UA_Log_Stdout_log, (void*)(uintptr_t)minlevel, NULL}; |
119 | 20.6k | return logger; |
120 | 20.6k | } |
121 | | |
122 | | UA_Logger * |
123 | 20.6k | UA_Log_Stdout_new(UA_LogLevel minlevel) { |
124 | 20.6k | UA_Logger *logger = (UA_Logger*)UA_malloc(sizeof(UA_Logger)); |
125 | 20.6k | if(!logger) |
126 | 0 | return NULL; |
127 | 20.6k | *logger = UA_Log_Stdout_withLevel(minlevel); |
128 | 20.6k | logger->clear = UA_Log_Stdout_clear; |
129 | 20.6k | return logger; |
130 | 20.6k | } |