11
11
12
12
static int logLevel = AV_LOG_VERBOSE ;
13
13
14
+ static bool logOutputJson = false;
15
+
14
16
const char * getLevelStr (int level ) {
15
17
switch (level ){
16
18
case AV_LOG_PANIC : return "PANIC" ;
@@ -48,7 +50,12 @@ int parseLoglevel(const char* loglevel)
48
50
49
51
pthread_mutex_t logger_locker ;
50
52
51
- char logger_id [256 ] = {0 };
53
+ char context_id [256 ] = {0 };
54
+ char channel_id [256 ] = {0 };
55
+
56
+ static int stringify_message (FILE * out , const char * fmt , va_list args );
57
+
58
+ void logger_unit_test ();
52
59
53
60
void logger2 (const char * category ,const char * subcategory ,int level ,const char * fmt , bool newLine , va_list args )
54
61
{
@@ -66,14 +73,22 @@ void logger2(const char* category,const char* subcategory,int level,const char *
66
73
67
74
FILE * out = stdout ;
68
75
69
- fprintf ( out , "%s.%03lld %s:%s %s |%s| [%p] " ,buf ,( (now % 1000000 )/1000 ),category ,subcategory != NULL ? subcategory : "" , levelStr ,logger_id ,pthread_self ());
70
- if (args != NULL ) {
71
- vfprintf ( out , fmt , args );
76
+ if (logOutputJson ) {
77
+ fprintf ( out , "{\"time\": \"%s.%03ld\", \"channelId\": \"%s\", \"category\": \"%s:%s\", \"logLevel\": \"%s\","
78
+ "\"contextId\": \"%s\", \"pthread\":\"%lx\", \"log\": " ,buf ,( (now % 1000000 )/1000 ), channel_id , category ,
79
+ subcategory != NULL ? subcategory : "" , levelStr ,context_id ,pthread_self ());
80
+ stringify_message (out , fmt , args );
81
+ fprintf ( out , "}\n" );
72
82
} else {
73
- fprintf (out ,"%s" ,fmt );
74
- }
75
- if (newLine ) {
76
- fprintf ( out , "\n" );
83
+ fprintf ( out , "%s.%03ld %s:%s %s |%s| [%s] [%lx] " ,buf ,( (now % 1000000 )/1000 ),category ,subcategory != NULL ? subcategory : "" , levelStr ,context_id ,channel_id ,pthread_self ());
84
+ if (args != NULL ) {
85
+ vfprintf ( out , fmt , args );
86
+ } else {
87
+ fwrite (fmt , strlen (fmt ), 1 , out );
88
+ }
89
+ if (newLine ) {
90
+ fprintf ( out , "\n" );
91
+ }
77
92
}
78
93
fflush (out );
79
94
pthread_mutex_unlock (& logger_locker ); // unlock once you are done
@@ -141,17 +156,149 @@ void log_init(int level)
141
156
142
157
void set_log_level (const char * loglevel ) {
143
158
logLevel = parseLoglevel (loglevel );
144
- if (!* logger_id ) {
145
- json_get_string (GetConfig (),"logger.id" ,"\0" ,logger_id ,sizeof (logger_id ));
159
+ if (!* context_id ) {
160
+ json_get_string (GetConfig (),"logger.contextId" ,"\0" ,context_id ,sizeof (context_id ));
161
+ }
162
+ if (!* channel_id ) {
163
+ json_get_string (GetConfig (),"logger.channelId" ,"\0" ,channel_id ,sizeof (channel_id ));
146
164
}
147
165
}
148
166
149
167
int get_log_level (const char * category )
150
168
{
151
169
return logLevel ;
152
170
}
171
+
172
+ void set_log_output_json (bool val )
173
+ {
174
+ logOutputJson = val ;
175
+ }
176
+
177
+ bool get_log_output_json ()
178
+ {
179
+ return logOutputJson ;
180
+ }
181
+
153
182
void loggerFlush ()
154
183
{
155
184
fflush (stderr );
156
185
fflush (stdout );
157
186
}
187
+
188
+ /*
189
+ json logger utils
190
+ */
191
+
192
+ #define FWRITE_STR (x ) fwrite(x,1,sizeof(x)-1,out)
193
+
194
+ /*
195
+ escapes string according to https://tc39.es/ecma262/multipage/structured-data.html#sec-json.stringify
196
+ */
197
+ const char * hex = "0123456789ABCDEF" ;
198
+ static size_t json_escape (FILE * out , const char * str ) {
199
+ const char * start = str ;
200
+ while (* str ) {
201
+ char chr = * str ;
202
+
203
+ if (chr == '"' || chr == '\\' || chr == '/' ) {
204
+ FWRITE_STR ("\\" );
205
+ putc (chr ,out );
206
+ } else if (chr == '\b' ) {
207
+ FWRITE_STR ("\\b" );
208
+ } else if (chr == '\f' ) {
209
+ FWRITE_STR ("\\f" );
210
+ } else if (chr == '\n' ) {
211
+ FWRITE_STR ("\\n" );
212
+ } else if (chr == '\r' ) {
213
+ FWRITE_STR ("\\r" );
214
+ } else if (chr == '\t' ) {
215
+ FWRITE_STR ("\\t" );
216
+ } else if (!isprint (chr )) {
217
+ FWRITE_STR ("\\u" );
218
+ for (int i = 0 ; i < 4 ; i ++ ) {
219
+ int value = (chr >> 12 ) & 0xf ;
220
+ if (value >= 0 && value <= 15 )
221
+ putc (hex [value ],out );
222
+ chr <<= 4 ;
223
+ }
224
+ } else {
225
+ putc (chr ,out );
226
+ }
227
+ str ++ ;
228
+ }
229
+ return str - start ;
230
+ }
231
+
232
+ typedef struct {
233
+ char * buf ;
234
+ size_t size ;
235
+ FILE * fp ;
236
+ } mem_stream_t ;
237
+
238
+ static mem_stream_t aux_mem_stream = {.buf = NULL ,.size = 0 ,.fp = NULL };
239
+
240
+ static int open_mem_stream (mem_stream_t * stream ) {
241
+ if (!stream -> fp ){
242
+ stream -> fp = open_memstream (& stream -> buf ,& stream -> size );
243
+ }
244
+ if (!stream -> fp ) {
245
+ fprintf (stderr ,"ERROR: could not open_memstream - os error code %d" , errno );
246
+ return -1 ;
247
+ }
248
+ return 0 ;
249
+ }
250
+
251
+ /*
252
+ stringify_message. prints log message as a json stringified string
253
+ NOTE: not thread safe! must be called from within a lock.
254
+ */
255
+ static int stringify_message (FILE * out , const char * fmt , va_list args ) {
256
+ int ret = -1 ;
257
+ FWRITE_STR ("\"" );
258
+ if (args != NULL ) {
259
+ if ((ret = open_mem_stream (& aux_mem_stream ))){
260
+ goto error ;
261
+ }
262
+ if ((ret = fseek (aux_mem_stream .fp , 0 , SEEK_SET ))) {
263
+ fprintf (stderr ,"ERROR: could not fseek %d - os error code %d" , ret , errno );
264
+ goto error ;
265
+ }
266
+ if ( (ret = vfprintf (aux_mem_stream .fp , fmt , args )) > 0 ){
267
+ fflush (aux_mem_stream .fp );
268
+ aux_mem_stream .buf [ret ] = '\0' ;
269
+ ret = json_escape (out , aux_mem_stream .buf );
270
+ } else {
271
+ fprintf (stderr ,"WARNING: vfprintf error %d. os error code %d" , ret , errno );
272
+ }
273
+ } else {
274
+ ret = json_escape (out , fmt );
275
+ }
276
+ error :
277
+ FWRITE_STR ("\"" );
278
+ return ret ;
279
+ }
280
+
281
+ /*
282
+ runtime test
283
+ */
284
+ void logger_unit_test () {
285
+ const char * special = "\a\b\t\n\r\f\"\\" ;
286
+ char table [255 + sizeof (special )- 1 ],
287
+ * walker = table ;
288
+ memcpy (walker ,special ,strlen (special ));
289
+ walker += strlen (special );
290
+ for (int i = 1 ; i < 256 ; i ++ ) {
291
+ * walker ++ = (char )i ;
292
+ }
293
+
294
+ srand ((unsigned ) time (NULL ));
295
+ for (int i = 0 ; i < 1024 ; i ++ ) {
296
+ int a = rand () % sizeof (table ), b = rand () % sizeof (table );
297
+ int temp = table [a ];
298
+ table [a ] = table [b ];
299
+ table [b ] = temp ;
300
+
301
+ LOGGER (CATEGORY_DEFAULT ,AV_LOG_WARNING ,"%s" , table );
302
+ }
303
+ exit (0 );
304
+ }
0 commit comments