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 src/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 src/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 138990 : #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 5 : #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 48298 : #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 1092621 : #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 3146859 : #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 0 : #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 177 : #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 3 : #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 141321 : #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 0 : #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_nolock_0( STDOUT_FILENO, "%s", fd_log_private_0 a ); } while(0)
241 :
242 : /* FD_TEST is a single statement that evaluates condition c and, if c
243 : evaluates to false, will FD_LOG_ERR that the condition failed. It is
244 : optimized for the case where c will is non-zero. This is mostly
245 : meant for use in things like unit tests. Due to linguistic
246 : limitations, c cannot contain things like double quotes, etc. E.g.
247 :
248 : FD_TEST( broken_func_that_should_return_zero( arg1, arg2 )!=0 );
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 src/foo.c(901): FAIL: broken_func_that_should_return_zero( arg1, arg2 )!=0
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: broken_func_that_should_return_zero( arg1, arg2 )!=0
258 :
259 : to the permanent log. And similarly for other log levels.
260 :
261 : This macro is robust. */
262 :
263 >35820*10^7 : #define FD_TEST(c) do { if( FD_UNLIKELY( !(c) ) ) FD_LOG_ERR(( "FAIL: %s", #c )); } while(0)
264 :
265 : /* FD_TEST_CUSTOM is like FD_TEST but with a custom error msg err. */
266 :
267 6915 : #define FD_TEST_CUSTOM(c,err) do { if( FD_UNLIKELY( !(c) ) ) FD_LOG_ERR(( "FAIL: %s", (err) )); } while(0)
268 :
269 : /* FD_PARANOID / FD_CRIT / FD_ALERT:
270 :
271 : FD_PARANOID configures the FD_CRIT / FD_ALERT runtime checks.
272 :
273 : If FD_PARANOID is set: FD_CRIT / FD_ALERT will FD_LOG_CRIT /
274 : FD_LOG_ALERT the application if c evaluates to false with a
275 : descriptive error that includes the user message m (m should evaluate
276 : to a cstr when c is false).
277 :
278 : If not set: FD_CRIT will evaluate c (such that any side effects of c
279 : will still happen), the false code path will be marked as unreachable
280 : (such that the optimizer will treat the code following the FD_CRIT
281 : the same as when paranoid was set) and m will not be evaluated.
282 : FD_ALERT will not evaluate c or m.
283 :
284 : In short, use FD_ALERT when c is expensive to evalute but has no side
285 : effects. Use FD_CRIT for all other cases.
286 :
287 : FIXME: probably should rename FD_TEST_CUSTOM to FD_ERR. */
288 :
289 : #ifndef FD_PARANOID
290 : #define FD_PARANOID 0
291 : #endif
292 :
293 : #if FD_PARANOID
294 : #define FD_CRIT( c,m) do { if( FD_UNLIKELY( !(c) ) ) FD_LOG_CRIT (( "FAIL: %s (%s)", #c, (m) )); } while(0)
295 : #define FD_ALERT(c,m) do { if( FD_UNLIKELY( !(c) ) ) FD_LOG_ALERT(( "FAIL: %s (%s)", #c, (m) )); } while(0)
296 : #else
297 204143217 : #define FD_CRIT( c,m) do { (void)(c); } while(0)
298 12889212 : #define FD_ALERT(c,m) do { } while(0)
299 : #endif
300 :
301 : /* Macros for doing hexedit / tcpdump-like logging of memory regions.
302 : E.g.
303 :
304 : FD_LOG_NOTICE(( "cache line %016lx\n\t"
305 : "%02x: " FD_LOG_HEX16_FMT "\n\t"
306 : "%02x: " FD_LOG_HEX16_FMT "\n\t"
307 : "%02x: " FD_LOG_HEX16_FMT "\n\t"
308 : "%02x: " FD_LOG_HEX16_FMT,
309 : (ulong)mem,
310 : 0U, FD_LOG_HEX16_FMT_ARGS( mem ),
311 : 16U, FD_LOG_HEX16_FMT_ARGS( mem+16 ),
312 : 32U, FD_LOG_HEX16_FMT_ARGS( mem+32 ),
313 : 48U, FD_LOG_HEX16_FMT_ARGS( mem+48 ) ));
314 :
315 : would log something like:
316 :
317 : NOTICE 01-23 04:56:07.890123 45678 f0 0 src/foo.c(901): cache line 0123456789abcd00
318 : 00: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
319 : 10: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
320 : 20: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
321 : 30: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
322 :
323 : to the ephemeral log typically (and a more detailed message to the
324 : permanent log). And similarly for the other log levels. b should be
325 : safe against multiple evaluation. */
326 :
327 : #define FD_LOG_HEX16_FMT "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"
328 : #define FD_LOG_HEX16_FMT_ARGS(b) \
329 : (uint)(((uchar const *)(b))[ 0]), (uint)(((uchar const *)(b))[ 1]), \
330 : (uint)(((uchar const *)(b))[ 2]), (uint)(((uchar const *)(b))[ 3]), \
331 : (uint)(((uchar const *)(b))[ 4]), (uint)(((uchar const *)(b))[ 5]), \
332 : (uint)(((uchar const *)(b))[ 6]), (uint)(((uchar const *)(b))[ 7]), \
333 : (uint)(((uchar const *)(b))[ 8]), (uint)(((uchar const *)(b))[ 9]), \
334 : (uint)(((uchar const *)(b))[10]), (uint)(((uchar const *)(b))[11]), \
335 : (uint)(((uchar const *)(b))[12]), (uint)(((uchar const *)(b))[13]), \
336 : (uint)(((uchar const *)(b))[14]), (uint)(((uchar const *)(b))[15])
337 :
338 : #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"
339 : #define FD_LOG_HEX20_FMT_ARGS(b) \
340 : FD_LOG_HEX16_FMT_ARGS(b), \
341 : (uint)(((uchar const *)(b))[16]), (uint)(((uchar const *)(b))[17]), \
342 : (uint)(((uchar const *)(b))[18]), (uint)(((uchar const *)(b))[19])
343 :
344 26330 : #define FD_LOG_NAME_MAX (40UL)
345 :
346 : FD_PROTOTYPES_BEGIN
347 :
348 : /* APPLICATION LOGICAL IDENTIFIERS ************************************/
349 :
350 : /* fd_log_app_id() returns an integer application id of the application
351 : to which the caller belongs. An application id is intended, at a
352 : minimum, to uniquely identify all concurrently running applications
353 : in the enterprise. This is cheap after the first call. */
354 :
355 : FD_FN_PURE ulong fd_log_app_id( void );
356 :
357 : /* fd_log_app() returns a non-NULL pointer to a cstr describing the
358 : application to which the caller belongs. This is typically something
359 : provided to the caller when the caller started. This is cheap after
360 : the first call and the lifetime of the returned string is infinite
361 : from the caller's point of view. strlen(fd_log_app()) is in
362 : [1,FD_LOG_NAME_MAX). */
363 :
364 : FD_FN_CONST char const * fd_log_app( void ); /* Pointer is CONST, cstr pointed at is PURE */
365 :
366 : /* fd_log_thread_id() returns the caller's integer thread id. A thread
367 : id is intended, at a minimum, to be unique over all concurrently
368 : running threads in the application. This is cheap after the first
369 : call. */
370 :
371 : ulong fd_log_thread_id( void );
372 :
373 : /* fd_log_thread() returns a non-NULL pointer to a cstr describing the
374 : caller. This defaults to some target specific default essentially
375 : determined at the caller's startup and can be explicitly set by the
376 : caller. This is cheap after the first call within a thread and the
377 : lifetime of the returned pointer is until the next time the name is
378 : set or the caller terminates. strlen(fd_log_thread()) is in
379 : [1,FD_LOG_NAME_MAX). */
380 :
381 : char const * fd_log_thread( void );
382 :
383 : /* fd_log_thread_set() sets the caller's description to the cstr
384 : pointed to by name. A NULL name and/or an empty name ("") indicate
385 : to reset to the description that would have been assigned if the
386 : caller started at the time this is called. name is not changed by
387 : the function and the fd_log does not retain any interest in name
388 : after return. The actual resulting description will be truncated to
389 : a strlen of FD_LOG_NAME_MAX-1 if name is longer and potentially
390 : sanitized in other ways as necessary for the log. */
391 :
392 : void
393 : fd_log_thread_set( char const * name );
394 :
395 : /* APPLICATION PHYSICAL IDENTIFIERS ***********************************/
396 :
397 : /* fd_log_host_id() returns an integer host id of the host on which the
398 : caller is running. A host id is intended, at a minimum, to uniquely
399 : identify a host enterprise wide. This cheap after the first call. */
400 :
401 : FD_FN_PURE ulong fd_log_host_id( void );
402 :
403 : /* fd_log_host() returns a non-NULL pointer to a cstr describing the
404 : host on which the caller is running. In simple cases, this defaults
405 : to the hostname. In general cases, this is something provided to the
406 : caller at that caller's startup. This is cheap after the first call
407 : and the lifetime of the returned string is infinite from the caller's
408 : point of view. strlen(fd_log_host()) is in [1,FD_LOG_NAME_MAX). */
409 :
410 : FD_FN_CONST char const * fd_log_host( void ); /* ptr is CONST, cstr pointed at is PURE */
411 :
412 : /* fd_log_cpu_id() returns an integer cpu id of one of the cpus on
413 : where the caller was allowed to run when first called by a thread (or
414 : boot if the caller is the one that booted fd). A cpu id is intended
415 : to uniquely identify a cpu on a host (e.g. for a host with
416 : homogeneous x86 cores, idx from /proc/cpuinfo). This is cheap after
417 : the first call. */
418 :
419 : ulong fd_log_cpu_id( void );
420 :
421 : /* fd_log_cpu() returns a non-NULL pointer to a cstr describing the cpu
422 : on which the caller is running. This defaults to some target
423 : specific default determined when first called on a thread (or boot if
424 : the caller is the one that booted fd). This is cheap after the first
425 : call by a thread and the returned string is infinite from the
426 : caller's point of view. strlen(fd_log_cpu()) is in
427 : [1,FD_LOG_NAME_MAX). */
428 :
429 : char const * fd_log_cpu( void );
430 :
431 : /* fd_log_cpu_set() sets the description of the cpu on which the caller
432 : is running on to the cstr pointed to by name. A NULL name and/or an
433 : empty name ("") indicate to reset to the description that would have
434 : been assigned if the caller started at the time this is called. name
435 : is not changed by the function and the fd_log does not retain any
436 : interest in name after return. The actual resulting description will
437 : be truncated to a strlen of FD_LOG_NAME_MAX-1 if name is longer and
438 : potentially sanitized in other ways as necessary for the log. */
439 :
440 : void
441 : fd_log_cpu_set( char const * name );
442 :
443 : /* THREAD GROUP RELATED IDENTIFIERS ***********************************/
444 :
445 : /* fd_log_group_id() returns the thread group id of the thread group to
446 : which the caller belongs. The thread group id is intended, at a
447 : minimum, to be unique over all thread groups on a host. In simple
448 : cases, this is the OS pid of the process to which the caller belongs.
449 : In general cases, this is typically something provided to the caller
450 : when the caller started. This is cheap after the first call.
451 :
452 : For sanity, this should be at least 2 (e.g. in POSIX group_id is
453 : equivalent to pid and pids<=1 are special such that a user is highly
454 : likely to assume group ids <= 1 are special). */
455 :
456 : FD_FN_PURE ulong fd_log_group_id( void );
457 :
458 : /* fd_log_group() returns a non-NULL pointer to a cstr describing the
459 : thread group to which the caller belongs. In simple cases, this
460 : defaults to an abbreviated version of argv[0]. In general cases,
461 : this is typically something provided to the caller when the caller
462 : started. This is cheap after the first call and the lifetime of the
463 : returned string is infinite from the caller's point of view. The
464 : actual pointer and cstr is the same for all threads in the group. */
465 :
466 : FD_FN_CONST char const * fd_log_group( void ); /* ptr is CONST, cstr pointed at is PURE */
467 :
468 : /* fd_log_tid() returns the caller's thread group thread id. A thread
469 : group thread id is intended, at a minimum, to be unique over all
470 : running threads in a thread group. In simple cases, this is the
471 : caller's OS tid. In general cases, this is typically something
472 : provided to the thread when that thread started. This is cheap after
473 : the first call. */
474 :
475 : ulong fd_log_tid( void );
476 :
477 : /* fd_log_user_id() returns the user id of the thread group to which the
478 : caller belongs. The user id is intended, at a minimum, to be unique
479 : over all users on a host. In simple cases, this is the OS uid of the
480 : process to which the caller belongs. In general cases, this is
481 : typically something provided to the caller when the caller started.
482 : This is cheap after the first call. */
483 :
484 : FD_FN_PURE ulong fd_log_user_id( void );
485 :
486 : /* fd_log_user() returns a non-NULL pointer to a cstr describing the
487 : user that created the thread group to which the caller belongs. In
488 : simple cases, this defaults to the LOGNAME / login that started the
489 : process running the caller. In general cases, this is something
490 : provided to the caller at that caller's startup. This is cheap after
491 : the first call and the lifetime of the returned string is infinite
492 : from the caller's point of view. strlen(fd_log_user()) is in
493 : [1,FD_LOG_NAME_MAX). */
494 :
495 : FD_FN_CONST char const * fd_log_user( void ); /* ptr is CONST, cstr pointed at is PURE */
496 :
497 : /* fd_log_group_id_query() returns the status of group_id. Will be a
498 : FD_LOG_GROUP_ID_QUERY_* code. Positive indicates live, zero
499 : indicates dead, negative indicates failure reason. */
500 :
501 0 : #define FD_LOG_GROUP_ID_QUERY_LIVE (1) /* group_id is live */
502 0 : #define FD_LOG_GROUP_ID_QUERY_DEAD (0) /* group_id is not live */
503 0 : #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) */
504 0 : #define FD_LOG_GROUP_ID_QUERY_PERM (-2) /* query failed because caller lacks permissions */
505 0 : #define FD_LOG_GROUP_ID_QUERY_FAIL (-3) /* query failed for unknown reason (should not happen) */
506 :
507 : int fd_log_group_id_query( ulong group_id );
508 :
509 : /* FIXME: TID DESC? */
510 :
511 : /* Build info APIs ****************************************************/
512 :
513 : /* fd_log_build_info points in the caller's address space to the first
514 : byte of a memory region of size fd_log_build_info_sz containing a
515 : cstr with information about the environment in which the calling code
516 : was built.
517 :
518 : If build information was not available at compile time, the build
519 : info will be the empty string and size will be one.
520 :
521 : The value in this field is the last time the build info file was
522 : generated (such that, in a development compile-execute-debug
523 : iteration, the build info reflect the build environment since the
524 : last "make clean" or the developer manually deleted the build info).
525 :
526 : Code that is meant to be general purpose should not assume any
527 : particular format, contents, length, etc. The build system,
528 : packaging manager, distribution manager, etc might external impose
529 : additional requirements on this string for application specific code
530 : though. */
531 :
532 : extern char const fd_log_build_info[] __attribute__((aligned(1)));
533 : extern ulong const fd_log_build_info_sz; /* == strlen( fd_log_build_info ) + 1UL */
534 :
535 : /* Logging helper APIs ************************************************/
536 :
537 : /* fd_log_wallclock_host( NULL ) reads the host's wallclock as ns since
538 : the UNIX epoch GMT. On x86, this uses clock_gettime/CLOCK_REALTIME
539 : under the hood and is reasonably cheap (~25-50 ns nowadays). But it
540 : still may involve system calls under the hood and is much slower
541 : than, say, RTSDC. */
542 :
543 : long fd_log_wallclock_host( void const * _ ); /* fd_clock_func_t compat */
544 :
545 : /* fd_log_wallclock reads the log's timesource to get the ns since the
546 : UNIX epoch GMT. By default, this is fd_log_wallclock_host but the
547 : thread group can be configures this to use an alternative time source
548 : if desired. */
549 :
550 : long fd_log_wallclock( void ); /* FIXME: Make fd_clock_func_t compat */
551 :
552 : /* fd_log_wallclock_set configures the log to use "clock( args )" as its
553 : time source. This time source should report ns since the UNIX epoch
554 : GMT. There should be no concurrent users of the log when this is
555 : called. */
556 :
557 : void
558 : fd_log_wallclock_set( fd_clock_func_t clock,
559 : void const * args );
560 :
561 : /* fd_log_wallclock_cstr( t, buf ) pretty prints the wallclock
562 : measurement t as:
563 : "YYYY-MM-DD hh:mm:ss.nnnnnnnnn GMT+TZ".
564 : or in cases where conversion is not locally practical:
565 : " ssssssssss.nnnnnnnnn s UNIX"
566 : buf must be a character buffer of at least
567 : FD_LOG_WALLCLOCK_CSTR_BUF_SZ bytes. Returns buf and buf will be
568 : populated with the desired cstr on return. */
569 :
570 : #define FD_LOG_WALLCLOCK_CSTR_BUF_SZ (37UL)
571 :
572 : char *
573 : fd_log_wallclock_cstr( long t,
574 : char * buf );
575 :
576 : /* fd_log_sleep puts the calling thread to sleep for dt ns. dt<=0 is
577 : assumed to be a sched_yield request. Returns the amount of sleep
578 : remaining if the sleep was interrupted. */
579 :
580 : long
581 : fd_log_sleep( long dt );
582 :
583 : /* fd_log_wait_until waits until fd_log_wallclock() is at least then.
584 : Returns the time on the clock when the wait ended (will be at least
585 : then). This makes a best effort to be a good citizen and sleep /
586 : yield / hyperthreading friendly the caller while also being as
587 : precise on the wait as possible (i.e. limited by the overhead
588 : fd_log_wallclock). That is, as the time remaining to wait decreases,
589 : the wait gets progressively more precise and CPU intensive. If
590 : remaining is the number of ns remaining in the wait, then:
591 :
592 : remaining <~ 1 us: spin
593 : 1 us <~ remaining <~ 100 ms: hyper threading friendly spin
594 : 100 ms <~ remaining <~ 1 s: yielding spin
595 : 1 s <~ remaining : sleep until ~100 ms remaining
596 :
597 : If (as is usually the case) fd_log_sleep precision is much better
598 : than <<~100 ms accurate, FD_YIELD() delays take <<~100ms and
599 : FD_SPIN_PAUSE() << 1 us, the return value will be an accurate read of
600 : the fd_log_wallclock at the time of return and within the overhead of
601 : fd_log_wallclock. */
602 :
603 : long
604 : fd_log_wait_until( long then );
605 :
606 : /* fd_log_flush() manually flushes the log (e.g. log a bunch of low
607 : priority messages and then flush to ensure the bunch gets written out
608 : before proceeding). */
609 :
610 : void
611 : fd_log_flush( void );
612 :
613 : /* These all the logging levels to be configured at runtime. These do
614 : no validation of there inputs so the values may not behave like the
615 : caller things (e.g. stderr<logfile will be treated as
616 : stderr==logfile, flush<stderr will be treated as flush==stderr,
617 : core<4 will be treated as 4). colorize returns the colorization mode
618 : of the ephemeral log. Currently, zero indicates no colorization of
619 : the ephemeral log and non-zero indicates to colorize it. */
620 :
621 : int fd_log_colorize( void );
622 : int fd_log_level_logfile ( void );
623 : int fd_log_level_stderr ( void );
624 : int fd_log_level_flush ( void );
625 : int fd_log_level_core ( void );
626 :
627 : void fd_log_colorize_set ( int mode );
628 : void fd_log_level_logfile_set( int level );
629 : void fd_log_level_stderr_set ( int level );
630 : void fd_log_level_flush_set ( int level );
631 : void fd_log_level_core_set ( int level );
632 :
633 : void fd_log_enable_signal_handler( void );
634 : void fd_log_enable_unclean_exit( void );
635 :
636 : /* These functions are for fd_log internal use only. */
637 :
638 : void
639 : fd_log_private_fprintf_0( int fd, char const * fmt, ... ) __attribute__((format(printf,2,3))); /* Type check the fmt string at compile time */
640 :
641 : void
642 : fd_log_private_fprintf_nolock_0( int fd, char const * fmt, ... ) __attribute__((format(printf,2,3))); /* Type check the fmt string at compile time */
643 :
644 : char const *
645 : fd_log_private_0( char const * fmt, ... ) __attribute__((format(printf,1,2))); /* Type check the fmt string at compile time */
646 :
647 : void
648 : fd_log_private_1( int level,
649 : long now,
650 : char const * file,
651 : int line,
652 : char const * func,
653 : char const * msg );
654 :
655 : void
656 : fd_log_private_2( int level,
657 : long now,
658 : char const * file,
659 : int line,
660 : char const * func,
661 : char const * msg ) __attribute__((noreturn)); /* Let compiler know this will not be returning */
662 :
663 : void
664 : fd_log_private_raw_2( char const * file,
665 : int line,
666 : char const * func,
667 : char const * msg ) __attribute__((noreturn)); /* Let compiler know this will not be returning */
668 :
669 : char const *
670 : fd_log_private_hexdump_msg( char const * tag,
671 : void const * mem,
672 : ulong sz );
673 :
674 : void
675 : fd_log_private_boot( int * pargc,
676 : char *** pargv );
677 :
678 : void
679 : fd_log_private_boot_custom( int * lock,
680 : ulong app_id,
681 : char const * app,
682 : ulong thread_id,
683 : char const * thread,
684 : ulong host_id,
685 : char const * host,
686 : ulong cpu_id,
687 : char const * cpu,
688 : ulong group_id,
689 : char const * group,
690 : ulong tid,
691 : ulong user_id,
692 : char const * user,
693 : int dedup,
694 : int colorize,
695 : int level_logfile,
696 : int level_stderr,
697 : int level_flush,
698 : int level_core,
699 : int log_fd,
700 : char const * log_path );
701 :
702 : void
703 : fd_log_private_halt( void );
704 :
705 : ulong fd_log_private_main_stack_sz( void ); /* Returns ulimit -s (if reasonable) on success, 0 on failure (logs details) */
706 :
707 : ulong
708 : fd_log_private_tid_default( void );
709 :
710 : ulong
711 : fd_log_private_cpu_id_default( void );
712 :
713 : void
714 : fd_log_private_stack_discover( ulong stack_sz, /* Size the stack is expected to be */
715 : ulong * _stack0, /* [*_stack0,*_stack1) is the caller's stack region (will have stack_sz */
716 : ulong * _stack1 ); /* bytes) on success. Both set to 0UL on failure (logs details). */
717 :
718 : /* These are exposed to allow the user to override the values set at
719 : boot/halt time. If these are used, they are usually a sign of
720 : working around a higher level architectural or operational issue. */
721 :
722 : void fd_log_private_app_id_set ( ulong app_id );
723 : void fd_log_private_thread_id_set( ulong thread_id );
724 : void fd_log_private_host_id_set ( ulong host_id );
725 : void fd_log_private_cpu_id_set ( ulong cpu_id );
726 : void fd_log_private_group_id_set ( ulong group_id );
727 : void fd_log_private_tid_set ( ulong tid );
728 : void fd_log_private_user_id_set ( ulong user_id );
729 :
730 : void fd_log_private_app_set ( char const * app ); /* Not thread safe */
731 : void fd_log_private_host_set ( char const * host ); /* Not thread safe */
732 : void fd_log_private_group_set( char const * group ); /* Not thread safe */
733 : void fd_log_private_user_set ( char const * user ); /* Not thread safe */
734 :
735 : /* This is exposed to allow the user to know the expected file descriptor
736 : for filtering and security, it should never be used to actually write
737 : logs and that should be done by the functions in fd_log.h */
738 : int fd_log_private_logfile_fd( void );
739 :
740 : FD_PROTOTYPES_END
741 :
742 : #endif /* HEADER_fd_src_util_log_fd_log_h */
|