PreviousUpNext

15.4.940  src/lib/src/lib/thread-kit/src/lib/logger.pkg

# logger.pkg
#
# See the overview comments in
#
#     src/lib/src/lib/thread-kit/src/lib/logger.api
#
# This version of this package is adapted from
# Cliff Krumvieda's utility for logging from
# threadkit programs.
#
# This package is heavily used by:
#     src/lib/x-kit/xclient/src/stuff/xlogger.pkg
#
# See also:
#     src/lib/src/lib/thread-kit/src/lib/thread-deathwatch.pkg
#     src/lib/src/lib/thread-kit/src/lib/uncaught-exception-reporting.pkg

# Compiled by:
#     src/lib/std/standard.lib



###            "Einstein argued that there must be
###             simplified explanations of nature,
###             because God is not capricious or arbitrary.
###
###            "No such faith comforts the software engineer."
###
###                         -- Fred Brooks, Jr.



# From: Hue White <hue.white@gmail.com> 
# Subject: Re: [Mythryl] Mythryl 5.1.0: debug logging. 
# To: cynbe@mythryl.org 
# Cc: mythryl@mythryl.org 
# Date: Tue, 27 Sep 2011 09:48:03 -0500 
#  
#  
# Cynbe, 
#  
# I can't help but think of automatically logging entry into each function, 
# although thunks are problematic. But having this capability at work has saved 
# untold hours of confusion and ennui.... 
#  
# Hue 
#
# CrT: This might be an actual use for
#
#          src/lib/compiler/debugging-and-profiling/profiling/tdp-instrument.pkg
#
#      -- Blume's hack supporting adding arbitrary code on a per-function basis.
#      (in my impression at least).  Presumably this would in practice be switched
#      on using something like a per-file
#
#          #DO set_control "function_entry_logging" "TRUE";
#
#



stipulate
    include package   threadkit;                # threadkit                     is from   src/lib/src/lib/thread-kit/src/core-thread-kit/threadkit.pkg
    #
    package fil =  file__premicrothread;        # file__premicrothread          is from   src/lib/std/src/posix/file--premicrothread.pkg
    package ns  =  number_string;               # number_string                 is from   src/lib/std/src/number-string.pkg
    package psx =  posixlib;                    # posixlib                      is from   src/lib/std/src/psx/posixlib.pkg
    package str =  string;                      # string                        is from   src/lib/std/string.pkg
    package th  =  microthread;                 # microthread                   is from   src/lib/src/lib/thread-kit/src/core-thread-kit/microthread.pkg
    package u1w =  one_word_unt;                # one_word_unt                  is from   src/lib/std/one-word-unt.pkg
    package tsc =  thread_scheduler_control;    # thread_scheduler_control      is from   src/lib/src/lib/thread-kit/src/posix/thread-scheduler-control.pkg
    package tsr =  thread_scheduler_is_running; # thread_scheduler_is_running   is from   src/lib/src/lib/thread-kit/src/core-thread-kit/thread-scheduler-is-running.pkg
herein

    package   logger
    : (weak)  Logger                            # Logger                        is from   src/lib/src/lib/thread-kit/src/lib/logger.api
    {

        #############################################################################################3
        # Log server

        # All log printing (i.e., calls to log_if)
        # ultimately goes through this mailslot:
        #
        print_if_slot =   make_mailslot():   Mailslot( String );
        plea_slot     =   make_mailslot():   Mailslot (Void -> Void);


                                                                                                # Run_At                                is from   src/lib/src/lib/thread-kit/src/core-thread-kit/run-at.api
                                                                                                # Thread_Scheduler_Control              is from   src/lib/src/lib/thread-kit/src/glue/thread-scheduler-control.api
                                                                                                # thread_scheduler_control              is from   src/lib/src/lib/thread-kit/src/posix/thread-scheduler-control.pkg
                                                                                                # thread_scheduler_control_g            is from   src/lib/src/lib/thread-kit/src/glue/thread-scheduler-control-g.pkg

        my _ =  {   tsc::note_mailslot    ("logging: log_if", print_if_slot);
                    tsc::note_mailslot    ("logging: request",  plea_slot);

                    tsc::note_imp
                      {
                        name => "logging: log-imp",
                        #
                        at_startup  =>  start_log_imp,
                        at_shutdown =>  log_imp_shutdown
                      };
                }
                where
                    # 
                    fun log_imp ()
                        =
                        for (;;) {      
                            #
                            do_one_mailop [
                                #
                                take_from_mailslot'  print_if_slot
                                    ==>
                                    (\\ message = fil::logprint message),

                                take_from_mailslot'  plea_slot
                                    ==>
                                    (\\ f = f())
                            ];
                        };


                    fun start_log_imp ()
                        =
                        {
# printf "start_log_imp/AAA       -- logger.pkg\n";
                            make_thread  "logging imp"  log_imp;
                            ();
                        };


                    fun log_imp_shutdown  ()
                        =
                        {   *fil::logger_cleanup ();
                            #
                            fil::logger_cleanup
                                :=
                                (\\ () = ());
                        };
                end;

        stipulate

            fun carefully f
                =
                if (tsr::thread_scheduler_is_running ())
                    #
                    put_in_mailslot (plea_slot, f);
                else
                    f ();
                fi;

            fun carefully' f
                =
                if (tsr::thread_scheduler_is_running ())
                    #
                    reply_drop =  make_oneshot_maildrop ();

                    put_in_mailslot (plea_slot, {.  put_in_oneshot (reply_drop, f());  });

                    get_from_oneshot  reply_drop;

                else
                    f ();
                fi;
        herein

            fun make_logtree_leaf arg                   = carefully' (\\ () =  fil::make_logtree_leaf arg);

            fun enable          tm                      = carefully  (\\ () =  fil::enable tm);                         # Enable  logging per log subtree.
            fun disable         tm                      = carefully  (\\ () =  fil::disable tm);                        # Disable logging per log subtree.
            fun enable_node     tm                      = carefully  (\\ () =  fil::enable_node tm);                    # Enable  logging per logtree node.

            fun set_logger_to  f                        = carefully  (\\ () =  fil::set_logger_to f);                   # Select destination file/whatever.
            fun subtree_nodes_and_log_flags  root       = carefully' (\\ () =  fil::subtree_nodes_and_log_flags root);

        end;


        stipulate
            fun drop_leading_blanks  string
                =
                {   =~ = regex::(=~);

                    if (string =~ ./^\s*$/)
                        #
                        "";
                        #
                    else
                        # Drop leading whitespace:
                        #
                        string =    case (regex::find_first_match_to_ith_group 1 ./^\s*(\S.*)$/ string)
                                        THE x => x; 
                                        NULL  => string;
                                    esac;       
                        string;
                    fi;
                };
        herein

            fun make_logstring (fil::LOGTREE_NODE { name => logswitch_name, ... },  severity, make_message_string_fn)
                =       
                {
                    # Construct the 'log_if' string to print,
                    # and then pass it to the log imp.
                    #
                    # The point of constructing the string here,
                    # rather than in the  log_if  call, is that
                    # this way we avoid the work of creating it
                    # if we're not going to print it (i.e., if
                    # logging is disabled for that call).
                    #
                    # NB: The line format we generate here should
                    # stay synched with those in
                    #
                    #     src/lib/std/src/io/winix-text-file-for-os-g--premicrothread.pkg
                    #     src/c/main/error-reporting.c

                    # Get pid and left-pad with blanks to width 8:
                    #
                    pid =  psx::get_process_id ();                                                      # Here we do   sprintf "%08d" pid   by hand to simplify maintenance
                    pid =  int::to_string pid;                                                          # by keeping the code parallel to that in   src/lib/std/src/io/winix-text-file-for-os-g--premicrothread.pkg
                    pid =  ns::pad_left '0' 8 pid;                                                      # where sprintf is not available due to package dependency graph acyclicity requirement.

                    ptid =  hostthread::get_hostthread_ptid ();
                    ptid =  u1w::to_string ptid;
                    ptid =  ns::pad_left '0' 8 ptid;                                                    # where sprintf is not available.

                    thread      =  th::get_current_microthread ();
                    thread_id   =  th::get_thread's_id    thread;
                    thread_name =  th::get_thread's_name  thread;
                    task        =  th::get_thread's_task  thread;
                    task_id     =  th::get_task's_id      task;

                    tid =  int::to_string  thread_id;
                    tid =  ns::pad_left '0' 8 tid;

                    tad =  int::to_string  task_id;
                    tad =  ns::pad_left '0' 8 tad;

                    nam =  thread_name;
                    pad =  ns::pad_right ' ' (48 - str::length_in_bytes nam) "";

#                   time_string  =  date::strftime "%Y-%m-%d:%H:%M:%S" (date::from_time_local (time::get_current_time_utc()));          # "2010-01-05:14:17:23" or such.
                    time_string  =  time::format 6 (time::get_current_time_utc());                                                      # "1262722876.273621"   or such.
                        #
                        # NB: If you change the time_string content/format you
                        #     should probably make corresponding changes in log_if in
                        #
                        #          src/c/main/error-reporting.c

                    message_string
                        =
                        drop_leading_blanks (make_message_string_fn ());


                    # The intent here is
                    #
                    #   1) That doing unix 'sort' on a logfile will do the right thing:
                    #      sort first by time, then by process id, then by thread id.
                    #
                    #   2) To facilitate egrep/perl processing, e.g. doing stuff like
                    #            egrep 'pid=021456' logfile | egrep 'tnm=color-imp'
                    #
                    logstring =  "time="   + time_string 
                              +  " pid="   + pid
                              +  " ptid="  + ptid
                              +  " task="  + tad
                              +  " tid="   + tid
                              +  " sev="   + (int::to_string severity)
                              +  " name='" + nam
                              +  "'"       + pad
                              +  " msg="  + message_string
                              +  "    \t(" + logswitch_name
                              +  ")\n";

                    logstring;
                };
        end;

        fun log_if (logtree_node as fil::LOGTREE_NODE { logging, name, ... })  severity  make_message_string_fn
            =
            if (*logging)
                if (not (tsr::thread_scheduler_is_running ()))
                    #
                    logstring = make_logstring  (logtree_node, severity, make_message_string_fn);
                    fil::logprint  logstring;
                    ();
                else
                    #
                    # Originally here we always did
                    #
                    #     fil::logprint (make_logstring  (logtree_node, make_message_string_fn));
                    #
                    # thus doing the print via our thread for mutual exclusion
                    # in standard concurrent-programming style.  Unfortunately,
                    # this produces problems when trying to log through oddball
                    # code like the microthread_preemptive_scheduler itself, where thread-scheduling
                    # is off or SIGARLM is disabled or such.
                    #
                    # Since we're mostly just doing a single unbuffered write to a
                    # unix file descriptor on these calls, which unix semantics
                    # guarantees to be atomic anyhow, there is actually vanishingly
                    # little need for mutual exclusion except when we're actually
                    # opening the file (LOG_TO_FILE case).
                    #   
                    # Consequently, we currently avoid going through print_if_slot
                    # and the log_if thread in all cases except LOG_TO_FILE:
                    #
                    #
                    logstring = make_logstring  (logtree_node, severity, make_message_string_fn);
                    #
                    case (fil::logger_is_set_to ())
                        #
                        fil::LOG_TO_NULL   =>  ();
                        fil::LOG_TO_FILE _ =>  put_in_mailslot (print_if_slot, logstring);
                        _                  =>  fil::logprint          logstring;
                    esac;
                fi;
            fi;

#           if (*logging)
#    
#    
#               if (tsr::thread_scheduler_is_running ())
#                   #
#                   logstring =  make_logstring  (logtree_node, make_message_string_fn);
#                   put_in_mailslot (print_if_slot, logstring);
#               fi;
#    
#    #          if (not (tsr::thread_scheduler_is_running ()))
#    #              #
#    # #                    logprint  logstring;
#    #              ();
#    #          else
#    #              case *log_to
#    #                  #
#    #                  LOG_TO_NULL   =>  ();
#    #                  LOG_TO_FILE _ =>  put_in_mailslot (print_if_slot, logstring);
#    #                  _             =>  logprint          logstring;
#    #              esac;
#    #          fi;
#           fi;

        # This is an ugly little hack to solve a package cycle problem in
        #     src/lib/src/lib/thread-kit/src/core-thread-kit/microthread-preemptive-scheduler.pkg
        #
        stipulate
            # This is going to be called at weird places
            # within the thread scheduler, so we COMPLETELY
            # skip going through the print_if_slot and our
            # log_if thread:
            #
            fun log_if (logtree_node as fil::LOGTREE_NODE { logging, name, ... })  severity  make_message_string_fn
                =
                if *logging
                    #
                    logstring = make_logstring  (logtree_node, severity, make_message_string_fn);
                    #
                    fil::logprint  logstring;
                fi;

            thread_scheduler_logging =   make_logtree_leaf { parent => fil::all_logging, name => "thread_scheduler_logging", default => FALSE };
            to_log                   =   log_if  thread_scheduler_logging  0;
        herein
            my _ =   (microthread_preemptive_scheduler::trace_backpatchfn  :=  to_log);
        end;
    };                                                  # package logging

end;


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext