logging.h 16 KB

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