Correct meid table parse bug
[ric-plt/lib/rmr.git] / src / rmr / common / src / logging.c
1 // :vi sw=4 ts=4 noet:
2 /*
3 ==================================================================================
4         Copyright (c) 2019-2020 Nokia
5         Copyright (c) 2018-2020 AT&T Intellectual Property.
6
7    Licensed under the Apache License, Version 2.0 (the "License");
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10
11            http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18 ==================================================================================
19 */
20 /*
21         Mnemonic:       logging.c
22         Abstract:       Implements a common logging interface.
23                 
24                                 Some thoughts and theory....
25                                 Libraries should write human readable messages to standard error
26                                 when errors, warnings, or general information messages are called
27                                 for.  These messages should NEVER be written to standard output,
28                                 and should never be mixed with an application's log messages which
29                                 are assumed to contain potentially sensitive information, such as
30                                 user names, and should not be written to standard error.
31
32                                 For libraries such as RMR, where speed and latency are absolutely
33                                 critical, logging should only be done from non-critical path code,
34                                 or when an error has already compromised the ability to be performant.
35                                 Thus, it is expected that most calls to the functions here are of
36                                 the form:
37                                         if( DEBUG ) rmr_logger( vlevel, fmt, parms);
38
39                                 such that the call to rmr_logger() is removed from the code unless
40                                 compiled with the DEBUG flag set.  Code such as
41                                         if( vlevel >= WARNING ) {
42                                                 rmr_logger( fmt, parms )
43                                         }
44
45                                 is unacceptable because it adds unnecessary checking for the current
46                                 verbosity level setting in line.
47
48         
49                                 With respect to formatting messages written to stderr: they should NOT
50                                 be written with json, or anyother form of mark up.  Idealy, they should
51                                 use a syntax similar to system log:
52                                         <timestamp> <pid> <situation> <message>
53
54                                 where timestamp is UNIX time, pid is the process id, situation is error,
55                                 warning, etc, and message is obviously the text.
56
57                                 Thus, the default messages geneated are "plain."  Because some might feel
58                                 that messages need to be formatted, the code here provides for the gross
59                                 encapsulation of standard error text into json (best wishes for the poor
60                                 sap who is left having to read the output on some dark and stormy night).
61                                 To enable this, the environment veriable RMR_HRLOG=0 must be set to
62                                 turn off human readable logs.
63                                 
64         Author:         E. Scott Daniels
65         Date:           27 January 2020
66 */
67
68 #ifndef _logger_static_c
69 #define _logger_static_c
70
71 #include <unistd.h>
72 #include <stdarg.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <strings.h>
76 #include <errno.h>
77 #include <string.h>
78 #include <stdint.h>
79 #include <time.h>
80
81
82 #include <rmr.h>
83 #include <rmr_agnostic.h>
84 #include <rmr_logging.h>
85
86 static int log_initialised = 0;
87 static int log_vlevel = RMR_VL_ERR;
88 static int log_hrlogging = 1;
89 static int log_pid = 0;
90 static char* log_situations[RMR_VL_DEBUG+1];
91
92 /*
93         Initialise logging. Returns the current log level.
94 */
95 extern int rmr_vlog_init( ) {
96         char*   data;
97
98         if( (data = getenv( ENV_LOG_HR )) != NULL ) {
99                 log_hrlogging = atoi( data );
100         }
101
102         if( (data = getenv( ENV_LOG_VLEVEL )) != NULL ) {
103                 log_vlevel = atoi( data );
104                 if( log_vlevel < 0 ) {
105                         log_vlevel = 0;
106                 } else {
107                         if( log_vlevel > RMR_VL_DEBUG ) {
108                                 log_vlevel = RMR_VL_DEBUG;
109                         }
110                 }
111         }
112
113         log_pid = getpid();
114         log_situations[RMR_VL_DEBUG] = LOG_DEBUG;
115         log_situations[RMR_VL_INFO] = LOG_INFO;
116         log_situations[RMR_VL_WARN] = LOG_WARN;
117         log_situations[RMR_VL_ERR] = LOG_ERROR;
118         log_situations[RMR_VL_CRIT] = LOG_CRIT;
119
120         log_initialised = 1;
121
122         return log_vlevel;
123 }
124
125 /*
126         Write a variable message formatted in the same vein as *printf.
127         We add a header to each log message with time, pid and message
128         situation (error, warning, etc.).
129 */
130 extern void rmr_vlog( int write_level, char* fmt, ... ) {
131         va_list argp;           // first arg in our parms which is variable
132         char    msg[4096];
133         int             hlen;
134         char*   body;           // pointer into msg, past header
135
136         if( write_level > log_vlevel ) {                        // write too big, skip
137                 return;
138         }
139
140         if( ! log_initialised ) {
141                 rmr_vlog_init();
142         }
143
144         if( write_level > RMR_VL_DEBUG || write_level < 0 ) {
145                 write_level = RMR_VL_DEBUG;
146         }
147
148         hlen = snprintf( msg, sizeof( msg ), "%ld %d/RMR [%s] ", (long) time( NULL ), log_pid, log_situations[write_level] );
149         body = msg + hlen;
150
151         va_start( argp, fmt );          // suss out parm past fmt
152
153         vsnprintf( body, sizeof( msg ) - (hlen+2), fmt, argp );                 // add in user message formatting it along the way
154         fprintf( stderr, "%s", msg );                                                                   // we grew from printfs so all existing msg have \n; assume there
155 }
156
157 /*
158         This ensures that the message is written regardless of the current log level
159         setting. This allows for route table collection verbose levels to be taken
160         into consideration separately from the err/warn/debug messages.  Component
161         verbosity would be better and should be implemented.
162 */
163
164 extern void rmr_vlog_force( int write_level, char* fmt, ... ) {
165         va_list argp;           // first arg in our parms which is variable
166         char    msg[4096];
167         int             hlen;
168         char*   body;           // pointer into msg, past header
169
170         if( ! log_initialised ) {
171                 rmr_vlog_init();
172         }
173
174         if( log_vlevel <= 0 ) {                 // cant force if off completely to allow for total silience
175                 return;
176         }
177
178         if( write_level > RMR_VL_DEBUG || write_level < 0 ) {
179                 write_level = RMR_VL_DEBUG;
180         }
181
182         hlen = snprintf( msg, sizeof( msg ), "%ld %d/RMR [%s] ", (long) time( NULL ), log_pid, log_situations[write_level] );
183         body = msg + hlen;
184
185         va_start( argp, fmt );          // suss out parm past fmt
186
187         vsnprintf( body, sizeof( msg ) - (hlen+2), fmt, argp );                 // add in user message formatting it along the way
188         fprintf( stderr, "%s", msg );                                                                   // we grew from printfs so all existing msg have \n; assume there
189 }
190
191 // -------------------- public functions that are needed -----------------
192
193 /*
194         Allow user control to logging level control. Accepts a new log level 
195         from the user programme.  Messages which have a write level setting
196         which is >= to the new level will be written.  Setting the new value
197         to RMR_VL_OFF disables all logging (not advised).
198 */
199 extern void rmr_set_vlevel( int new_level ) {
200         if( new_level >= 0 ) {
201                 log_vlevel = new_level;
202         }
203 }
204
205 #endif