65fb7458ae3311a54e7e742dde3cc467195f278e
[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         memset( msg, 0, sizeof( msg ) );                                                                // logging is slow; this ensures 0 term if msg is too large
149         hlen = snprintf( msg, sizeof( msg ), "%ld %d/RMR [%s] ", (long) time( NULL ), log_pid, log_situations[write_level] );
150         if( hlen > sizeof( msg ) - 1024 ) {                                                             // should never happen, but be parinoid
151                 return;
152         }
153         body = msg + hlen;
154
155         va_start( argp, fmt );          // suss out parm past fmt
156
157         vsnprintf( body, sizeof( msg ) - (hlen+2), fmt, argp );                 // add in user message formatting it along the way
158         fprintf( stderr, "%s", msg );                                                                   // we grew from printfs so all existing msg have \n; assume there
159 }
160
161 /*
162         This ensures that the message is written regardless of the current log level
163         setting. This allows for route table collection verbose levels to be taken
164         into consideration separately from the err/warn/debug messages.  Component
165         verbosity would be better and should be implemented.
166 */
167
168 extern void rmr_vlog_force( int write_level, char* fmt, ... ) {
169         va_list argp;           // first arg in our parms which is variable
170         char    msg[4096];
171         int             hlen;
172         char*   body;           // pointer into msg, past header
173
174         if( ! log_initialised ) {
175                 rmr_vlog_init();
176         }
177
178         if( log_vlevel <= 0 ) {                 // can force if off completely to allow for total silience
179                 return;
180         }
181
182         if( write_level > RMR_VL_DEBUG || write_level < 0 ) {
183                 write_level = RMR_VL_DEBUG;
184         }
185
186         hlen = snprintf( msg, sizeof( msg ), "%ld %d/RMR [%s] ", (long) time( NULL ), log_pid, log_situations[write_level] );
187         body = msg + hlen;
188
189         va_start( argp, fmt );          // suss out parm past fmt
190
191         vsnprintf( body, sizeof( msg ) - (hlen+2), fmt, argp );                 // add in user message formatting it along the way
192         fprintf( stderr, "%s", msg );                                                                   // we grew from printfs so all existing msg have \n; assume there
193 }
194
195 // -------------------- public functions that are needed -----------------
196
197 /*
198         Allow user control to logging level control. Accepts a new log level 
199         from the user programme.  Messages which have a write level setting
200         which is >= to the new level will be written.  Setting the new value
201         to RMR_VL_OFF disables all logging (not advised).
202 */
203 extern void rmr_set_vlevel( int new_level ) {
204         if( new_level >= 0 ) {
205                 log_vlevel = new_level;
206         }
207 }
208
209 #endif