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