LCOV - code coverage report
Current view: top level - util/log - fd_log.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 20 27 74.1 %
Date: 2026-08-14 04:54:57 Functions: 0 0 -

          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      159957 : #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      195465 : #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    60062046 : #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      197382 : #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      394356 : #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 >15250*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 >15242*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 10743886977 : #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       27862 : #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 */

Generated by: LCOV version 1.14