logging.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /*
  2. * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #ifndef TENSORRT_LOGGING_H
  17. #define TENSORRT_LOGGING_H
  18. #include "NvInferRuntimeCommon.h"
  19. #include <cassert>
  20. #include <ctime>
  21. #include <iomanip>
  22. #include <iostream>
  23. #include <ostream>
  24. #include <sstream>
  25. #include <string>
  26. using Severity = nvinfer1::ILogger::Severity;
  27. class LogStreamConsumerBuffer : public std::stringbuf
  28. {
  29. public:
  30. LogStreamConsumerBuffer(std::ostream& stream, const std::string& prefix, bool shouldLog)
  31. : mOutput(stream)
  32. , mPrefix(prefix)
  33. , mShouldLog(shouldLog)
  34. {
  35. }
  36. LogStreamConsumerBuffer(LogStreamConsumerBuffer&& other)
  37. : mOutput(other.mOutput)
  38. {
  39. }
  40. ~LogStreamConsumerBuffer()
  41. {
  42. // std::streambuf::pbase() gives a pointer to the beginning of the buffered part of the output sequence
  43. // std::streambuf::pptr() gives a pointer to the current position of the output sequence
  44. // if the pointer to the beginning is not equal to the pointer to the current position,
  45. // call putOutput() to log the output to the stream
  46. if (pbase() != pptr())
  47. {
  48. putOutput();
  49. }
  50. }
  51. // synchronizes the stream buffer and returns 0 on success
  52. // synchronizing the stream buffer consists of inserting the buffer contents into the stream,
  53. // resetting the buffer and flushing the stream
  54. virtual int sync()
  55. {
  56. putOutput();
  57. return 0;
  58. }
  59. void putOutput()
  60. {
  61. if (mShouldLog)
  62. {
  63. // prepend timestamp
  64. std::time_t timestamp = std::time(nullptr);
  65. tm* tm_local = std::localtime(&timestamp);
  66. std::cout << "[";
  67. std::cout << std::setw(2) << std::setfill('0') << 1 + tm_local->tm_mon << "/";
  68. std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_mday << "/";
  69. std::cout << std::setw(4) << std::setfill('0') << 1900 + tm_local->tm_year << "-";
  70. std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_hour << ":";
  71. std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_min << ":";
  72. std::cout << std::setw(2) << std::setfill('0') << tm_local->tm_sec << "] ";
  73. // std::stringbuf::str() gets the string contents of the buffer
  74. // insert the buffer contents pre-appended by the appropriate prefix into the stream
  75. mOutput << mPrefix << str();
  76. // set the buffer to empty
  77. str("");
  78. // flush the stream
  79. mOutput.flush();
  80. }
  81. }
  82. void setShouldLog(bool shouldLog)
  83. {
  84. mShouldLog = shouldLog;
  85. }
  86. private:
  87. std::ostream& mOutput;
  88. std::string mPrefix;
  89. bool mShouldLog;
  90. };
  91. //!
  92. //! \class LogStreamConsumerBase
  93. //! \brief Convenience object used to initialize LogStreamConsumerBuffer before std::ostream in LogStreamConsumer
  94. //!
  95. class LogStreamConsumerBase
  96. {
  97. public:
  98. LogStreamConsumerBase(std::ostream& stream, const std::string& prefix, bool shouldLog)
  99. : mBuffer(stream, prefix, shouldLog)
  100. {
  101. }
  102. protected:
  103. LogStreamConsumerBuffer mBuffer;
  104. };
  105. //!
  106. //! \class LogStreamConsumer
  107. //! \brief Convenience object used to facilitate use of C++ stream syntax when logging messages.
  108. //! Order of base classes is LogStreamConsumerBase and then std::ostream.
  109. //! This is because the LogStreamConsumerBase class is used to initialize the LogStreamConsumerBuffer member field
  110. //! in LogStreamConsumer and then the address of the buffer is passed to std::ostream.
  111. //! This is necessary to prevent the address of an uninitialized buffer from being passed to std::ostream.
  112. //! Please do not change the order of the parent classes.
  113. //!
  114. class LogStreamConsumer : protected LogStreamConsumerBase, public std::ostream
  115. {
  116. public:
  117. //! \brief Creates a LogStreamConsumer which logs messages with level severity.
  118. //! Reportable severity determines if the messages are severe enough to be logged.
  119. LogStreamConsumer(Severity reportableSeverity, Severity severity)
  120. : LogStreamConsumerBase(severityOstream(severity), severityPrefix(severity), severity <= reportableSeverity)
  121. , std::ostream(&mBuffer) // links the stream buffer with the stream
  122. , mShouldLog(severity <= reportableSeverity)
  123. , mSeverity(severity)
  124. {
  125. }
  126. LogStreamConsumer(LogStreamConsumer&& other)
  127. : LogStreamConsumerBase(severityOstream(other.mSeverity), severityPrefix(other.mSeverity), other.mShouldLog)
  128. , std::ostream(&mBuffer) // links the stream buffer with the stream
  129. , mShouldLog(other.mShouldLog)
  130. , mSeverity(other.mSeverity)
  131. {
  132. }
  133. void setReportableSeverity(Severity reportableSeverity)
  134. {
  135. mShouldLog = mSeverity <= reportableSeverity;
  136. mBuffer.setShouldLog(mShouldLog);
  137. }
  138. private:
  139. static std::ostream& severityOstream(Severity severity)
  140. {
  141. return severity >= Severity::kINFO ? std::cout : std::cerr;
  142. }
  143. static std::string severityPrefix(Severity severity)
  144. {
  145. switch (severity)
  146. {
  147. case Severity::kINTERNAL_ERROR: return "[F] ";
  148. case Severity::kERROR: return "[E] ";
  149. case Severity::kWARNING: return "[W] ";
  150. case Severity::kINFO: return "[I] ";
  151. case Severity::kVERBOSE: return "[V] ";
  152. default: assert(0); return "";
  153. }
  154. }
  155. bool mShouldLog;
  156. Severity mSeverity;
  157. };
  158. //! \class Logger
  159. //!
  160. //! \brief Class which manages logging of TensorRT tools and samples
  161. //!
  162. //! \details This class provides a common interface for TensorRT tools and samples to log information to the console,
  163. //! and supports logging two types of messages:
  164. //!
  165. //! - Debugging messages with an associated severity (info, warning, error, or internal error/fatal)
  166. //! - Test pass/fail messages
  167. //!
  168. //! The advantage of having all samples use this class for logging as opposed to emitting directly to stdout/stderr is
  169. //! that the logic for controlling the verbosity and formatting of sample output is centralized in one location.
  170. //!
  171. //! In the future, this class could be extended to support dumping test results to a file in some standard format
  172. //! (for example, JUnit XML), and providing additional metadata (e.g. timing the duration of a test run).
  173. //!
  174. //! TODO: For backwards compatibility with existing samples, this class inherits directly from the nvinfer1::ILogger
  175. //! interface, which is problematic since there isn't a clean separation between messages coming from the TensorRT
  176. //! library and messages coming from the sample.
  177. //!
  178. //! In the future (once all samples are updated to use Logger::getTRTLogger() to access the ILogger) we can refactor the
  179. //! class to eliminate the inheritance and instead make the nvinfer1::ILogger implementation a member of the Logger
  180. //! object.
  181. class Logger : public nvinfer1::ILogger
  182. {
  183. public:
  184. Logger(Severity severity = Severity::kWARNING)
  185. : mReportableSeverity(severity)
  186. {
  187. }
  188. //!
  189. //! \enum TestResult
  190. //! \brief Represents the state of a given test
  191. //!
  192. enum class TestResult
  193. {
  194. kRUNNING, //!< The test is running
  195. kPASSED, //!< The test passed
  196. kFAILED, //!< The test failed
  197. kWAIVED //!< The test was waived
  198. };
  199. //!
  200. //! \brief Forward-compatible method for retrieving the nvinfer::ILogger associated with this Logger
  201. //! \return The nvinfer1::ILogger associated with this Logger
  202. //!
  203. //! TODO Once all samples are updated to use this method to register the logger with TensorRT,
  204. //! we can eliminate the inheritance of Logger from ILogger
  205. //!
  206. nvinfer1::ILogger& getTRTLogger()
  207. {
  208. return *this;
  209. }
  210. //!
  211. //! \brief Implementation of the nvinfer1::ILogger::log() virtual method
  212. //!
  213. //! Note samples should not be calling this function directly; it will eventually go away once we eliminate the
  214. //! inheritance from nvinfer1::ILogger
  215. //!
  216. void log(Severity severity, const char* msg) noexcept override
  217. {
  218. LogStreamConsumer(mReportableSeverity, severity) << "[TRT] " << std::string(msg) << std::endl;
  219. }
  220. //!
  221. //! \brief Method for controlling the verbosity of logging output
  222. //!
  223. //! \param severity The logger will only emit messages that have severity of this level or higher.
  224. //!
  225. void setReportableSeverity(Severity severity)
  226. {
  227. mReportableSeverity = severity;
  228. }
  229. //!
  230. //! \brief Opaque handle that holds logging information for a particular test
  231. //!
  232. //! This object is an opaque handle to information used by the Logger to print test results.
  233. //! The sample must call Logger::defineTest() in order to obtain a TestAtom that can be used
  234. //! with Logger::reportTest{Start,End}().
  235. //!
  236. class TestAtom
  237. {
  238. public:
  239. TestAtom(TestAtom&&) = default;
  240. private:
  241. friend class Logger;
  242. TestAtom(bool started, const std::string& name, const std::string& cmdline)
  243. : mStarted(started)
  244. , mName(name)
  245. , mCmdline(cmdline)
  246. {
  247. }
  248. bool mStarted;
  249. std::string mName;
  250. std::string mCmdline;
  251. };
  252. //!
  253. //! \brief Define a test for logging
  254. //!
  255. //! \param[in] name The name of the test. This should be a string starting with
  256. //! "TensorRT" and containing dot-separated strings containing
  257. //! the characters [A-Za-z0-9_].
  258. //! For example, "TensorRT.sample_googlenet"
  259. //! \param[in] cmdline The command line used to reproduce the test
  260. //
  261. //! \return a TestAtom that can be used in Logger::reportTest{Start,End}().
  262. //!
  263. static TestAtom defineTest(const std::string& name, const std::string& cmdline)
  264. {
  265. return TestAtom(false, name, cmdline);
  266. }
  267. //!
  268. //! \brief A convenience overloaded version of defineTest() that accepts an array of command-line arguments
  269. //! as input
  270. //!
  271. //! \param[in] name The name of the test
  272. //! \param[in] argc The number of command-line arguments
  273. //! \param[in] argv The array of command-line arguments (given as C strings)
  274. //!
  275. //! \return a TestAtom that can be used in Logger::reportTest{Start,End}().
  276. static TestAtom defineTest(const std::string& name, int argc, char const* const* argv)
  277. {
  278. auto cmdline = genCmdlineString(argc, argv);
  279. return defineTest(name, cmdline);
  280. }
  281. //!
  282. //! \brief Report that a test has started.
  283. //!
  284. //! \pre reportTestStart() has not been called yet for the given testAtom
  285. //!
  286. //! \param[in] testAtom The handle to the test that has started
  287. //!
  288. static void reportTestStart(TestAtom& testAtom)
  289. {
  290. reportTestResult(testAtom, TestResult::kRUNNING);
  291. assert(!testAtom.mStarted);
  292. testAtom.mStarted = true;
  293. }
  294. //!
  295. //! \brief Report that a test has ended.
  296. //!
  297. //! \pre reportTestStart() has been called for the given testAtom
  298. //!
  299. //! \param[in] testAtom The handle to the test that has ended
  300. //! \param[in] result The result of the test. Should be one of TestResult::kPASSED,
  301. //! TestResult::kFAILED, TestResult::kWAIVED
  302. //!
  303. static void reportTestEnd(const TestAtom& testAtom, TestResult result)
  304. {
  305. assert(result != TestResult::kRUNNING);
  306. assert(testAtom.mStarted);
  307. reportTestResult(testAtom, result);
  308. }
  309. static int reportPass(const TestAtom& testAtom)
  310. {
  311. reportTestEnd(testAtom, TestResult::kPASSED);
  312. return EXIT_SUCCESS;
  313. }
  314. static int reportFail(const TestAtom& testAtom)
  315. {
  316. reportTestEnd(testAtom, TestResult::kFAILED);
  317. return EXIT_FAILURE;
  318. }
  319. static int reportWaive(const TestAtom& testAtom)
  320. {
  321. reportTestEnd(testAtom, TestResult::kWAIVED);
  322. return EXIT_SUCCESS;
  323. }
  324. static int reportTest(const TestAtom& testAtom, bool pass)
  325. {
  326. return pass ? reportPass(testAtom) : reportFail(testAtom);
  327. }
  328. Severity getReportableSeverity() const
  329. {
  330. return mReportableSeverity;
  331. }
  332. private:
  333. //!
  334. //! \brief returns an appropriate string for prefixing a log message with the given severity
  335. //!
  336. static const char* severityPrefix(Severity severity)
  337. {
  338. switch (severity)
  339. {
  340. case Severity::kINTERNAL_ERROR: return "[F] ";
  341. case Severity::kERROR: return "[E] ";
  342. case Severity::kWARNING: return "[W] ";
  343. case Severity::kINFO: return "[I] ";
  344. case Severity::kVERBOSE: return "[V] ";
  345. default: assert(0); return "";
  346. }
  347. }
  348. //!
  349. //! \brief returns an appropriate string for prefixing a test result message with the given result
  350. //!
  351. static const char* testResultString(TestResult result)
  352. {
  353. switch (result)
  354. {
  355. case TestResult::kRUNNING: return "RUNNING";
  356. case TestResult::kPASSED: return "PASSED";
  357. case TestResult::kFAILED: return "FAILED";
  358. case TestResult::kWAIVED: return "WAIVED";
  359. default: assert(0); return "";
  360. }
  361. }
  362. //!
  363. //! \brief returns an appropriate output stream (cout or cerr) to use with the given severity
  364. //!
  365. static std::ostream& severityOstream(Severity severity)
  366. {
  367. return severity >= Severity::kINFO ? std::cout : std::cerr;
  368. }
  369. //!
  370. //! \brief method that implements logging test results
  371. //!
  372. static void reportTestResult(const TestAtom& testAtom, TestResult result)
  373. {
  374. severityOstream(Severity::kINFO) << "&&&& " << testResultString(result) << " " << testAtom.mName << " # "
  375. << testAtom.mCmdline << std::endl;
  376. }
  377. //!
  378. //! \brief generate a command line string from the given (argc, argv) values
  379. //!
  380. static std::string genCmdlineString(int argc, char const* const* argv)
  381. {
  382. std::stringstream ss;
  383. for (int i = 0; i < argc; i++)
  384. {
  385. if (i > 0)
  386. ss << " ";
  387. ss << argv[i];
  388. }
  389. return ss.str();
  390. }
  391. Severity mReportableSeverity;
  392. };
  393. namespace
  394. {
  395. //!
  396. //! \brief produces a LogStreamConsumer object that can be used to log messages of severity kVERBOSE
  397. //!
  398. //! Example usage:
  399. //!
  400. //! LOG_VERBOSE(logger) << "hello world" << std::endl;
  401. //!
  402. inline LogStreamConsumer LOG_VERBOSE(const Logger& logger)
  403. {
  404. return LogStreamConsumer(logger.getReportableSeverity(), Severity::kVERBOSE);
  405. }
  406. //!
  407. //! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINFO
  408. //!
  409. //! Example usage:
  410. //!
  411. //! LOG_INFO(logger) << "hello world" << std::endl;
  412. //!
  413. inline LogStreamConsumer LOG_INFO(const Logger& logger)
  414. {
  415. return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINFO);
  416. }
  417. //!
  418. //! \brief produces a LogStreamConsumer object that can be used to log messages of severity kWARNING
  419. //!
  420. //! Example usage:
  421. //!
  422. //! LOG_WARN(logger) << "hello world" << std::endl;
  423. //!
  424. inline LogStreamConsumer LOG_WARN(const Logger& logger)
  425. {
  426. return LogStreamConsumer(logger.getReportableSeverity(), Severity::kWARNING);
  427. }
  428. //!
  429. //! \brief produces a LogStreamConsumer object that can be used to log messages of severity kERROR
  430. //!
  431. //! Example usage:
  432. //!
  433. //! LOG_ERROR(logger) << "hello world" << std::endl;
  434. //!
  435. inline LogStreamConsumer LOG_ERROR(const Logger& logger)
  436. {
  437. return LogStreamConsumer(logger.getReportableSeverity(), Severity::kERROR);
  438. }
  439. //!
  440. //! \brief produces a LogStreamConsumer object that can be used to log messages of severity kINTERNAL_ERROR
  441. // ("fatal" severity)
  442. //!
  443. //! Example usage:
  444. //!
  445. //! LOG_FATAL(logger) << "hello world" << std::endl;
  446. //!
  447. inline LogStreamConsumer LOG_FATAL(const Logger& logger)
  448. {
  449. return LogStreamConsumer(logger.getReportableSeverity(), Severity::kINTERNAL_ERROR);
  450. }
  451. } // anonymous namespace
  452. #endif // TENSORRT_LOGGING_H