• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "logging.h"
2 
3 #include <chrono>
4 #include <cinttypes>
5 #include <cstdarg>
6 #include <cstring>
7 #include <sstream>
8 #include <thread>
9 
10 #ifdef _WIN32
11 #include <windows.h>
12 #endif
13 
14 #ifdef __linux__
15 #include <sys/syscall.h>
16 #include <sys/types.h>
17 #include <unistd.h>
18 #endif
19 
20 namespace {
21 
22 constexpr int kMaxThreadIdLength = 7;  // 7 digits for the thread id is what Google uses everywhere.
23 
24 // Returns the current thread id as a string of at most kMaxThreadIdLength characters.
25 // We try to avoid using std::this_thread::get_id() because on Linux at least it returns a long
26 // number (e.g. 139853607339840) which isn't the same as the thread id from the OS itself.
27 // The current logic is inspired by:
28 // https://github.com/abseil/abseil-cpp/blob/52d41a9ec23e39db7e2cbce5c9449506cf2d3a5c/absl/base/internal/sysinfo.cc#L367-L381
getThreadID()29 std::string getThreadID() {
30     std::ostringstream ss;
31 #if defined(_WIN32)
32     ss << GetCurrentThreadId();
33 #elif defined(__linux__)
34     ss << syscall(__NR_gettid);
35 #else
36     ss << std::this_thread::get_id();
37 #endif
38     std::string result = ss.str();
39     // Truncate on the left if necessary
40     return result.length() > kMaxThreadIdLength
41                ? result.substr(result.length() - kMaxThreadIdLength)
42                : result;
43 }
44 
45 // Caches the thread id in thread local storage to increase performance
46 // Inspired by: https://github.com/abseil/abseil-cpp/blob/52d41a9ec23e39db7e2cbce5c9449506cf2d3a5c/absl/base/internal/sysinfo.cc#L494-L504
getCachedThreadID()47 const char* getCachedThreadID() {
48     static thread_local std::string thread_id = getThreadID();
49     return thread_id.c_str();
50 }
51 
52 // Borrowed from https://cs.android.com/android/platform/superproject/+/master:system/libbase/logging.cpp;l=84-98;drc=18c2bd4f3607cb300bb96e543df91dfdda6a9655
53 // Note: we use this over std::filesystem::path to keep it as fast as possible.
GetFileBasename(const char * file)54 const char* GetFileBasename(const char* file) {
55 #if defined(_WIN32)
56     const char* last_backslash = strrchr(file, '\\');
57     if (last_backslash != nullptr) {
58         return last_backslash + 1;
59     }
60 #endif
61     const char* last_slash = strrchr(file, '/');
62     if (last_slash != nullptr) {
63         return last_slash + 1;
64     }
65     return file;
66 }
67 
68 }  // namespace
69 
OutputLog(FILE * stream,char severity,const char * file,unsigned int line,int64_t timestamp_us,const char * format,...)70 void OutputLog(FILE* stream, char severity, const char* file, unsigned int line,
71                int64_t timestamp_us, const char* format, ...) {
72     if (timestamp_us == 0) {
73         timestamp_us = std::chrono::duration_cast<std::chrono::microseconds>(
74                            std::chrono::system_clock::now().time_since_epoch())
75                            .count();
76     }
77     std::time_t timestamp_s = timestamp_us / 1000000;
78 
79     // Break down the timestamp into the individual time parts
80     std::tm ts_parts = {};
81 #if defined(_WIN32)
82     localtime_s(&ts_parts, &timestamp_s);
83 #else
84     localtime_r(&timestamp_s, &ts_parts);
85 #endif
86 
87     // Get the microseconds part of the timestamp since it's not available in the tm struct
88     int64_t microseconds = timestamp_us % 1000000;
89 
90     // Output the standard Google logging prefix
91     // See also: https://github.com/google/glog/blob/9dc1107f88d3a1613d61b80040d83c1c1acbac3d/src/logging.cc#L1612-L1615
92     fprintf(stream, "%c%02d%02d %02d:%02d:%02d.%06" PRId64 " %7s %s:%d] ", severity,
93             ts_parts.tm_mon + 1, ts_parts.tm_mday, ts_parts.tm_hour, ts_parts.tm_min,
94             ts_parts.tm_sec, microseconds, getCachedThreadID(), GetFileBasename(file), line);
95 
96     // Output the actual log message and newline
97     va_list args;
98     va_start(args, format);
99     vfprintf(stream, format, args);
100     va_end(args);
101     fprintf(stream, "\n");
102 }
103