/ common / log.h
log.h
  1  #pragma once
  2  
  3  #include <chrono>
  4  #include <cstring>
  5  #include <sstream>
  6  #include <iostream>
  7  #include <thread>
  8  #include <vector>
  9  #include <algorithm>
 10  #include <cinttypes>
 11  
 12  // --------------------------------
 13  //
 14  // Basic usage:
 15  //
 16  // --------
 17  //
 18  //  The LOG() and LOG_TEE() macros are ready to go by default
 19  //   they do not require any initialization.
 20  //
 21  //  LOGLN() and LOG_TEELN() are variants which automatically
 22  //   include \n character at the end of the log string.
 23  //
 24  //  LOG() behaves exactly like printf, by default writing to a logfile.
 25  //  LOG_TEE() additionally, prints to the screen too ( mimics Unix tee command ).
 26  //
 27  //  Default logfile is named
 28  //   "llama.<threadID>.log"
 29  //  Default LOG_TEE() secondary output target is
 30  //   stderr
 31  //
 32  //  Logs can be dynamically disabled or enabled using functions:
 33  //   log_disable()
 34  //  and
 35  //   log_enable()
 36  //
 37  //  A log target can be changed with:
 38  //   log_set_target( string )
 39  //    creating and opening, or re-opening a file by string filename
 40  //  or
 41  //   log_set_target( FILE* )
 42  //    allowing to point at stderr, stdout, or any valid FILE* file handler.
 43  //
 44  // --------
 45  //
 46  // End of Basic usage.
 47  //
 48  // --------------------------------
 49  
 50  // Specifies a log target.
 51  //  default uses log_handler() with "llama.log" log file
 52  //  this can be changed, by defining LOG_TARGET
 53  //  like so:
 54  //
 55  //  #define LOG_TARGET (a valid FILE*)
 56  //  #include "log.h"
 57  //
 58  //  or it can be simply redirected to stdout or stderr
 59  //  like so:
 60  //
 61  //  #define LOG_TARGET stderr
 62  //  #include "log.h"
 63  //
 64  //  The log target can also be redirected to a different function
 65  //  like so:
 66  //
 67  //  #define LOG_TARGET log_handler_different()
 68  //  #include "log.h"
 69  //
 70  //  FILE* log_handler_different()
 71  //  {
 72  //      return stderr;
 73  //  }
 74  //
 75  //  or:
 76  //
 77  //  #define LOG_TARGET log_handler_another_one("somelog.log")
 78  //  #include "log.h"
 79  //
 80  //  FILE* log_handler_another_one(char*filename)
 81  //  {
 82  //      static FILE* logfile = nullptr;
 83  //      (...)
 84  //      if( !logfile )
 85  //      {
 86  //          fopen(...)
 87  //      }
 88  //      (...)
 89  //      return logfile
 90  //  }
 91  //
 92  #ifndef LOG_TARGET
 93      #define LOG_TARGET log_handler()
 94  #endif
 95  
 96  #ifndef LOG_TEE_TARGET
 97      #define LOG_TEE_TARGET stderr
 98  #endif
 99  
100  // Utility for synchronizing log configuration state
101  //  since std::optional was introduced only in c++17
102  enum LogTriState
103  {
104      LogTriStateSame,
105      LogTriStateFalse,
106      LogTriStateTrue
107  };
108  
109  // Utility to obtain "pid" like unique process id and use it when creating log files.
110  inline std::string log_get_pid()
111  {
112     static std::string pid;
113     if (pid.empty())
114     {
115         // std::this_thread::get_id() is the most portable way of obtaining a "process id"
116         //  it's not the same as "pid" but is unique enough to solve multiple instances
117         //  trying to write to the same log.
118         std::stringstream ss;
119         ss << std::this_thread::get_id();
120         pid = ss.str();
121     }
122  
123     return pid;
124  }
125  
126  // Utility function for generating log file names with unique id based on thread id.
127  //  invocation with log_filename_generator( "llama", "log" ) creates a string "llama.<number>.log"
128  //  where the number is a runtime id of the current thread.
129  
130  #define log_filename_generator(log_file_basename, log_file_extension) log_filename_generator_impl(LogTriStateSame, log_file_basename, log_file_extension)
131  
132  // INTERNAL, DO NOT USE
133  inline std::string log_filename_generator_impl(LogTriState multilog, const std::string & log_file_basename, const std::string & log_file_extension)
134  {
135      static bool _multilog = false;
136  
137      if (multilog != LogTriStateSame)
138      {
139          _multilog = multilog == LogTriStateTrue;
140      }
141  
142      std::stringstream buf;
143  
144      buf << log_file_basename;
145      if (_multilog)
146      {
147          buf << ".";
148          buf << log_get_pid();
149      }
150      buf << ".";
151      buf << log_file_extension;
152  
153      return buf.str();
154  }
155  
156  #ifndef LOG_DEFAULT_FILE_NAME
157      #define LOG_DEFAULT_FILE_NAME log_filename_generator("llama", "log")
158  #endif
159  
160  // Utility for turning #define values into string literals
161  //  so we can have a define for stderr and
162  //  we can print "stderr" instead of literal stderr, etc.
163  #define LOG_STRINGIZE1(s) #s
164  #define LOG_STRINGIZE(s) LOG_STRINGIZE1(s)
165  
166  #define LOG_TEE_TARGET_STRING LOG_STRINGIZE(LOG_TEE_TARGET)
167  
168  // Allows disabling timestamps.
169  //  in order to disable, define LOG_NO_TIMESTAMPS
170  //  like so:
171  //
172  //  #define LOG_NO_TIMESTAMPS
173  //  #include "log.h"
174  //
175  #ifndef LOG_NO_TIMESTAMPS
176      #ifndef _MSC_VER
177          #define LOG_TIMESTAMP_FMT "[%" PRIu64 "] "
178          #define LOG_TIMESTAMP_VAL , (std::chrono::duration_cast<std::chrono::duration<std::uint64_t>>(std::chrono::system_clock::now().time_since_epoch())).count()
179      #else
180          #define LOG_TIMESTAMP_FMT "[%" PRIu64 "] "
181          #define LOG_TIMESTAMP_VAL , (std::chrono::duration_cast<std::chrono::duration<std::uint64_t>>(std::chrono::system_clock::now().time_since_epoch())).count()
182      #endif
183  #else
184      #define LOG_TIMESTAMP_FMT "%s"
185      #define LOG_TIMESTAMP_VAL ,""
186  #endif
187  
188  #ifdef LOG_TEE_TIMESTAMPS
189      #ifndef _MSC_VER
190          #define LOG_TEE_TIMESTAMP_FMT "[%" PRIu64 "] "
191          #define LOG_TEE_TIMESTAMP_VAL , (std::chrono::duration_cast<std::chrono::duration<std::uint64_t>>(std::chrono::system_clock::now().time_since_epoch())).count()
192      #else
193          #define LOG_TEE_TIMESTAMP_FMT "[%" PRIu64 "] "
194          #define LOG_TEE_TIMESTAMP_VAL , (std::chrono::duration_cast<std::chrono::duration<std::uint64_t>>(std::chrono::system_clock::now().time_since_epoch())).count()
195      #endif
196  #else
197      #define LOG_TEE_TIMESTAMP_FMT "%s"
198      #define LOG_TEE_TIMESTAMP_VAL ,""
199  #endif
200  
201  // Allows disabling file/line/function prefix
202  //  in order to disable, define LOG_NO_FILE_LINE_FUNCTION
203  //  like so:
204  //
205  //  #define LOG_NO_FILE_LINE_FUNCTION
206  //  #include "log.h"
207  //
208  #ifndef LOG_NO_FILE_LINE_FUNCTION
209      #ifndef _MSC_VER
210          #define LOG_FLF_FMT "[%24s:%5d][%24s] "
211          #define LOG_FLF_VAL , __FILE__, __LINE__, __FUNCTION__
212      #else
213          #define LOG_FLF_FMT "[%24s:%5ld][%24s] "
214          #define LOG_FLF_VAL , __FILE__, (long)__LINE__, __FUNCTION__
215      #endif
216  #else
217      #define LOG_FLF_FMT "%s"
218      #define LOG_FLF_VAL ,""
219  #endif
220  
221  #ifdef LOG_TEE_FILE_LINE_FUNCTION
222      #ifndef _MSC_VER
223          #define LOG_TEE_FLF_FMT "[%24s:%5d][%24s] "
224          #define LOG_TEE_FLF_VAL , __FILE__, __LINE__, __FUNCTION__
225      #else
226          #define LOG_TEE_FLF_FMT "[%24s:%5ld][%24s] "
227          #define LOG_TEE_FLF_VAL , __FILE__, (long)__LINE__, __FUNCTION__
228      #endif
229  #else
230      #define LOG_TEE_FLF_FMT "%s"
231      #define LOG_TEE_FLF_VAL ,""
232  #endif
233  
234  // INTERNAL, DO NOT USE
235  //  USE LOG() INSTEAD
236  //
237  #if !defined(_MSC_VER) || defined(__INTEL_LLVM_COMPILER) || defined(__clang__)
238      #define LOG_IMPL(str, ...)                                                                                      \
239      do {                                                                                                            \
240          if (LOG_TARGET != nullptr)                                                                                  \
241          {                                                                                                           \
242              fprintf(LOG_TARGET, LOG_TIMESTAMP_FMT LOG_FLF_FMT str "%s" LOG_TIMESTAMP_VAL LOG_FLF_VAL, __VA_ARGS__); \
243              fflush(LOG_TARGET);                                                                                     \
244          }                                                                                                           \
245      } while (0)
246  #else
247      #define LOG_IMPL(str, ...)                                                                                           \
248      do {                                                                                                                 \
249          if (LOG_TARGET != nullptr)                                                                                       \
250          {                                                                                                                \
251              fprintf(LOG_TARGET, LOG_TIMESTAMP_FMT LOG_FLF_FMT str "%s" LOG_TIMESTAMP_VAL LOG_FLF_VAL "", ##__VA_ARGS__); \
252              fflush(LOG_TARGET);                                                                                          \
253          }                                                                                                                \
254      } while (0)
255  #endif
256  
257  // INTERNAL, DO NOT USE
258  //  USE LOG_TEE() INSTEAD
259  //
260  #if !defined(_MSC_VER) || defined(__INTEL_LLVM_COMPILER) || defined(__clang__)
261      #define LOG_TEE_IMPL(str, ...)                                                                                                      \
262      do {                                                                                                                                \
263          if (LOG_TARGET != nullptr)                                                                                                      \
264          {                                                                                                                               \
265              fprintf(LOG_TARGET, LOG_TIMESTAMP_FMT LOG_FLF_FMT str "%s" LOG_TIMESTAMP_VAL LOG_FLF_VAL, __VA_ARGS__);                     \
266              fflush(LOG_TARGET);                                                                                                         \
267          }                                                                                                                               \
268          if (LOG_TARGET != nullptr && LOG_TARGET != stdout && LOG_TARGET != stderr && LOG_TEE_TARGET != nullptr)                         \
269          {                                                                                                                               \
270              fprintf(LOG_TEE_TARGET, LOG_TEE_TIMESTAMP_FMT LOG_TEE_FLF_FMT str "%s" LOG_TEE_TIMESTAMP_VAL LOG_TEE_FLF_VAL, __VA_ARGS__); \
271              fflush(LOG_TEE_TARGET);                                                                                                     \
272          }                                                                                                                               \
273      } while (0)
274  #else
275      #define LOG_TEE_IMPL(str, ...)                                                                                                           \
276      do {                                                                                                                                     \
277          if (LOG_TARGET != nullptr)                                                                                                           \
278          {                                                                                                                                    \
279              fprintf(LOG_TARGET, LOG_TIMESTAMP_FMT LOG_FLF_FMT str "%s" LOG_TIMESTAMP_VAL LOG_FLF_VAL "", ##__VA_ARGS__);                     \
280              fflush(LOG_TARGET);                                                                                                              \
281          }                                                                                                                                    \
282          if (LOG_TARGET != nullptr && LOG_TARGET != stdout && LOG_TARGET != stderr && LOG_TEE_TARGET != nullptr)                              \
283          {                                                                                                                                    \
284              fprintf(LOG_TEE_TARGET, LOG_TEE_TIMESTAMP_FMT LOG_TEE_FLF_FMT str "%s" LOG_TEE_TIMESTAMP_VAL LOG_TEE_FLF_VAL "", ##__VA_ARGS__); \
285              fflush(LOG_TEE_TARGET);                                                                                                          \
286          }                                                                                                                                    \
287      } while (0)
288  #endif
289  
290  // The '\0' as a last argument, is a trick to bypass the silly
291  //  "warning: ISO C++11 requires at least one argument for the "..." in a variadic macro"
292  //  so we can have a single macro which can be called just like printf.
293  
294  // Main LOG macro.
295  //  behaves like printf, and supports arguments the exact same way.
296  //
297  #if !defined(_MSC_VER) || defined(__clang__)
298      #define LOG(...) LOG_IMPL(__VA_ARGS__, "")
299  #else
300      #define LOG(str, ...) LOG_IMPL("%s" str, "", ##__VA_ARGS__, "")
301  #endif
302  
303  // Main TEE macro.
304  //  does the same as LOG
305  //  and
306  //  simultaneously writes stderr.
307  //
308  // Secondary target can be changed just like LOG_TARGET
309  //  by defining LOG_TEE_TARGET
310  //
311  #if !defined(_MSC_VER) || defined(__clang__)
312      #define LOG_TEE(...) LOG_TEE_IMPL(__VA_ARGS__, "")
313  #else
314      #define LOG_TEE(str, ...) LOG_TEE_IMPL("%s" str, "", ##__VA_ARGS__, "")
315  #endif
316  
317  // LOG macro variants with auto endline.
318  #if !defined(_MSC_VER) || defined(__clang__)
319      #define LOGLN(...) LOG_IMPL(__VA_ARGS__, "\n")
320      #define LOG_TEELN(...) LOG_TEE_IMPL(__VA_ARGS__, "\n")
321  #else
322      #define LOGLN(str, ...) LOG_IMPL("%s" str, "", ##__VA_ARGS__, "\n")
323      #define LOG_TEELN(str, ...) LOG_TEE_IMPL("%s" str, "", ##__VA_ARGS__, "\n")
324  #endif
325  
326  // INTERNAL, DO NOT USE
327  inline FILE *log_handler1_impl(bool change = false, LogTriState append = LogTriStateSame, LogTriState disable = LogTriStateSame, const std::string & filename = LOG_DEFAULT_FILE_NAME, FILE *target = nullptr)
328  {
329      static bool _initialized = false;
330      static bool _append = false;
331      static bool _disabled = filename.empty() && target == nullptr;
332      static std::string log_current_filename{filename};
333      static FILE *log_current_target{target};
334      static FILE *logfile = nullptr;
335  
336      if (change)
337      {
338          if (append != LogTriStateSame)
339          {
340              _append = append == LogTriStateTrue;
341              return logfile;
342          }
343  
344          if (disable == LogTriStateTrue)
345          {
346              // Disable primary target
347              _disabled = true;
348          }
349          // If previously disabled, only enable, and keep previous target
350          else if (disable == LogTriStateFalse)
351          {
352              _disabled = false;
353          }
354          // Otherwise, process the arguments
355          else if (log_current_filename != filename || log_current_target != target)
356          {
357              _initialized = false;
358          }
359      }
360  
361      if (_disabled)
362      {
363          // Log is disabled
364          return nullptr;
365      }
366  
367      if (_initialized)
368      {
369          // with fallback in case something went wrong
370          return logfile ? logfile : stderr;
371      }
372  
373      // do the (re)initialization
374      if (target != nullptr)
375      {
376          if (logfile != nullptr && logfile != stdout && logfile != stderr)
377          {
378              fclose(logfile);
379          }
380  
381          log_current_filename = LOG_DEFAULT_FILE_NAME;
382          log_current_target = target;
383  
384          logfile = target;
385      }
386      else
387      {
388          if (log_current_filename != filename)
389          {
390              if (logfile != nullptr && logfile != stdout && logfile != stderr)
391              {
392                  fclose(logfile);
393              }
394          }
395  
396          logfile = fopen(filename.c_str(), _append ? "a" : "w");
397      }
398  
399      if (!logfile)
400      {
401          //  Verify whether the file was opened, otherwise fallback to stderr
402          logfile = stderr;
403  
404          fprintf(stderr, "Failed to open logfile '%s' with error '%s'\n", filename.c_str(), std::strerror(errno));
405          fflush(stderr);
406  
407          // At this point we let the init flag be to true below, and let the target fallback to stderr
408          //  otherwise we would repeatedly fopen() which was already unsuccessful
409      }
410  
411      _initialized = true;
412  
413      return logfile ? logfile : stderr;
414  }
415  
416  // INTERNAL, DO NOT USE
417  inline FILE *log_handler2_impl(bool change = false, LogTriState append = LogTriStateSame, LogTriState disable = LogTriStateSame, FILE *target = nullptr, const std::string & filename = LOG_DEFAULT_FILE_NAME)
418  {
419      return log_handler1_impl(change, append, disable, filename, target);
420  }
421  
422  // Disables logs entirely at runtime.
423  //  Makes LOG() and LOG_TEE() produce no output,
424  //  until enabled back.
425  #define log_disable() log_disable_impl()
426  
427  // INTERNAL, DO NOT USE
428  inline FILE *log_disable_impl()
429  {
430      return log_handler1_impl(true, LogTriStateSame, LogTriStateTrue);
431  }
432  
433  // Enables logs at runtime.
434  #define log_enable() log_enable_impl()
435  
436  // INTERNAL, DO NOT USE
437  inline FILE *log_enable_impl()
438  {
439      return log_handler1_impl(true, LogTriStateSame, LogTriStateFalse);
440  }
441  
442  // Sets target fir logs, either by a file name or FILE* pointer (stdout, stderr, or any valid FILE*)
443  #define log_set_target(target) log_set_target_impl(target)
444  
445  // INTERNAL, DO NOT USE
446  inline FILE *log_set_target_impl(const std::string & filename) { return log_handler1_impl(true, LogTriStateSame, LogTriStateSame, filename); }
447  inline FILE *log_set_target_impl(FILE *target) { return log_handler2_impl(true, LogTriStateSame, LogTriStateSame, target); }
448  
449  // INTERNAL, DO NOT USE
450  inline FILE *log_handler() { return log_handler1_impl(); }
451  
452  // Enable or disable creating separate log files for each run.
453  //  can ONLY be invoked BEFORE first log use.
454  #define log_multilog(enable) log_filename_generator_impl((enable) ? LogTriStateTrue : LogTriStateFalse, "", "")
455  // Enable or disable append mode for log file.
456  //  can ONLY be invoked BEFORE first log use.
457  #define log_append(enable) log_append_impl(enable)
458  // INTERNAL, DO NOT USE
459  inline FILE *log_append_impl(bool enable)
460  {
461      return log_handler1_impl(true, enable ? LogTriStateTrue : LogTriStateFalse, LogTriStateSame);
462  }
463  
464  inline void log_test()
465  {
466      log_disable();
467      LOG("01 Hello World to nobody, because logs are disabled!\n");
468      log_enable();
469      LOG("02 Hello World to default output, which is \"%s\" ( Yaaay, arguments! )!\n", LOG_STRINGIZE(LOG_TARGET));
470      LOG_TEE("03 Hello World to **both** default output and " LOG_TEE_TARGET_STRING "!\n");
471      log_set_target(stderr);
472      LOG("04 Hello World to stderr!\n");
473      LOG_TEE("05 Hello World TEE with double printing to stderr prevented!\n");
474      log_set_target(LOG_DEFAULT_FILE_NAME);
475      LOG("06 Hello World to default log file!\n");
476      log_set_target(stdout);
477      LOG("07 Hello World to stdout!\n");
478      log_set_target(LOG_DEFAULT_FILE_NAME);
479      LOG("08 Hello World to default log file again!\n");
480      log_disable();
481      LOG("09 Hello World _1_ into the void!\n");
482      log_enable();
483      LOG("10 Hello World back from the void ( you should not see _1_ in the log or the output )!\n");
484      log_disable();
485      log_set_target("llama.anotherlog.log");
486      LOG("11 Hello World _2_ to nobody, new target was selected but logs are still disabled!\n");
487      log_enable();
488      LOG("12 Hello World this time in a new file ( you should not see _2_ in the log or the output )?\n");
489      log_set_target("llama.yetanotherlog.log");
490      LOG("13 Hello World this time in yet new file?\n");
491      log_set_target(log_filename_generator("llama_autonamed", "log"));
492      LOG("14 Hello World in log with generated filename!\n");
493  #ifdef _MSC_VER
494      LOG_TEE("15 Hello msvc TEE without arguments\n");
495      LOG_TEE("16 Hello msvc TEE with (%d)(%s) arguments\n", 1, "test");
496      LOG_TEELN("17 Hello msvc TEELN without arguments\n");
497      LOG_TEELN("18 Hello msvc TEELN with (%d)(%s) arguments\n", 1, "test");
498      LOG("19 Hello msvc LOG without arguments\n");
499      LOG("20 Hello msvc LOG with (%d)(%s) arguments\n", 1, "test");
500      LOGLN("21 Hello msvc LOGLN without arguments\n");
501      LOGLN("22 Hello msvc LOGLN with (%d)(%s) arguments\n", 1, "test");
502  #endif
503  }
504  
505  inline bool log_param_single_parse(const std::string & param)
506  {
507      if ( param == "--log-test")
508      {
509          log_test();
510          return true;
511      }
512  
513      if ( param == "--log-disable")
514      {
515          log_disable();
516          return true;
517      }
518  
519      if ( param == "--log-enable")
520      {
521          log_enable();
522          return true;
523      }
524  
525      if (param == "--log-new")
526      {
527          log_multilog(true);
528          return true;
529      }
530  
531      if (param == "--log-append")
532      {
533          log_append(true);
534          return true;
535      }
536  
537      return false;
538  }
539  
540  inline bool log_param_pair_parse(bool check_but_dont_parse, const std::string & param, const std::string & next = std::string())
541  {
542      if ( param == "--log-file")
543      {
544          if (!check_but_dont_parse)
545          {
546              log_set_target(log_filename_generator(next.empty() ? "unnamed" : next, "log"));
547          }
548  
549          return true;
550      }
551  
552      return false;
553  }
554  
555  inline void log_print_usage()
556  {
557      printf("log options:\n");
558      /* format
559      printf("  -h, --help            show this help message and exit\n");*/
560      /* spacing
561      printf("__-param----------------Description\n");*/
562      printf("  --log-test            Run simple logging test\n");
563      printf("  --log-disable         Disable trace logs\n");
564      printf("  --log-enable          Enable trace logs\n");
565      printf("  --log-file            Specify a log filename (without extension)\n");
566      printf("  --log-new             Create a separate new log file on start. "
567                                     "Each log file will have unique name: \"<name>.<ID>.log\"\n");
568      printf("  --log-append          Don't truncate the old log file.\n");
569      printf("\n");
570  }
571  
572  #define log_dump_cmdline(argc, argv) log_dump_cmdline_impl(argc, argv)
573  
574  // INTERNAL, DO NOT USE
575  inline void log_dump_cmdline_impl(int argc, char **argv)
576  {
577      std::stringstream buf;
578      for (int i = 0; i < argc; ++i)
579      {
580          if (std::string(argv[i]).find(' ') != std::string::npos)
581          {
582              buf << " \"" << argv[i] <<"\"";
583          }
584          else
585          {
586              buf << " " << argv[i];
587          }
588      }
589      LOGLN("Cmd:%s", buf.str().c_str());
590  }
591  
592  #define log_tostr(var) log_var_to_string_impl(var).c_str()
593  
594  inline std::string log_var_to_string_impl(bool var)
595  {
596      return var ? "true" : "false";
597  }
598  
599  inline std::string log_var_to_string_impl(std::string var)
600  {
601      return var;
602  }
603  
604  inline std::string log_var_to_string_impl(const std::vector<int> & var)
605  {
606      std::stringstream buf;
607      buf << "[ ";
608      bool first = true;
609      for (auto e : var)
610      {
611          if (first)
612          {
613              first = false;
614          }
615          else
616          {
617              buf << ", ";
618          }
619          buf << std::to_string(e);
620      }
621      buf << " ]";
622  
623      return buf.str();
624  }
625  
626  template <typename C, typename T>
627  inline std::string LOG_TOKENS_TOSTR_PRETTY(const C & ctx, const T & tokens)
628  {
629      std::stringstream buf;
630      buf << "[ ";
631  
632      bool first = true;
633      for (const auto &token : tokens)
634      {
635          if (!first) {
636              buf << ", ";
637          } else {
638              first = false;
639          }
640  
641          auto detokenized = llama_token_to_piece(ctx, token);
642  
643          detokenized.erase(
644              std::remove_if(
645                  detokenized.begin(),
646                  detokenized.end(),
647                  [](const unsigned char c) { return !std::isprint(c); }),
648              detokenized.end());
649  
650          buf
651              << "'" << detokenized << "'"
652              << ":" << std::to_string(token);
653      }
654      buf << " ]";
655  
656      return buf.str();
657  }
658  
659  template <typename C, typename B>
660  inline std::string LOG_BATCH_TOSTR_PRETTY(const C & ctx, const B & batch)
661  {
662      std::stringstream buf;
663      buf << "[ ";
664  
665      bool first = true;
666      for (int i = 0; i < batch.n_tokens; ++i)
667      {
668          if (!first) {
669              buf << ", ";
670          } else {
671              first = false;
672          }
673  
674          auto detokenized = llama_token_to_piece(ctx, batch.token[i]);
675  
676          detokenized.erase(
677              std::remove_if(
678                  detokenized.begin(),
679                  detokenized.end(),
680                  [](const unsigned char c) { return !std::isprint(c); }),
681              detokenized.end());
682  
683          buf
684              << "\n" << std::to_string(i)
685              << ":token '" << detokenized << "'"
686              << ":pos " << std::to_string(batch.pos[i])
687              << ":n_seq_id  " << std::to_string(batch.n_seq_id[i])
688              << ":seq_id " << std::to_string(batch.seq_id[i][0])
689              << ":logits " << std::to_string(batch.logits[i]);
690      }
691      buf << " ]";
692  
693      return buf.str();
694  }
695  
696  #ifdef LOG_DISABLE_LOGS
697  
698  #undef LOG
699  #define LOG(...) // dummy stub
700  #undef LOGLN
701  #define LOGLN(...) // dummy stub
702  
703  #undef LOG_TEE
704  #define LOG_TEE(...) fprintf(stderr, __VA_ARGS__) // convert to normal fprintf
705  
706  #undef LOG_TEELN
707  #define LOG_TEELN(...) fprintf(stderr, __VA_ARGS__) // convert to normal fprintf
708  
709  #undef LOG_DISABLE
710  #define LOG_DISABLE() // dummy stub
711  
712  #undef LOG_ENABLE
713  #define LOG_ENABLE() // dummy stub
714  
715  #undef LOG_ENABLE
716  #define LOG_ENABLE() // dummy stub
717  
718  #undef LOG_SET_TARGET
719  #define LOG_SET_TARGET(...) // dummy stub
720  
721  #undef LOG_DUMP_CMDLINE
722  #define LOG_DUMP_CMDLINE(...) // dummy stub
723  
724  #endif // LOG_DISABLE_LOGS