Line data Source code
1 : #ifndef HEADER_fd_src_util_log_fd_log_h
2 : #define HEADER_fd_src_util_log_fd_log_h
3 :
4 : /* Note: fd must be booted to use the APIs in this module */
5 :
6 : /* The fd_log conceptually produces up to two log message streams for
7 : an application. One is the ephemeral log message stream (aka
8 : "stderr") and the other is permanent log message stream ("the log
9 : file"). Messages to "stderr" are abbreviated as somebody watching
10 : this stream realtime typically already knows the stream context in
11 : great detail (the host, the user, the application, etc). Messages to
12 : the "log file" are much more detailed and thus suitable long time
13 : archival purposes.
14 :
15 : In producing these streams, writes to the log file are prioritized
16 : over writes to stderr. Further, writes to these streams are done
17 : quasi-atomically at message granularity to reduce the risk that
18 : concurrent log messages from different threads will get mixed
19 : together.
20 :
21 : Default behaviors are:
22 :
23 : - FD_LOG_DEBUG messages are not written to either stream (the
24 : argument list is still processed though so that any side effects of
25 : the argument list are not lost).
26 :
27 : - FD_LOG_INFO messages are written in detailed form to the log file
28 : (if the fd_log log file is setup).
29 :
30 : - FD_LOG_NOTICE is FD_LOG_INFO + messages are written in summary
31 : form to stderr.
32 :
33 : - FD_LOG_WARNING is FD_LOG_NOTICE + the log file and stderr are
34 : flushed to minimize the risk of this message and any preceding not
35 : making it out before thread resumption.
36 :
37 : - FD_LOG_ERR is FD_LOG_WARNING + the program will be exited with
38 : an error code of 1.
39 :
40 : - FD_LOG_CRIT and above are FD_LOG_WARNING + the program will
41 : do a backtrace if possible to the log file and stderr and, after a
42 : brief delay to let any pending fd_log writes complete, aborts the
43 : program (which typically also produces a core dump).
44 :
45 : These log level names mirror the Linux syslog levels.
46 :
47 : Useful concepts / terms:
48 :
49 : - An application is a collection of 1 or more thread groups that have
50 : common log.
51 :
52 : - A thread group is a collection of 1 or more threads. (It typically
53 : is a process but there are unhosted situations when a more
54 : generalized notion of process is required.)
55 :
56 : - The log has a single wall clock for timestamping log messages.
57 :
58 : - Log messages timestamps reflect the time when log message creation
59 : starts.
60 :
61 : - Back-to-back reads of the wallclock by a thread should be
62 : monotonically increasing such that the order in which that thread's
63 : log messages were generated is accurately reflected by the
64 : timestamps.
65 :
66 : - Concurrent reads of the wallclock by different threads should be
67 : reasonably well synchronized such that ordering of events between
68 : communicating threads is accurately reflected by the timestamps.
69 :
70 : - A thread runs on a cpu.
71 :
72 : - A CPU has an architecture (x86 cores, ASIC cores, FPGAs, GPU MPUs,
73 : etc).
74 :
75 : - Multiple CPU architectures might be used by an application.
76 :
77 : - A host is a collection of cpus for which shared memory style
78 : communication primitives are reasonably efficient.
79 :
80 : - CPUs in a host need not share a common memory address space.
81 :
82 : - CPUs in a host need not share a common architecture.
83 :
84 : - Threads in a thread group run on the same host.
85 :
86 : - Threads in a thread group run on the same architecture.
87 :
88 : - Threads in a thread group share a common address space.
89 :
90 : - Threads in a thread group share a common group global variables.
91 :
92 : - A thread group will be part of one application for its lifetime.
93 :
94 : - A thread will be part of only one thread group for its lifetime.
95 :
96 : - A thread will run on only one host for its lifetime.
97 :
98 : - A thread will run on only one architecture for its lifetime.
99 :
100 : - An application thread's thread id is unique over all running
101 : threads in an application.
102 :
103 : - An application thread's thread id reasonably cheaply identifies the
104 : thread group to which the thread belongs.
105 :
106 : - Typically, the set of threads in a thread group will be constant
107 : for the lifetime of the thread group (but this is not strictly
108 : required).
109 :
110 : - Typically, the set of threads groups in an application will be
111 : constant for the lifetime of the application (but this is not
112 : strictly required).
113 :
114 : - Typically, a thread will run on only one CPU for its lifetime
115 : (but this is not strictly required).
116 :
117 : - Typically, a CPU will only be responsible for the execution of at
118 : most one application thread at any given time (but this is not
119 : strictly required).
120 :
121 : The above implies:
122 :
123 : * The synchronization of concurrent clock reads between two
124 : communicating application threads should be tighter than the
125 : latency for these two threads to communicate (e.g. T_send < T_recv
126 : is preserved).
127 :
128 : * The range over which this can be done (i.e. the range of which
129 : the wallclock can be distributed with good synchronization and
130 : reasonably cheaply read) is the range over which application
131 : threads can be distributed.
132 :
133 : * There exist efficient forms of address space translation /
134 : virtualization to facilitate shared memory style communication
135 : between application threads on a host.
136 :
137 : * Communications between threads on different hosts is done via
138 : message passing.
139 :
140 : * Communications between threads on the same host can be done either
141 : by message passing or via shared memory. */
142 :
143 : #include "../env/fd_env.h"
144 : #include "../io/fd_io.h"
145 :
146 : /* FD_LOG_NOTICE(( ... printf style arguments ... )) will send a message
147 : at the NOTICE level to the logger. E.g. for a typical fd_log
148 : configuration:
149 :
150 : FD_LOG_NOTICE(( "%lu is the loneliest number", 1UL ));
151 :
152 : would log something like:
153 :
154 : NOTICE 01-23 04:56:07.890123 45678 f0 0 file.c(901): 1 is the loneliest number
155 :
156 : to the ephemeral log (stderr) and log something like:
157 :
158 : NOTICE 2023-01-23 04:56:07.890123456 GMT-06 45678:45678 user:host:f0 app:thread:0 src/file.c(901)[func]: 1 is the loneliest number
159 :
160 : to the permanent log (log file). Similarly for the other log levels.
161 : Additional logger details are described at the top of this file.
162 :
163 : FD_LOG_NOTICE has a hexdump counterpart that essentially behaves
164 : like:
165 :
166 : void
167 : FD_LOG_HEXDUMP_NOTICE(( char const * tag,
168 : void const * mem,
169 : ulong sz ));
170 :
171 : This logs pretty printed details about memory region to the log
172 : streams at the NOTICE log severity level.
173 :
174 : tag points to a cstr that is intended to be a human-readable /
175 : greppable tag describing the memory region. As such, it is strongly
176 : recommended that tag points to a cstr containing only printable
177 : characters with no internal double quotes (but this is not enforced
178 : currently). There are no length restrictions on the cstr but the
179 : logger under the hood might detectably truncate excessively long tags
180 : (e.g. strlen(tag) >> 32) due to internal implementation limitations.
181 : NULL and/or empty tags ("") are fine and will be detectably logged.
182 :
183 : mem points to the first byte of the memory region to hexdump and sz
184 : is the number of bytes in the region. There are no limits on sz but
185 : the number of bytes logged might be limited due to internal
186 : implementation details (e.g. sz >> 1500 bytes). NULL mem and/or 0 sz
187 : are fine and will be detectably logged.
188 :
189 : The lifetime the cstr and the memory region must be at least from the
190 : call entry to call return.
191 :
192 : E.g. for a typical fd_log configuration:
193 :
194 : FD_LOG_HEXDUMP_WARNING(( "bad_pkt", pkt, pkt_sz ));
195 :
196 : would log something like:
197 :
198 : WARNING 01-23 04:56:07.890123 75779 f0 0 file.c(901): HEXDUMP "bad_pkt" (96 bytes at 0x555555561a4e)
199 : 0000: 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46 0123456789ABCDEF
200 : 0010: 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 GHIJKLMNOPQRSTUV
201 : 0020: 57 58 59 5a 61 62 63 64 65 66 67 68 69 6a 6b 6c WXYZabcdefghijkl
202 : 0030: 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 20 7e mnopqrstuvwxyz ~
203 : 0040: 21 40 23 24 25 5e 26 2a 28 29 5f 2b 60 2d 3d 5b !@#$%^&*()_+`-=[
204 : 0050: 5d 5c 3b 27 2c 2e 2f 7b 7d 7c 3a 22 3c 3e 3f 00 ]\;',./{}|:"<>?.
205 :
206 : to the ephemeral log (stderr) and similarly to the permanent log.
207 :
208 : Similarly for hexdumping to other log levels.
209 :
210 : Note: fd_log_wallclock called outside the arg list to give it a
211 : linguistically strict point when it is called that is before logging
212 : activities commence.
213 :
214 : This family of functions is not async-signal safe. Do not call log functions from
215 : a signal handler, it may deadlock or corrupt the log. If you wish to write
216 : emergency diagnostics, you can call `write(2)` directly to stderr or the log file,
217 : which is safe. */
218 :
219 159315 : #define FD_LOG_DEBUG(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_1( 0, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_0 a ); } while(0)
220 195246 : #define FD_LOG_INFO(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_1( 1, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_0 a ); } while(0)
221 60061828 : #define FD_LOG_NOTICE(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_1( 2, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_0 a ); } while(0)
222 197379 : #define FD_LOG_WARNING(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_1( 3, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_0 a ); } while(0)
223 394350 : #define FD_LOG_ERR(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_2( 4, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_0 a ); } while(0)
224 12 : #define FD_LOG_CRIT(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_2( 5, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_0 a ); } while(0)
225 0 : #define FD_LOG_ALERT(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_2( 6, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_0 a ); } while(0)
226 0 : #define FD_LOG_EMERG(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_2( 7, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_0 a ); } while(0)
227 :
228 6210 : #define FD_LOG_HEXDUMP_DEBUG(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_1( 0, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_hexdump_msg a ); } while(0)
229 204 : #define FD_LOG_HEXDUMP_INFO(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_1( 1, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_hexdump_msg a ); } while(0)
230 165 : #define FD_LOG_HEXDUMP_NOTICE(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_1( 2, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_hexdump_msg a ); } while(0)
231 201 : #define FD_LOG_HEXDUMP_WARNING(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_1( 3, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_hexdump_msg a ); } while(0)
232 0 : #define FD_LOG_HEXDUMP_ERR(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_2( 4, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_hexdump_msg a ); } while(0)
233 : #define FD_LOG_HEXDUMP_CRIT(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_2( 5, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_hexdump_msg a ); } while(0)
234 : #define FD_LOG_HEXDUMP_ALERT(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_2( 6, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_hexdump_msg a ); } while(0)
235 : #define FD_LOG_HEXDUMP_EMERG(a) do { long _fd_log_msg_now = fd_log_wallclock(); fd_log_private_2( 7, _fd_log_msg_now, __FILE__, __LINE__, __func__, fd_log_private_hexdump_msg a ); } while(0)
236 :
237 : /* FD_LOG_STDOUT(()) is used for writing formatted messages to STDOUT, it does not
238 : take a lock and might interleave with other messages to the same pipe. It
239 : should only be used for command output. */
240 0 : #define FD_LOG_STDOUT(a) do { fd_log_private_fprintf_0( STDOUT_FILENO, "%s", fd_log_private_0 a ); } while(0)
241 :
242 : /* FD_CHECK_ERR is a single statement that evaluates c and, if c
243 : evaluates to false, will FD_LOG_ERR (typically exits the thread group
244 : with status 1) with a descriptive error message. It is optimized for
245 : the case where c is non-zero. If c is false, m should evaluate to a
246 : cstr. E.g.:
247 :
248 : FD_CHECK_ERR( func_that_should_return_zero( arg1, arg2 )!=0, "it's bad you know" );
249 :
250 : would typically cause the program to exit with error code 1, logging
251 : something like:
252 :
253 : ERR 01-23 04:56:07.890123 45678 f0 0 foo.c(901): FAIL: func_that_should_return_zero( arg1, arg2 )!=0 (it's bad you know)
254 :
255 : to the ephemeral log (stderr) and something like:
256 :
257 : ERR 2023-01-23 04:56:07.890123456 GMT-06 45678:45678 user:host:f0 app:thread:0 src/foo.c(901)[func]: FAIL: func_that_should_return_zero( arg1, arg2 )!=0 (it's bad you know)
258 :
259 : to the permanent log. Due to linguistic limitations, c cannot
260 : contain things like double quotes, etc. This macro is robust.
261 :
262 : FD_CHECK_CRIT is the same but will FD_LOG_CRIT (typically aborting
263 : the thread group) instead.
264 :
265 : FD_TEST_ERR / FD_TEST_CRIT are the same as FD_CHECK_ERR /
266 : FD_CHECK_CRIT but do not include a user message. These are meant
267 : for use in unit tests. FD_TEST is a short for FD_TEST_ERR. */
268 :
269 175563 : #define FD_CHECK_ERR( c,m) do { if( FD_UNLIKELY( !(c) ) ) FD_LOG_ERR(( "FAIL: %s (%s)", #c, (m) )); } while(0)
270 316092 : #define FD_CHECK_CRIT(c,m) do { if( FD_UNLIKELY( !(c) ) ) FD_LOG_CRIT(( "FAIL: %s (%s)", #c, (m) )); } while(0)
271 :
272 >15251*10^7 : #define FD_TEST_ERR( c) do { if( FD_UNLIKELY( !(c) ) ) FD_LOG_ERR(( "FAIL: %s", #c )); } while(0)
273 3 : #define FD_TEST_CRIT(c) do { if( FD_UNLIKELY( !(c) ) ) FD_LOG_CRIT(( "FAIL: %s", #c )); } while(0)
274 :
275 >15243*10^7 : #define FD_TEST FD_TEST_ERR
276 :
277 : /* FD_DCHECK_STYLE / FD_DCHECK_{CRIT,ALERT}
278 :
279 : The main purpose of these is to document assumptions made by a code
280 : block in a human and machine readable way. These contracts can be
281 : configured to be run-time assertions in a debugging build
282 : (FD_DCHECK_STYLE==1), omitted in production build
283 : (FD_DCHECK_STYLE==0) or treated as compiler assumptions in
284 : experimental builds (FD_DCHECK_STYLE==-1).
285 :
286 : FD_DCHECK_ALERT is meant for code that is expensive to evaluate but
287 : has no side effects. FD_DCHECK_CRIT is meant for all other cases.
288 :
289 : Specifically:
290 :
291 : If FD_DCHECK_STYLE is 1: FD_DCHECK_{CRIT,ALERT} will
292 : FD_LOG_{CRIT,ALERT} if c evaluates to false with a descriptive error
293 : that includes the user message m (m should evaluate to a cstr when c
294 : is false). m will not be evaluated when c is true.
295 :
296 : If FD_DCHECK_STYLE is 0: FD_DCHECK_CRIT will evaluate c, ignore the
297 : result and continue (such that any side effects of c still happen).
298 : FD_DCHECK_ALERT will evaluate neither c nor m.
299 :
300 : If FD_DCHECK_STYLE is -1: FD_DCHECK_{CRIT,ALERT} will evaluate c and
301 : the false code path will be marked as unreachable (such that U.B. is
302 : introduced when c evaluates to false). These will not evalate m.
303 : (For clang, a __builtin_assume implementation of FD_DCHECK_ALERT
304 : might be preferable or maybe style for this.)
305 :
306 : IMPORTANT SAFETY TIP! Don't use FD_DCHECK_STYLE==-1 at build level!
307 : It is meant for use in very limited use within individual translation
308 : units during development. If you don't understand when it makes
309 : sense to use it, to use it, don't! */
310 :
311 : #ifndef FD_DCHECK_STYLE
312 : #define FD_DCHECK_STYLE 0
313 : #endif
314 :
315 : #if FD_DCHECK_STYLE==1
316 : #define FD_DCHECK_CRIT( c,m) do { if( FD_UNLIKELY( !(c) ) ) FD_LOG_CRIT(( "FAIL: %s (%s)", #c, (m) )); } while(0)
317 : #define FD_DCHECK_ALERT(c,m) do { if( FD_UNLIKELY( !(c) ) ) FD_LOG_ALERT(( "FAIL: %s (%s)", #c, (m) )); } while(0)
318 : #elif FD_DCHECK_STYLE==0
319 10744047646 : #define FD_DCHECK_CRIT( c,m) ((void)(c))
320 3482745 : #define FD_DCHECK_ALERT(c,m) ((void)0)
321 : #elif FD_DCHECK_STYLE==-1
322 : #define FD_DCHECK_CRIT( c,m) do { if( FD_UNLIKELY( !(c) ) ) __builtin_unreachable(); } while(0)
323 : #define FD_DCHECK_ALERT(c,m) do { if( FD_UNLIKELY( !(c) ) ) __builtin_unreachable(); } while(0)
324 : #else
325 : #error "unknown FD_DCHECK_STYLE"
326 : #endif
327 :
328 : /* Macros for doing hexedit / tcpdump-like logging of memory regions.
329 : E.g.
330 :
331 : FD_LOG_NOTICE(( "cache line %016lx\n\t"
332 : "%02x: " FD_LOG_HEX16_FMT "\n\t"
333 : "%02x: " FD_LOG_HEX16_FMT "\n\t"
334 : "%02x: " FD_LOG_HEX16_FMT "\n\t"
335 : "%02x: " FD_LOG_HEX16_FMT,
336 : (ulong)mem,
337 : 0U, FD_LOG_HEX16_FMT_ARGS( mem ),
338 : 16U, FD_LOG_HEX16_FMT_ARGS( mem+16 ),
339 : 32U, FD_LOG_HEX16_FMT_ARGS( mem+32 ),
340 : 48U, FD_LOG_HEX16_FMT_ARGS( mem+48 ) ));
341 :
342 : would log something like:
343 :
344 : NOTICE 01-23 04:56:07.890123 45678 f0 0 foo.c(901): cache line 0123456789abcd00
345 : 00: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
346 : 10: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
347 : 20: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
348 : 30: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
349 :
350 : to the ephemeral log typically (and a more detailed message to the
351 : permanent log). And similarly for the other log levels. b should be
352 : safe against multiple evaluation. */
353 :
354 : #define FD_LOG_HEX16_FMT "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"
355 : #define FD_LOG_HEX16_FMT_ARGS(b) \
356 : (uint)(((uchar const *)(b))[ 0]), (uint)(((uchar const *)(b))[ 1]), \
357 : (uint)(((uchar const *)(b))[ 2]), (uint)(((uchar const *)(b))[ 3]), \
358 : (uint)(((uchar const *)(b))[ 4]), (uint)(((uchar const *)(b))[ 5]), \
359 : (uint)(((uchar const *)(b))[ 6]), (uint)(((uchar const *)(b))[ 7]), \
360 : (uint)(((uchar const *)(b))[ 8]), (uint)(((uchar const *)(b))[ 9]), \
361 : (uint)(((uchar const *)(b))[10]), (uint)(((uchar const *)(b))[11]), \
362 : (uint)(((uchar const *)(b))[12]), (uint)(((uchar const *)(b))[13]), \
363 : (uint)(((uchar const *)(b))[14]), (uint)(((uchar const *)(b))[15])
364 :
365 : #define FD_LOG_HEX20_FMT "%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x"
366 : #define FD_LOG_HEX20_FMT_ARGS(b) \
367 : FD_LOG_HEX16_FMT_ARGS(b), \
368 : (uint)(((uchar const *)(b))[16]), (uint)(((uchar const *)(b))[17]), \
369 : (uint)(((uchar const *)(b))[18]), (uint)(((uchar const *)(b))[19])
370 :
371 27820 : #define FD_LOG_NAME_MAX (40UL)
372 :
373 : FD_PROTOTYPES_BEGIN
374 :
375 : /* APPLICATION LOGICAL IDENTIFIERS ************************************/
376 :
377 : /* fd_log_app_id() returns an integer application id of the application
378 : to which the caller belongs. An application id is intended, at a
379 : minimum, to uniquely identify all concurrently running applications
380 : in the enterprise. This is cheap after the first call. */
381 :
382 : FD_FN_PURE ulong fd_log_app_id( void );
383 :
384 : /* fd_log_app() returns a non-NULL pointer to a cstr describing the
385 : application to which the caller belongs. This is typically something
386 : provided to the caller when the caller started. This is cheap after
387 : the first call and the lifetime of the returned string is infinite
388 : from the caller's point of view. strlen(fd_log_app()) is in
389 : [1,FD_LOG_NAME_MAX). */
390 :
391 : FD_FN_CONST char const * fd_log_app( void ); /* Pointer is CONST, cstr pointed at is PURE */
392 :
393 : /* fd_log_thread_id() returns the caller's integer thread id. A thread
394 : id is intended, at a minimum, to be unique over all concurrently
395 : running threads in the application. This is cheap after the first
396 : call. */
397 :
398 : ulong fd_log_thread_id( void );
399 :
400 : /* fd_log_thread() returns a non-NULL pointer to a cstr describing the
401 : caller. This defaults to some target specific default essentially
402 : determined at the caller's startup and can be explicitly set by the
403 : caller. This is cheap after the first call within a thread and the
404 : lifetime of the returned pointer is until the next time the name is
405 : set or the caller terminates. strlen(fd_log_thread()) is in
406 : [1,FD_LOG_NAME_MAX). */
407 :
408 : char const * fd_log_thread( void );
409 :
410 : /* fd_log_thread_set() sets the caller's description to the cstr
411 : pointed to by name. A NULL name and/or an empty name ("") indicate
412 : to reset to the description that would have been assigned if the
413 : caller started at the time this is called. name is not changed by
414 : the function and the fd_log does not retain any interest in name
415 : after return. The actual resulting description will be truncated to
416 : a strlen of FD_LOG_NAME_MAX-1 if name is longer and potentially
417 : sanitized in other ways as necessary for the log. */
418 :
419 : void
420 : fd_log_thread_set( char const * name );
421 :
422 : /* APPLICATION PHYSICAL IDENTIFIERS ***********************************/
423 :
424 : /* fd_log_host_id() returns an integer host id of the host on which the
425 : caller is running. A host id is intended, at a minimum, to uniquely
426 : identify a host enterprise wide. This cheap after the first call. */
427 :
428 : FD_FN_PURE ulong fd_log_host_id( void );
429 :
430 : /* fd_log_host() returns a non-NULL pointer to a cstr describing the
431 : host on which the caller is running. In simple cases, this defaults
432 : to the hostname. In general cases, this is something provided to the
433 : caller at that caller's startup. This is cheap after the first call
434 : and the lifetime of the returned string is infinite from the caller's
435 : point of view. strlen(fd_log_host()) is in [1,FD_LOG_NAME_MAX). */
436 :
437 : FD_FN_CONST char const * fd_log_host( void ); /* ptr is CONST, cstr pointed at is PURE */
438 :
439 : /* fd_log_cpu_id() returns an integer cpu id of one of the cpus on
440 : where the caller was allowed to run when first called by a thread (or
441 : boot if the caller is the one that booted fd). A cpu id is intended
442 : to uniquely identify a cpu on a host (e.g. for a host with
443 : homogeneous x86 cores, idx from /proc/cpuinfo). This is cheap after
444 : the first call. */
445 :
446 : ulong fd_log_cpu_id( void );
447 :
448 : /* fd_log_cpu() returns a non-NULL pointer to a cstr describing the cpu
449 : on which the caller is running. This defaults to some target
450 : specific default determined when first called on a thread (or boot if
451 : the caller is the one that booted fd). This is cheap after the first
452 : call by a thread and the returned string is infinite from the
453 : caller's point of view. strlen(fd_log_cpu()) is in
454 : [1,FD_LOG_NAME_MAX). */
455 :
456 : char const * fd_log_cpu( void );
457 :
458 : /* fd_log_cpu_set() sets the description of the cpu on which the caller
459 : is running on to the cstr pointed to by name. A NULL name and/or an
460 : empty name ("") indicate to reset to the description that would have
461 : been assigned if the caller started at the time this is called. name
462 : is not changed by the function and the fd_log does not retain any
463 : interest in name after return. The actual resulting description will
464 : be truncated to a strlen of FD_LOG_NAME_MAX-1 if name is longer and
465 : potentially sanitized in other ways as necessary for the log. */
466 :
467 : void
468 : fd_log_cpu_set( char const * name );
469 :
470 : /* THREAD GROUP RELATED IDENTIFIERS ***********************************/
471 :
472 : /* fd_log_group_id() returns the thread group id of the thread group to
473 : which the caller belongs. The thread group id is intended, at a
474 : minimum, to be unique over all thread groups on a host. In simple
475 : cases, this is the OS pid of the process to which the caller belongs.
476 : In general cases, this is typically something provided to the caller
477 : when the caller started. This is cheap after the first call.
478 :
479 : For sanity, this should be at least 2 (e.g. in POSIX group_id is
480 : equivalent to pid and pids<=1 are special such that a user is highly
481 : likely to assume group ids <= 1 are special). */
482 :
483 : FD_FN_PURE ulong fd_log_group_id( void );
484 :
485 : /* fd_log_group() returns a non-NULL pointer to a cstr describing the
486 : thread group to which the caller belongs. In simple cases, this
487 : defaults to an abbreviated version of argv[0]. In general cases,
488 : this is typically something provided to the caller when the caller
489 : started. This is cheap after the first call and the lifetime of the
490 : returned string is infinite from the caller's point of view. The
491 : actual pointer and cstr is the same for all threads in the group. */
492 :
493 : FD_FN_CONST char const * fd_log_group( void ); /* ptr is CONST, cstr pointed at is PURE */
494 :
495 : /* fd_log_tid() returns the caller's thread group thread id. A thread
496 : group thread id is intended, at a minimum, to be unique over all
497 : running threads in a thread group. In simple cases, this is the
498 : caller's OS tid. In general cases, this is typically something
499 : provided to the thread when that thread started. This is cheap after
500 : the first call. */
501 :
502 : ulong fd_log_tid( void );
503 :
504 : /* fd_log_user_id() returns the user id of the thread group to which the
505 : caller belongs. The user id is intended, at a minimum, to be unique
506 : over all users on a host. In simple cases, this is the OS uid of the
507 : process to which the caller belongs. In general cases, this is
508 : typically something provided to the caller when the caller started.
509 : This is cheap after the first call. */
510 :
511 : ulong fd_log_user_id( void );
512 :
513 : /* fd_log_user() returns a non-NULL pointer to a cstr describing the
514 : user that created the thread group to which the caller belongs. In
515 : simple cases, this defaults to the LOGNAME / login that started the
516 : process running the caller. In general cases, this is something
517 : provided to the caller at that caller's startup. This is cheap after
518 : the first call and the lifetime of the returned string is infinite
519 : from the caller's point of view. strlen(fd_log_user()) is in
520 : [1,FD_LOG_NAME_MAX). */
521 :
522 : FD_FN_CONST char const * fd_log_user( void ); /* ptr is CONST, cstr pointed at is PURE */
523 :
524 : /* fd_log_group_id_query() returns the status of group_id. Will be a
525 : FD_LOG_GROUP_ID_QUERY_* code. Positive indicates live, zero
526 : indicates dead, negative indicates failure reason. */
527 :
528 3 : #define FD_LOG_GROUP_ID_QUERY_LIVE (1) /* group_id is live */
529 0 : #define FD_LOG_GROUP_ID_QUERY_DEAD (0) /* group_id is not live */
530 6 : #define FD_LOG_GROUP_ID_QUERY_INVAL (-1) /* query failed because invalid group_id (e.g. group_id does to map to a host pid) */
531 0 : #define FD_LOG_GROUP_ID_QUERY_PERM (-2) /* query failed because caller lacks permissions */
532 0 : #define FD_LOG_GROUP_ID_QUERY_FAIL (-3) /* query failed for unknown reason (should not happen) */
533 :
534 : int fd_log_group_id_query( ulong group_id );
535 :
536 : /* FIXME: TID DESC? */
537 :
538 : /* Build info APIs ****************************************************/
539 :
540 : /* fd_log_build_info points in the caller's address space to the first
541 : byte of a memory region of size fd_log_build_info_sz containing a
542 : cstr with information about the environment in which the calling code
543 : was built.
544 :
545 : If build information was not available at compile time, the build
546 : info will be the empty string and size will be one.
547 :
548 : The value in this field is the last time the build info file was
549 : generated (such that, in a development compile-execute-debug
550 : iteration, the build info reflect the build environment since the
551 : last "make clean" or the developer manually deleted the build info).
552 :
553 : Code that is meant to be general purpose should not assume any
554 : particular format, contents, length, etc. The build system,
555 : packaging manager, distribution manager, etc might external impose
556 : additional requirements on this string for application specific code
557 : though. */
558 :
559 : extern char const fd_log_build_info[] __attribute__((aligned(1)));
560 : extern ulong const fd_log_build_info_sz; /* == strlen( fd_log_build_info ) + 1UL */
561 :
562 : /* Logging helper APIs ************************************************/
563 :
564 : /* fd_log_wallclock_host( NULL ) reads the host's wallclock as ns since
565 : the UNIX epoch GMT. On x86, this uses clock_gettime/CLOCK_REALTIME
566 : under the hood and is reasonably cheap (~25-50 ns nowadays). But it
567 : still may involve system calls under the hood and is much slower
568 : than, say, RTSDC. */
569 :
570 : long fd_log_wallclock_host( void const * _ ); /* fd_clock_func_t compat */
571 :
572 : /* fd_log_wallclock reads the log's timesource to get the ns since the
573 : UNIX epoch GMT. By default, this is fd_log_wallclock_host but the
574 : thread group can be configures this to use an alternative time source
575 : if desired. */
576 :
577 : long fd_log_wallclock( void ); /* FIXME: Make fd_clock_func_t compat */
578 :
579 : /* fd_log_wallclock_set configures the log to use "clock( args )" as its
580 : time source. This time source should report ns since the UNIX epoch
581 : GMT. There should be no concurrent users of the log when this is
582 : called. */
583 :
584 : void
585 : fd_log_wallclock_set( fd_clock_func_t clock,
586 : void const * args );
587 :
588 : /* fd_log_wallclock_cstr( t, buf ) pretty prints the wallclock
589 : measurement t as:
590 : "YYYY-MM-DD hh:mm:ss.nnnnnnnnn GMT+TZ".
591 : or in cases where conversion is not locally practical:
592 : " ssssssssss.nnnnnnnnn s UNIX"
593 : buf must be a character buffer of at least
594 : FD_LOG_WALLCLOCK_CSTR_BUF_SZ bytes. Returns buf and buf will be
595 : populated with the desired cstr on return. */
596 :
597 : #define FD_LOG_WALLCLOCK_CSTR_BUF_SZ (37UL)
598 :
599 : char *
600 : fd_log_wallclock_cstr( long t,
601 : char * buf );
602 :
603 : /* fd_log_sleep puts the calling thread to sleep for dt ns. dt<=0 is
604 : assumed to be a sched_yield request. Returns the amount of sleep
605 : remaining if the sleep was interrupted. */
606 :
607 : long
608 : fd_log_sleep( long dt );
609 :
610 : /* fd_log_wait_until waits until fd_log_wallclock() is at least then.
611 : Returns the time on the clock when the wait ended (will be at least
612 : then). This makes a best effort to be a good citizen and sleep /
613 : yield / hyperthreading friendly the caller while also being as
614 : precise on the wait as possible (i.e. limited by the overhead
615 : fd_log_wallclock). That is, as the time remaining to wait decreases,
616 : the wait gets progressively more precise and CPU intensive. If
617 : remaining is the number of ns remaining in the wait, then:
618 :
619 : remaining <~ 1 us: spin
620 : 1 us <~ remaining <~ 100 ms: hyper threading friendly spin
621 : 100 ms <~ remaining <~ 1 s: yielding spin
622 : 1 s <~ remaining : sleep until ~100 ms remaining
623 :
624 : If (as is usually the case) fd_log_sleep precision is much better
625 : than <<~100 ms accurate, FD_YIELD() delays take <<~100ms and
626 : FD_SPIN_PAUSE() << 1 us, the return value will be an accurate read of
627 : the fd_log_wallclock at the time of return and within the overhead of
628 : fd_log_wallclock. */
629 :
630 : long
631 : fd_log_wait_until( long then );
632 :
633 : /* fd_log_flush() manually flushes the log (e.g. log a bunch of low
634 : priority messages and then flush to ensure the bunch gets written out
635 : before proceeding). */
636 :
637 : void
638 : fd_log_flush( void );
639 :
640 : /* These all the logging levels to be configured at runtime. These do
641 : no validation of there inputs so the values may not behave like the
642 : caller things (e.g. stderr<logfile will be treated as
643 : stderr==logfile, flush<stderr will be treated as flush==stderr,
644 : core<4 will be treated as 4). colorize returns the colorization mode
645 : of the ephemeral log. Currently, zero indicates no colorization of
646 : the ephemeral log and non-zero indicates to colorize it. */
647 :
648 : int fd_log_colorize( void );
649 :
650 : /* fd_log_style_{bold,dim,normal} return the terminal escape for the
651 : style when the ephemeral log is colorized and "" otherwise, for
652 : emphasis inside log messages. Convention: bold for identifiers the
653 : operator chose or must recognize, dim for provenance detail, plain
654 : for everything else. The logfile always receives the message with
655 : escapes stripped. */
656 :
657 : char const * fd_log_style_bold ( void );
658 : char const * fd_log_style_dim ( void );
659 : char const * fd_log_style_normal( void );
660 :
661 : int fd_log_level_logfile ( void );
662 : int fd_log_level_stderr ( void );
663 : int fd_log_level_flush ( void );
664 : int fd_log_level_core ( void );
665 :
666 : void fd_log_colorize_set ( int mode );
667 : void fd_log_level_logfile_set( int level );
668 : void fd_log_level_stderr_set ( int level );
669 : void fd_log_level_flush_set ( int level );
670 : void fd_log_level_core_set ( int level );
671 :
672 : void fd_log_enable_signal_handler( void );
673 : void fd_log_enable_unclean_exit( void );
674 :
675 : /* These functions are for fd_log internal use only. */
676 :
677 : void
678 : fd_log_private_fprintf_0( int fd, char const * fmt, ... ) __attribute__((format(printf,2,3))); /* Type check the fmt string at compile time */
679 :
680 : char const *
681 : fd_log_private_0( char const * fmt, ... ) __attribute__((format(printf,1,2))); /* Type check the fmt string at compile time */
682 :
683 : void
684 : fd_log_private_1( int level,
685 : long now,
686 : char const * file,
687 : int line,
688 : char const * func,
689 : char const * msg );
690 :
691 : void
692 : fd_log_private_2( int level,
693 : long now,
694 : char const * file,
695 : int line,
696 : char const * func,
697 : char const * msg ) __attribute__((noreturn)); /* Let compiler know this will not be returning */
698 :
699 : char const *
700 : fd_log_private_hexdump_msg( char const * tag,
701 : void const * mem,
702 : ulong sz );
703 :
704 : void
705 : fd_log_private_boot( int * pargc,
706 : char *** pargv );
707 :
708 : void
709 : fd_log_private_boot_custom( ulong app_id,
710 : char const * app,
711 : ulong thread_id,
712 : char const * thread,
713 : ulong host_id,
714 : char const * host,
715 : ulong cpu_id,
716 : char const * cpu,
717 : ulong group_id,
718 : char const * group,
719 : ulong tid,
720 : ulong user_id,
721 : char const * user,
722 : int dedup,
723 : int colorize,
724 : int level_logfile,
725 : int level_stderr,
726 : int level_flush,
727 : int level_core,
728 : int log_fd,
729 : char const * log_path );
730 :
731 : void
732 : fd_log_private_halt( void );
733 :
734 : ulong fd_log_private_main_stack_sz( void ); /* Returns ulimit -s (if reasonable) on success, 0 on failure (logs details) */
735 :
736 : ulong
737 : fd_log_private_tid_default( void );
738 :
739 : ulong
740 : fd_log_private_cpu_id_default( void );
741 :
742 : void
743 : fd_log_private_stack_discover( ulong stack_sz, /* Size the stack is expected to be */
744 : ulong * _stack0, /* [*_stack0,*_stack1) is the caller's stack region (will have stack_sz */
745 : ulong * _stack1 ); /* bytes) on success. Both set to 0UL on failure (logs details). */
746 :
747 : /* These are exposed to allow the user to override the values set at
748 : boot/halt time. If these are used, they are usually a sign of
749 : working around a higher level architectural or operational issue. */
750 :
751 : void fd_log_private_app_id_set ( ulong app_id );
752 : void fd_log_private_thread_id_set( ulong thread_id );
753 : void fd_log_private_host_id_set ( ulong host_id );
754 : void fd_log_private_cpu_id_set ( ulong cpu_id );
755 : void fd_log_private_group_id_set ( ulong group_id );
756 : void fd_log_private_tid_set ( ulong tid );
757 : void fd_log_private_user_id_set ( ulong user_id );
758 :
759 : void fd_log_private_app_set ( char const * app ); /* Not thread safe */
760 : void fd_log_private_host_set ( char const * host ); /* Not thread safe */
761 : void fd_log_private_group_set( char const * group ); /* Not thread safe */
762 : void fd_log_private_user_set ( char const * user ); /* Not thread safe */
763 :
764 : /* This is exposed to allow the user to know the expected file descriptor
765 : for filtering and security, it should never be used to actually write
766 : logs and that should be done by the functions in fd_log.h */
767 : int fd_log_private_logfile_fd( void );
768 :
769 :
770 : /* fd_log_should_colorize() returns 1 if ANSI color escape sequences
771 : should be emitted for log output, 0 otherwise.
772 :
773 : Check (in order):
774 : - NO_COLOR env var set and non-empty, no
775 : - Output is not a TTY (piped/file), no
776 : - TERM is unset or "dumb", no
777 : - Queries compiled terminfo db for the terminal's max_colors
778 : capability. If it is more than 0, returns yes.
779 :
780 : Does not detect 24-bit/truecolor (terminfo commonly underreports
781 : the number of colors supported). Simple ASCII-based color codes
782 : won't need this. */
783 :
784 : int
785 : fd_log_should_colorize( void );
786 :
787 : FD_PROTOTYPES_END
788 :
789 : #endif /* HEADER_fd_src_util_log_fd_log_h */
|