forked from arekinath/pivy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bunyan.c
356 lines (315 loc) · 6.85 KB
/
bunyan.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) 2017, Joyent Inc
* Author: Alex Wilson <[email protected]>
*/
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
#include <limits.h>
#include <fcntl.h>
#include "bunyan.h"
#include "debug.h"
static const char *bunyan_name = NULL;
static char *bunyan_buf = NULL;
static size_t bunyan_buf_sz = 0;
static enum bunyan_log_level bunyan_min_level = INFO;
#if defined(__APPLE__)
typedef unsigned int uint;
#endif
struct bunyan_timers {
struct timer_block *bt_first;
struct timer_block *bt_last;
struct timespec bt_current;
};
#define TBLOCK_N 16
struct timer_block {
struct timespec tb_timers[TBLOCK_N];
const char *tb_names[TBLOCK_N];
size_t tb_pos;
struct timer_block *tb_next;
};
#define NS_PER_S 1000000000ULL
static inline char
nybble_to_hex(uint8_t nybble)
{
if (nybble >= 0xA)
return ('A' + (nybble - 0xA));
else
return ('0' + nybble);
}
char *
buf_to_hex(const uint8_t *buf, size_t len, int spaces)
{
size_t i, j = 0;
char *out = calloc(1, len * 3 + 1);
uint8_t nybble;
for (i = 0; i < len; ++i) {
nybble = (buf[i] & 0xF0) >> 4;
out[j++] = nybble_to_hex(nybble);
nybble = (buf[i] & 0x0F);
out[j++] = nybble_to_hex(nybble);
if (spaces && i + 1 < len)
out[j++] = ' ';
}
out[j] = 0;
return (out);
}
void
tspec_subtract(struct timespec *result, const struct timespec *x,
const struct timespec *y)
{
struct timespec xcarry;
bcopy(x, &xcarry, sizeof (xcarry));
if (xcarry.tv_nsec < y->tv_nsec) {
xcarry.tv_sec -= 1;
xcarry.tv_nsec += NS_PER_S;
}
result->tv_sec = xcarry.tv_sec - y->tv_sec;
result->tv_nsec = xcarry.tv_nsec - y->tv_nsec;
}
void
bunyan_set_level(enum bunyan_log_level level)
{
bunyan_min_level = level;
}
struct bunyan_timers *
bny_timers_new(void)
{
struct bunyan_timers *tms;
tms = calloc(1, sizeof (struct bunyan_timers));
if (tms == NULL)
return (NULL);
tms->bt_first = calloc(1, sizeof (struct timer_block));
if (tms->bt_first == NULL) {
free(tms);
return (NULL);
}
tms->bt_last = tms->bt_first;
return (tms);
}
int
bny_timer_begin(struct bunyan_timers *tms)
{
if (clock_gettime(CLOCK_MONOTONIC, &tms->bt_current))
return (errno);
return (0);
}
int
bny_timer_next(struct bunyan_timers *tms, const char *name)
{
struct timespec now;
struct timer_block *b;
if (clock_gettime(CLOCK_MONOTONIC, &now))
return (errno);
b = tms->bt_last;
b->tb_names[b->tb_pos] = name;
tspec_subtract(&b->tb_timers[b->tb_pos], &now, &tms->bt_current);
if (++b->tb_pos >= TBLOCK_N) {
b = calloc(1, sizeof (struct timer_block));
if (b == NULL) {
tms->bt_last->tb_pos--;
return (ENOMEM);
}
tms->bt_last->tb_next = b;
tms->bt_last = b;
if (clock_gettime(CLOCK_MONOTONIC, &tms->bt_current))
return (errno);
} else {
bcopy(&now, &tms->bt_current, sizeof (struct timespec));
}
return (0);
}
void
bny_timers_free(struct bunyan_timers *tms)
{
struct timer_block *b, *nb;
for (b = tms->bt_first; b != NULL; b = nb) {
nb = b->tb_next;
free(b);
}
free(tms);
}
static void
printf_buf(const char *fmt, ...)
{
size_t wrote, orig, avail;
char *nbuf;
va_list ap;
if (bunyan_buf_sz == 0) {
bunyan_buf_sz = 1024;
bunyan_buf = calloc(bunyan_buf_sz, 1);
VERIFY(bunyan_buf != NULL);
}
va_start(ap, fmt);
orig = strlen(bunyan_buf);
avail = bunyan_buf_sz - orig;
again:
wrote = vsnprintf(bunyan_buf + orig, avail, fmt, ap);
if (wrote >= avail) {
bunyan_buf_sz *= 2;
nbuf = calloc(bunyan_buf_sz, 1);
VERIFY(nbuf != NULL);
bcopy(bunyan_buf, nbuf, orig);
nbuf[orig] = 0;
free(bunyan_buf);
bunyan_buf = nbuf;
avail = bunyan_buf_sz - orig;
goto again;
}
va_end(ap);
}
static void
reset_buf(void)
{
if (bunyan_buf_sz > 0)
bunyan_buf[0] = 0;
}
static int
bny_timer_print(struct bunyan_timers *tms)
{
struct timer_block *b;
size_t idx;
uint64_t usec;
for (b = tms->bt_first; b != NULL; b = b->tb_next) {
for (idx = 0; idx < b->tb_pos; ++idx) {
usec = b->tb_timers[idx].tv_nsec / 1000;
usec += b->tb_timers[idx].tv_sec * 1000000;
printf_buf("[%s: %llu usec]", b->tb_names[idx], usec);
}
}
return (0);
}
void
bunyan_init(void)
{
}
void
bunyan_set_name(const char *name)
{
bunyan_name = name;
}
void
bunyan_timestamp(char *buffer, size_t len)
{
struct timespec ts;
struct tm *info;
VERIFY0(clock_gettime(CLOCK_REALTIME, &ts));
info = gmtime(&ts.tv_sec);
VERIFY(info != NULL);
snprintf(buffer, len, "%04d-%02d-%02dT%02d:%02d:%02d.%03ldZ",
info->tm_year + 1900, info->tm_mon + 1, info->tm_mday,
info->tm_hour, info->tm_min, info->tm_sec, ts.tv_nsec / 1000000);
}
void
bunyan_set(const char *name1, enum bunyan_arg_type typ1, ...)
{
abort();
}
void
bunyan_log(enum bunyan_log_level level, const char *msg, ...)
{
char time[128];
va_list ap;
const char *propname;
enum bunyan_arg_type typ;
int didsep = 0;
reset_buf();
bunyan_timestamp(time, sizeof (time));
printf_buf("[%s] ", time);
switch (level) {
case TRACE:
printf_buf("TRACE: ");
break;
case DEBUG:
printf_buf("DEBUG: ");
break;
case INFO:
printf_buf("INFO: ");
break;
case WARN:
printf_buf("WARN: ");
break;
case ERROR:
printf_buf("ERROR: ");
break;
case FATAL:
printf_buf("FATAL: ");
break;
}
printf_buf("%s", msg);
va_start(ap, msg);
while (1) {
const char *strval;
char *wstrval;
const uint8_t *binval;
int intval;
uint uintval;
uint64_t uint64val;
size_t szval;
struct bunyan_timers *tsval;
propname = va_arg(ap, const char *);
if (propname == NULL)
break;
if (!didsep) {
didsep = 1;
printf_buf(": ");
} else {
printf_buf(", ");
}
typ = va_arg(ap, enum bunyan_arg_type);
switch (typ) {
case BNY_STRING:
strval = va_arg(ap, const char *);
printf_buf("%s = \"%s\"", propname, strval);
break;
case BNY_INT:
intval = va_arg(ap, int);
printf_buf("%s = %d", propname, intval);
break;
case BNY_UINT:
uintval = va_arg(ap, uint);
printf_buf("%s = 0x%x", propname, uintval);
break;
case BNY_UINT64:
uint64val = va_arg(ap, uint64_t);
printf_buf("%s = 0x%llx", propname, uint64val);
break;
case BNY_SIZE_T:
szval = va_arg(ap, size_t);
printf_buf("%s = %llu", propname, szval);
break;
case BNY_NVLIST:
abort();
break;
case BNY_TIMERS:
tsval = va_arg(ap, struct bunyan_timers *);
VERIFY0(bny_timer_print(tsval));
break;
case BNY_BIN_HEX:
binval = va_arg(ap, const uint8_t *);
szval = va_arg(ap, size_t);
wstrval = buf_to_hex(binval, szval, 1);
printf_buf("%s = << %s >>", propname, wstrval);
free(wstrval);
break;
default:
abort();
}
}
va_end(ap);
printf_buf("\n");
if (level < bunyan_min_level) {
return;
}
fprintf(stderr, bunyan_buf);
}