HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
logging.h
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License.
3 
4 #pragma once
5 
6 #include <atomic>
7 #include <chrono>
8 #include <climits>
9 #include <map>
10 #include <memory>
11 #include <mutex>
12 #include <string>
13 
14 #include "core/common/common.h"
21 #include "date/date.h"
22 
23 /*
24 
25  Logging overview and expected usage:
26 
27  At program startup:
28  * Create one or more ISink instances. If multiple, combine using composite_sink.
29  * Create a LoggingManager instance with the sink/s with is_default_instance set to true
30  * Only one instance should be created in this way, and it should remain valid for
31  until the program no longer needs to produce log output.
32 
33  You can either use the static default Logger which LoggingManager will create when constructed
34  via LoggingManager::DefaultLogger(), or separate Logger instances each with different log ids
35  via LoggingManager::CreateLogger.
36 
37  The log id is passed to the ISink instance with the sink determining how the log id is used
38  in the output.
39 
40  LoggingManager
41  * creates the Logger instances used by the application
42  * provides a static default logger instance
43  * owns the log sink instance
44  * applies checks on severity and output of user data
45 
46  The log macros create a Capture instance to capture the information to log.
47  If the severity and/or user filtering settings would prevent logging, no evaluation
48  of the log arguments will occur, so no performance cost beyond the severity and user
49  filtering check.
50 
51  A sink can do further filter as needed.
52 
53 */
54 
55 namespace onnxruntime {
56 
57 namespace logging {
58 
59 using Timestamp = std::chrono::time_point<std::chrono::system_clock>;
60 
61 // C++20 has operator<< in std::chrono for Timestamp type but mac builds need additional checks
62 // to ensure usage is valid.
63 // TODO: As we enable C++20 on other platforms we may need similar checks.
64 // define a temporary value to determine whether to use the std::chrono or date implementation.
65 #define ORT_USE_CXX20_STD_CHRONO __cplusplus >= 202002L
66 
67 // Apply constraints for mac builds
68 #if __APPLE__
69 #include <TargetConditionals.h>
70 
71 // Catalyst check must be first as it has both TARGET_OS_MACCATALYST and TARGET_OS_MAC set
72 #if TARGET_OS_MACCATALYST
73 // maccatalyst requires version 16.3
74 #if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 160300)
75 #undef ORT_USE_CXX20_STD_CHRONO
76 #endif
77 
78 #elif TARGET_OS_MAC
79 // Xcode added support for C++20's std::chrono::operator<< in SDK version 14.4,
80 // but the target macOS version must also be >= 13.3 for it to be used.
81 #if (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED < 140400) || \
82  (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 130300)
83 #undef ORT_USE_CXX20_STD_CHRONO
84 #endif
85 
86 #endif
87 #endif // __APPLE__
88 
89 #if ORT_USE_CXX20_STD_CHRONO
90 namespace timestamp_ns = std::chrono;
91 #else
92 namespace timestamp_ns = ::date;
93 #endif
94 
95 #undef ORT_USE_CXX20_STD_CHRONO
96 
97 #ifndef NDEBUG
98 ORT_ATTRIBUTE_UNUSED static bool vlog_enabled = true; // Set directly based on your needs.
99 #else
100 constexpr bool vlog_enabled = false; // no VLOG output
101 #endif
102 
103 enum class DataType {
104  SYSTEM = 0, ///< System data.
105  USER = 1 ///< Contains potentially sensitive user data.
106 };
107 
108 // Internal log categories.
109 // Logging interface takes const char* so arbitrary values can also be used.
110 struct Category {
111  static const char* onnxruntime; ///< General output
112  static const char* System; ///< Log output regarding interactions with the host system
113  // TODO: What other high level categories are meaningful? Model? Optimizer? Execution?
114 };
115 
116 /// <summary>
117 /// ORT TraceLogging keywords for categories of dynamic logging enablement
118 /// </summary>
119 enum class ORTTraceLoggingKeyword : uint64_t {
120  Session = 0x1, // ORT Session TraceLoggingWrite
121  Logs = 0x2, // LOGS() Macro ORT logs. Pair with an appropriate level depending on detail required
122  Reserved1 = 0x4, // Reserved if we want to add some specific sub-categories instead of just LOGS() or other uses
123  Reserved2 = 0x8,
124  Reserved3 = 0x10,
125  Reserved4 = 0x20,
126  Reserved5 = 0x40,
127  Reserved6 = 0x80,
128  Profiling = 0x100 // Enables profiling. At higher levels >5 can impact inference performance
129 };
130 
131 class ISink;
132 class Logger;
133 class Capture;
134 
135 /// <summary>
136 /// The logging manager.
137 /// Owns the log sink and potentially provides a default Logger instance.
138 /// Provides filtering based on a minimum LogSeverity level, and of messages with DataType::User if enabled.
139 /// </summary>
140 class LoggingManager final {
141  public:
143  Default, ///< Default instance of LoggingManager that should exist for the lifetime of the program
144  Temporal ///< Temporal instance. CreateLogger(...) should be used, however DefaultLogger() will NOT be provided via this instance.
145  };
146 
147  /**
148  Initializes a new instance of the LoggingManager class.
149  @param sink The sink to write to. Use CompositeSink if you need to write to multiple places.
150  @param default_min_severity The default minimum severity. Messages with lower severity will be ignored unless
151  overridden in CreateLogger.
152  @param default_filter_user_data If set to true ignore messages with DataType::USER unless overridden in CreateLogger.
153  @param instance_type If InstanceType::Default, this is the default instance of the LoggingManager
154  and is expected to exist for the lifetime of the program.
155  It creates and owns the default logger that calls to the static DefaultLogger method return.
156  @param default_logger_id Logger Id to use for the default logger. nullptr/ignored if instance_type == Temporal.
157  @param default_max_vlog_level Default maximum level for VLOG messages to be created unless overridden in CreateLogger.
158  Requires a severity of kVERBOSE for VLOG messages to be logged.
159  */
160  LoggingManager(std::unique_ptr<ISink> sink, Severity default_min_severity, bool default_filter_user_data,
161  InstanceType instance_type,
162  const std::string* default_logger_id = nullptr,
163  int default_max_vlog_level = -1);
164 
165  /**
166  Creates a new logger instance which will use the provided logger_id and default severity and vlog levels.
167  @param logger_id The log identifier.
168  @returns A new Logger instance that the caller owns.
169  */
170  std::unique_ptr<Logger> CreateLogger(const std::string& logger_id);
171 
172  /**
173  Creates a new logger instance which will use the provided logger_id, severity and vlog levels.
174  @param logger_id The log identifier.
175  @param min_severity The minimum severity. Requests to create messages with lower severity will be ignored.
176  @param filter_user_data If set to true ignore messages with DataType::USER.
177  @param max_vlog_level Maximum level for VLOG messages to be created.
178  @returns A new Logger instance that the caller owns.
179  */
180  std::unique_ptr<Logger> CreateLogger(const std::string& logger_id,
181  Severity min_severity, bool filter_user_data, int max_vlog_level = -1);
182 
183  /**
184  Gets the default logger instance if set. Throws if no default logger is currently registered.
185  @remarks
186  Creating a LoggingManager instance with is_default_instance == true registers a default logger.
187  Note that the default logger is only valid until the LoggerManager that registered it is destroyed.
188  @returns The default logger if available.
189  */
190  static const Logger& DefaultLogger();
191 
192  /**
193  Return a boolean indicating if the default logger has been initialized
194  */
195  static bool HasDefaultLogger() { return nullptr != s_default_logger_; }
196 
197  /**
198  Gets the default instance of the LoggingManager.
199  */
201 
202  /**
203  Removes a Sink if one is present
204  */
205  void RemoveSink(SinkType sinkType);
206 
207  /**
208  Adds a Sink to the current sink creating a CompositeSink if necessary
209  Sinks types must be unique
210  @param severity The severity level for the new Sink
211  */
212  bool AddSinkOfType(SinkType sinkType, std::function<std::unique_ptr<ISink>()> sinkFactory, logging::Severity severity);
213 
214  /**
215  Change the minimum severity level for log messages to be output by the default logger.
216  @param severity The severity.
217  */
219 
220  /**
221  Change the maximum verbosity level for log messages to be output by the default logger.
222  @remarks
223  To activate the verbose log, the logger severity must also be set to kVERBOSE.
224  @param vlog_level The verbosity level.
225  */
226  static void SetDefaultLoggerVerbosity(int vlog_level);
227 
228  /**
229  Logs a FATAL level message and creates an exception that can be thrown with error information.
230  @param category The log category.
231  @param location The location the log message was generated.
232  @param format_str The printf format string.
233  @param ... The printf arguments.
234  @returns A new Logger instance that the caller owns.
235  */
236  static std::exception LogFatalAndCreateException(const char* category,
237  const CodeLocation& location,
238  const char* format_str, ...);
239 
240  /**
241  Logs the message using the provided logger id.
242  @param logger_id The log identifier.
243  @param message The log message.
244  */
245  void Log(const std::string& logger_id, const Capture& message) const;
246 
247  /**
248  Sends a Profiling Event Record to the sink.
249  @param Profiling Event Record
250  */
251  void SendProfileEvent(profiling::EventRecord& eventRecord) const;
252  ~LoggingManager();
253 
254  private:
255  ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(LoggingManager);
256 
257  Timestamp GetTimestamp() const noexcept;
258  void CreateDefaultLogger(const std::string& logger_id);
259 
260  std::unique_ptr<ISink> sink_;
261 #ifdef _WIN32
262  mutable OrtMutex sink_mutex_;
263 #endif
264  Severity default_min_severity_;
265  const bool default_filter_user_data_;
266  const int default_max_vlog_level_;
267  bool owns_default_logger_;
268 
269  static Logger* s_default_logger_;
270 
271  struct Epochs {
272  const std::chrono::time_point<std::chrono::high_resolution_clock> high_res;
273  const std::chrono::time_point<std::chrono::system_clock> system;
274  const std::chrono::minutes localtime_offset_from_utc;
275  };
276 
277  static const Epochs& GetEpochs() noexcept;
278 };
279 
280 /**
281  Logger provides a per-instance log id. Everything else is passed back up to the LoggingManager
282 */
284  public:
285  /**
286  Initializes a new instance of the Logger class.
287  @param loggingManager The logging manager.
288  @param id The identifier for messages coming from this Logger.
289  @param severity Minimum severity for messages to be created and logged.
290  @param filter_user_data Should USER data be filtered from output.
291  @param vlog_level Minimum level for VLOG messages to be created. Note that a severity of kVERBOSE must be provided
292  for VLOG messages to be logged.
293  */
294  Logger(const LoggingManager& loggingManager, std::string id,
295  Severity severity, bool filter_user_data, int vlog_level)
296  : logging_manager_{&loggingManager},
297  id_{id},
298  min_severity_{severity},
299  filter_user_data_{filter_user_data},
300  max_vlog_level_{vlog_level} {
301  }
302 
303  /**
304  Get the minimum severity level for log messages to be output.
305  @returns The severity.
306  */
307  Severity GetSeverity() const noexcept { return min_severity_; }
308 
309  /**
310  Change the minimum severity level for log messages to be output.
311  @param severity The severity.
312  */
313  void SetSeverity(Severity severity) noexcept { min_severity_ = severity; }
314 
315  /**
316  Change the maximum verbosity level for log messages to be output.
317  @remarks
318  To activate the verbose log, the logger severity must also be set to kVERBOSE.
319  @param vlog_level The verbosity.
320  */
321  void SetVerbosity(int vlog_level) noexcept { max_vlog_level_ = vlog_level; }
322 
323  /**
324  Check if output is enabled for the provided LogSeverity and DataType values.
325  @param severity The severity.
326  @param data_type Type of the data.
327  @returns True if a message with these values will be logged.
328  */
329  bool OutputIsEnabled(Severity severity, DataType data_type) const noexcept {
330  return (severity >= min_severity_ && (data_type != DataType::USER || !filter_user_data_));
331  }
332 
333  /**
334  Return the maximum VLOG level allowed. Disabled unless logging VLOG messages
335  */
336  int VLOGMaxLevel() const noexcept {
337  return min_severity_ > Severity::kVERBOSE ? -1 : max_vlog_level_;
338  }
339 
340  /**
341  Logs the captured message.
342  @param message The log message.
343  */
344  void Log(const Capture& message) const {
345  logging_manager_->Log(id_, message);
346  }
347 
348  /**
349  Sends a Profiling Event Record to the sink.
350  @param Profiling Event Record
351  */
352  void SendProfileEvent(profiling::EventRecord& eventRecord) const {
353  logging_manager_->SendProfileEvent(eventRecord);
354  }
355 
356  private:
357  const LoggingManager* logging_manager_;
358  const std::string id_;
359  Severity min_severity_;
360  const bool filter_user_data_;
361  int max_vlog_level_;
362 };
363 
365  if (s_default_logger_ == nullptr) {
366  // fail early for attempted misuse. don't use logging macros as we have no logger.
367  ORT_THROW("Attempt to use DefaultLogger but none has been registered.");
368  }
369 
370  return *s_default_logger_;
371 }
372 
374  if (s_default_logger_ == nullptr) {
375  // fail early for attempted misuse. don't use logging macros as we have no logger.
376  ORT_THROW("Attempt to use DefaultLogger but none has been registered.");
377  }
378 
379  s_default_logger_->SetSeverity(severity);
380 }
381 
382 inline void LoggingManager::SetDefaultLoggerVerbosity(int vlog_level) {
383  if (s_default_logger_ == nullptr) {
384  // fail early for attempted misuse. don't use logging macros as we have no logger.
385  ORT_THROW("Attempt to use DefaultLogger but none has been registered.");
386  }
387 
388  s_default_logger_->SetVerbosity(vlog_level);
389 }
390 
391 inline Timestamp LoggingManager::GetTimestamp() const noexcept {
392  static const Epochs& epochs = GetEpochs();
393 
394  const auto high_res_now = std::chrono::high_resolution_clock::now();
395  return std::chrono::time_point_cast<std::chrono::system_clock::duration>(
396  epochs.system + (high_res_now - epochs.high_res) + epochs.localtime_offset_from_utc);
397 }
398 
399 /**
400  Return the current thread id.
401 */
402 unsigned int GetThreadId();
403 
404 /**
405  Return the current process id.
406 */
407 unsigned int GetProcessId();
408 
409 /**
410  If the ONNXRuntimeTraceLoggingProvider ETW Provider is enabled, then adds to the existing logger.
411 */
412 std::unique_ptr<ISink> EnhanceSinkWithEtw(std::unique_ptr<ISink> existingSink, logging::Severity originalSeverity,
413  logging::Severity etwSeverity);
414 
415 /**
416  If the ONNXRuntimeTraceLoggingProvider ETW Provider is enabled, then can override the logging level.
417  But this overrided level only applies to the ETW sink. The original logger(s) retain their original logging level
418 */
419 Severity OverrideLevelWithEtw(Severity originalSeverity);
420 
421 } // namespace logging
422 } // namespace onnxruntime
Logger(const LoggingManager &loggingManager, std::string id, Severity severity, bool filter_user_data, int vlog_level)
Definition: logging.h:294
GLuint GLsizei const GLchar * message
Definition: glcorearb.h:2543
void Log(const std::string &logger_id, const Capture &message) const
*get result *(waiting if necessary)*A common idiom is to fire a bunch of sub tasks at the and then *wait for them to all complete We provide a helper class
Definition: thread.h:632
std::unique_ptr< Logger > CreateLogger(const std::string &logger_id)
void SendProfileEvent(profiling::EventRecord &eventRecord) const
Definition: logging.h:352
LoggingManager(std::unique_ptr< ISink > sink, Severity default_min_severity, bool default_filter_user_data, InstanceType instance_type, const std::string *default_logger_id=nullptr, int default_max_vlog_level=-1)
void SendProfileEvent(profiling::EventRecord &eventRecord) const
static void SetDefaultLoggerSeverity(Severity severity)
Definition: logging.h:373
Contains potentially sensitive user data.
unsigned int GetThreadId()
Temporal instance. CreateLogger(...) should be used, however DefaultLogger() will NOT be provided via...
Definition: logging.h:144
ORTTraceLoggingKeyword
ORT TraceLogging keywords for categories of dynamic logging enablement
Definition: logging.h:119
unsigned int GetProcessId()
Severity OverrideLevelWithEtw(Severity originalSeverity)
static const Logger & DefaultLogger()
Definition: logging.h:364
static const char * onnxruntime
General output.
Definition: logging.h:111
The logging manager. Owns the log sink and potentially provides a default Logger instance. Provides filtering based on a minimum LogSeverity level, and of messages with DataType::User if enabled.
Definition: logging.h:140
std::chrono::time_point< std::chrono::system_clock > Timestamp
Definition: logging.h:59
void SetVerbosity(int vlog_level) noexcept
Definition: logging.h:321
std::unique_ptr< ISink > EnhanceSinkWithEtw(std::unique_ptr< ISink > existingSink, logging::Severity originalSeverity, logging::Severity etwSeverity)
#define ORT_ATTRIBUTE_UNUSED
Definition: common.h:66
GLint location
Definition: glcorearb.h:805
GLenum GLenum severity
Definition: glcorearb.h:2539
static void SetDefaultLoggerVerbosity(int vlog_level)
Definition: logging.h:382
Severity GetSeverity() const noexcept
Definition: logging.h:307
#define ORT_THROW(...)
Definition: common.h:162
void SetSeverity(Severity severity) noexcept
Definition: logging.h:313
bool OutputIsEnabled(Severity severity, DataType data_type) const noexcept
Definition: logging.h:329
static LoggingManager * GetDefaultInstance()
void Log(const Capture &message) const
Definition: logging.h:344
bool AddSinkOfType(SinkType sinkType, std::function< std::unique_ptr< ISink >()> sinkFactory, logging::Severity severity)
static const char * System
Log output regarding interactions with the host system.
Definition: logging.h:112
int VLOGMaxLevel() const noexcept
Definition: logging.h:336
void RemoveSink(SinkType sinkType)
static std::exception LogFatalAndCreateException(const char *category, const CodeLocation &location, const char *format_str,...)
Default instance of LoggingManager that should exist for the lifetime of the program.
Definition: logging.h:143