1 /* 2 * Error.hpp 3 * 4 * Copyright (C) 2021 by RStudio, PBC 5 * 6 * Unless you have received this program directly from RStudio pursuant to the terms of a commercial license agreement 7 * with RStudio, then this program is licensed to you under the following terms: 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 10 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 12 * permit persons to whom the Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 18 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 * 22 */ 23 24 #ifndef SHARED_CORE_ERROR_HPP 25 #define SHARED_CORE_ERROR_HPP 26 27 #include <string> 28 #include <system_error> 29 #include <vector> 30 31 #include <boost/current_function.hpp> 32 #include <boost/system/error_code.hpp> 33 34 #include "Logger.hpp" 35 #include "PImpl.hpp" 36 37 namespace rstudio { 38 namespace core { 39 40 class FilePath; 41 class Error; 42 class Success; 43 44 /** 45 * @brief A class which can be derived from in order to prevent child classes from being derived from further. 46 */ 47 class ErrorLock 48 { 49 friend class Error; 50 friend class Success; 51 52 /** 53 * @brief Virtual Destructor. 54 */ 55 virtual ~ErrorLock() = default; 56 57 private: 58 /** 59 * @brief Private constructor to prevent further derivation of Error and Success. 60 */ 61 ErrorLock() = default; 62 63 /** 64 * @brief Private copy constructor to prevent further derivation of Error and Success. 65 */ 66 ErrorLock(const ErrorLock&) = default; 67 }; 68 69 /** 70 * @brief Class which represents the location of an error. 71 */ 72 class ErrorLocation 73 { 74 public: 75 /** 76 * @brief Default constructor. 77 */ 78 ErrorLocation(); 79 80 /** 81 * @brief Copy constructor. 82 * 83 * @param in_other The error location to move to this. 84 */ 85 ErrorLocation(const ErrorLocation& in_other); 86 87 /** 88 * @brief Move constructor. 89 * 90 * @param in_other The error location to move to this. 91 */ 92 ErrorLocation(ErrorLocation&& in_other) noexcept; 93 94 /** 95 * @brief Constructor. 96 * 97 * @param in_function The function in which the error occurred. 98 * @param in_file The file in which the error occurred. 99 * @param in_line The line at which the error occurred. 100 */ 101 ErrorLocation(const char* in_function, const char* in_file, long in_line); 102 103 /** 104 * @brief Assignment operator. 105 * 106 * @param in_other The location to copy to this location. 107 * 108 * @return A reference to this location. 109 */ 110 ErrorLocation& operator=(const ErrorLocation& in_other); 111 112 /** 113 * @brief Equality comparison operator. 114 * 115 * @param in_location The location to compare this location with. 116 * 117 * @return True if in_location is the same as this location; false otherwise. 118 */ 119 bool operator==(const ErrorLocation& in_location) const; 120 121 /** 122 * @brief Formats the error location as a string. 123 * 124 * @return The error location formatted as a string. 125 */ 126 std::string asString() const; 127 128 /** 129 * @brief Gets the file where the error occurred. 130 * 131 * @return The file where the error occurred. 132 */ 133 const std::string& getFile() const; 134 135 /** 136 * @brief Gets the function where the error occurred. 137 * 138 * @return The function where the error occurred. 139 */ 140 const std::string& getFunction() const; 141 142 /** 143 * @brief Gets the line where the error occurred. 144 * 145 * @return The line where the error occurred. 146 */ 147 long getLine() const; 148 149 /** 150 * @brief Checks whether the location is set. 151 * 152 * @return True if a location has been set; false otherwise. 153 */ 154 bool hasLocation() const; 155 156 private: 157 // The private implementation of ErrorLocation. 158 PRIVATE_IMPL(m_impl); 159 }; 160 161 /** 162 * @brief Convenience typedef for error properties. 163 */ 164 typedef std::vector<std::pair<std::string, std::string> > ErrorProperties; 165 166 167 /** 168 * @brief Class which represents an error. 169 * 170 * This class should not be derived from since it is returned by value throughout the SDK. Instead, create helper 171 * functions for each "subclass" of Error that would be desired. 172 */ 173 class Error : public virtual ErrorLock 174 { 175 public: 176 /** 177 * @brief Constructor. 178 */ 179 Error(); 180 181 /** 182 * @brief Copy constructor. 183 * 184 * @param in_other The error to copy. 185 */ 186 Error(const Error& in_other); 187 188 /** 189 * @brief Constructor. 190 * 191 * @param in_ec The boost error code to convert from. 192 * @param in_location The location of the error. 193 */ 194 Error(const boost::system::error_code& in_ec, const ErrorLocation& in_location); 195 196 /** 197 * @brief Constructor. 198 * 199 * @param in_ec The boost error code to convert from. 200 * @param in_cause The error which caused this error. 201 * @param in_location The location of the error. 202 */ 203 Error(const boost::system::error_code& in_ec, const Error& in_cause, const ErrorLocation& in_location); 204 205 /** 206 * @brief Constructor. 207 * 208 * @param in_ec The boost error code to convert from. 209 * @param in_message The detailed error message. (e.g. "The JobNetworkRequest is not supported by this 210 * plugin.") 211 * @param in_location The location of the error. 212 */ 213 Error(const boost::system::error_code& in_ec, std::string in_message, const ErrorLocation& in_location); 214 215 /** 216 * @brief Constructor. 217 * 218 * @param in_ec The boost error code to convert from. 219 * @param in_message The detailed error message. (e.g. "The JobNetworkRequest is not supported by this 220 * plugin.") 221 * @param in_cause The error which caused this error. 222 * @param in_location The location of the error. 223 */ 224 Error(const boost::system::error_code& in_ec, 225 std::string in_message, 226 const Error& in_cause, 227 const ErrorLocation& in_location); 228 229 /** 230 * @brief Constructor. 231 * 232 * @param in_ec The boost error condition to convert from. 233 * @param in_location The location of the error. 234 */ 235 Error(const boost::system::error_condition& in_ec, const ErrorLocation& in_location); 236 237 /** 238 * @brief Constructor. 239 * 240 * @param in_ec The boost error condition to convert from. 241 * @param in_cause The error which caused this error. 242 * @param in_location The location of the error. 243 */ 244 Error(const boost::system::error_condition& in_ec, const Error& in_cause, const ErrorLocation& in_location); 245 246 /** 247 * @brief Constructor. 248 * 249 * @param in_ec The boost error condition to convert from. 250 * @param in_message The detailed error message. (e.g. "The JobNetworkRequest is not supported by this 251 * plugin.") 252 * @param in_location The location of the error. 253 */ 254 Error(const boost::system::error_condition& in_ec, std::string in_message, const ErrorLocation& in_location); 255 256 /** 257 * @brief Constructor. 258 * 259 * @param in_ec The boost error condition to convert from. 260 * @param in_message The detailed error message. (e.g. "The JobNetworkRequest is not supported by this 261 * plugin.") 262 * @param in_cause The error which caused this error. 263 * @param in_location The location of the error. 264 */ 265 Error(const boost::system::error_condition& in_ec, 266 std::string in_message, 267 const Error& in_cause, 268 const ErrorLocation& in_location); 269 270 /** 271 * @brief Constructor. 272 * 273 * @param in_name A contextual or categorical name for the error. (e.g. "RequestNotSupported") 274 * @param in_code The non-zero error code. Note that an error code of zero indicates success. (e.g. 1) 275 * @param in_location The location of the error. 276 */ 277 Error(std::string in_name, int in_code, const ErrorLocation& in_location); 278 279 /** 280 * @brief Constructor. 281 * 282 * @param in_name A contextual or categorical name for the error. (e.g. "RequestNotSupported") 283 * @param in_code The non-zero error code. Note that an error code of zero indicates success. (e.g. 1) 284 * @param in_cause The error which caused this error. 285 * @param in_location The location of the error. 286 */ 287 Error(std::string in_name, int in_code, const Error& in_cause, const ErrorLocation& in_location); 288 289 290 /** 291 * @brief Constructor. 292 * 293 * @param in_name A contextual or categorical name for the error. (e.g. "RequestNotSupported") 294 * @param in_code The non-zero error code. Note that an error code of zero indicates success. (e.g. 1) 295 * @param in_message The detailed error message. (e.g. "The JobNetworkRequest is not supported by this 296 * plugin.") 297 * @param in_location The location of the error. 298 */ 299 Error(std::string in_name, int in_code, std::string in_message, const ErrorLocation& in_location); 300 301 /** 302 * @brief Constructor. 303 * 304 * @param in_name A contextual or categorical name for the error. (e.g. "RequestNotSupported") 305 * @param in_code The non-zero error code. Note that an error code of zero indicates success. (e.g. 1) 306 * @param in_message The detailed error message. (e.g. "The JobNetworkRequest is not supported by this 307 * plugin.") 308 * @param in_cause The error which caused this error. 309 * @param in_location The location of the error. 310 */ 311 Error(std::string in_name, 312 int in_code, 313 std::string in_message, 314 const Error& in_cause, 315 const ErrorLocation& in_location); 316 317 /** 318 * @brief Non-virtual destructor because only Success inherits Error and it will keep Error lightweight. 319 */ 320 ~Error() override = default; 321 322 /** 323 * @brief Overloaded operator bool to allow Errors to be treated as boolean values. 324 * 325 * @return True if there is an error; false otherwise. 326 */ 327 explicit operator bool() const; 328 329 /** 330 * @brief Overloaded operator ! to allow Errors to be treated as boolean values. 331 * 332 * @return True if there is not an error; false otherwise. 333 */ 334 bool operator!() const; 335 336 /** 337 * @brief Equality operator. Two errors are equal if their codes and names are the same. 338 * 339 * @param in_other The error to compare with this error. 340 * 341 * @return True if in_other is equal to this error; false otherwise. 342 */ 343 bool operator==(const Error& in_other) const; 344 345 /** 346 * @brief Equality operator. Two errors are equal if their codes and names are the same. 347 * 348 * @param in_ec The boost error code to compare with this error. 349 * 350 * @return True if in_ec has the same error code and category name as this error; false otherwise. 351 */ 352 bool operator==(const boost::system::error_code& in_ec) const; 353 354 /** 355 * @brief Inequality operator. Two errors are equal if their codes and names are the same. 356 * 357 * @param in_other The error to compare with this error. 358 * 359 * @return True if in_other is not equal to this error; false otherwise. 360 */ 361 bool operator!=(const Error& in_other) const; 362 363 /** 364 * @brief Inequality operator. Two errors are equal if their codes and names are the same. 365 * 366 * @param in_ec The boost error code to compare with this error. 367 * 368 * @return True if !(this == in_ec) would return true; false otherwise. 369 */ 370 bool operator!=(const boost::system::error_code& in_ec) const; 371 372 /** 373 * @brief Add or updates a property of this error. If any properties with the specified name exist, they will all be 374 * updated. 375 * 376 * @param in_name The name of the property to add or update. 377 * @param in_value The new value of the property. 378 */ 379 void addOrUpdateProperty(const std::string& in_name, const std::string& in_value); 380 381 /** 382 * @brief Add or updates a property of this error. If any properties with the specified name exist, they will all be 383 * updated. 384 * 385 * @param in_name The name of the property to add or update. 386 * @param in_value The new value of the property. 387 */ 388 void addOrUpdateProperty(const std::string& in_name, const FilePath& in_value); 389 390 /** 391 * @brief Add or updates a property of this error. If any properties with the specified name exist, they will all be 392 * updated. 393 * 394 * @param in_name The name of the property to add or update. 395 * @param in_value The new value of the property. 396 */ 397 void addOrUpdateProperty(const std::string& in_name, int in_value); 398 399 /** 400 * @brief Adds a property of this error. If a property with the same name already exists, a duplicate will be added. 401 * 402 * @param in_name The name of the property to add or update. 403 * @param in_value The new value of the property. 404 */ 405 void addProperty(const std::string& in_name, const std::string& in_value); 406 407 /** 408 * @brief Adds a property of this error. If a property with the same name already exists, a duplicate will be added. 409 * 410 * @param in_name The name of the property to add or update. 411 * @param in_value The new value of the property. 412 */ 413 void addProperty(const std::string& in_name, const FilePath& in_value); 414 415 /** 416 * @brief Adds a property of this error. If a property with the same name already exists, a duplicate will be added. 417 * 418 * @param in_name The name of the property to add or update. 419 * @param in_value The new value of the property. 420 */ 421 void addProperty(const std::string& in_name, int in_value); 422 423 /** 424 * @brief Formats the error as a string. 425 * 426 * @return The error formatted as a string. 427 */ 428 std::string asString() const; 429 430 /** 431 * @brief Checks whether this error was caused by a separate error. 432 * 433 * @return True if the error has an associated cause. 434 */ 435 bool hasCause() const; 436 437 /** 438 * @brief Gets the error which caused this error. 439 * 440 * @return The error which caused this error. 441 */ 442 const Error& getCause() const; 443 444 /** 445 * @brief Gets the error code. 446 * 447 * @return The error code. 448 */ 449 int getCode() const; 450 451 /** 452 * @brief Gets the location where the error occurred. 453 * 454 * @return The location where the error occurred. 455 */ 456 const ErrorLocation& getLocation() const; 457 458 /** 459 * @brief Gets the error message. 460 * 461 * @return The error message. 462 */ 463 const std::string& getMessage() const; 464 465 /** 466 * @brief Gets the name of the error. 467 * 468 * @return The name of the error. 469 */ 470 const std::string& getName() const; 471 472 /** 473 * @brief Gets the custom properties of the error. 474 * 475 * @return The custom properties of this error. 476 */ 477 const ErrorProperties& getProperties() const; 478 479 /** 480 * @brief Gets a custom property of this error. 481 * 482 * @param name The name of the property to retrieve. 483 * 484 * @return The value of the specified property, if it exists; empty string otherwise. 485 */ 486 std::string getProperty(const std::string& name) const; 487 488 /** 489 * @brief Gets the cause of the error. 490 * 491 * @return The cause of the error. 492 */ 493 std::string getSummary() const; 494 495 /** 496 * @brief Gets whether this error was expected or not. 497 * 498 * @return True if this error was expected; false otherwise. 499 */ 500 bool isExpected() const; 501 502 /** 503 * @brief Sets the property that indicates that this error was expected. 504 * Errors are unexpected by default; only unexpected errors will be logged. 505 * Expected errors can be marked as such to suppress logging of those errors. 506 */ 507 void setExpected(); 508 509 private: 510 /** 511 * @brief Helper method to copy the error object on write. 512 */ 513 void copyOnWrite(); 514 515 /** 516 * @brief Helper method that checks whether this error object represents an error or not (i.e. a non-zero error 517 * code). 518 * 519 * @return True if the error code is non-zero; false otherwise. 520 */ 521 bool isError() const; 522 523 // The private implementation of Error. 524 PRIVATE_IMPL_SHARED(m_impl); 525 526 /** 527 * @brief Gets a reference to the private implementation member. 528 * 529 * @return A reference to the private implementation member. 530 */ 531 Impl& impl() const; 532 }; 533 534 /** 535 * @brief Class which represents a successful operation (i.e. no error). 536 */ 537 class Success : public Error 538 { 539 public: 540 /** 541 * @brief Constructor. 542 */ Success()543 Success() : Error() {} 544 }; 545 546 /** 547 * @brief Output stream operator. Writes the specified error to the provided output stream. 548 * 549 * @param io_ostream The output stream to which to write the error. 550 * @param in_error The error to write to the output stream. 551 * 552 * @return A reference to the provided output stream, for chaining writes. 553 */ 554 std::ostream& operator<<(std::ostream& io_ostream, const Error& in_error); 555 556 #ifdef _WIN32 557 558 // Use this macro instead of systemError(::GetLastError(), ERROR_LOCATION) otherwise 559 // the ERROR_LOCATION macro may evaluate first and reset the Win32 error code to 560 // zero (no error), causing the wrong value to be passed to systemError. This is currently 561 // the case on debug builds using MSVC. 562 #define LAST_SYSTEM_ERROR() []() {auto lastErr = ::GetLastError(); return systemError(lastErr, ERROR_LOCATION);}() 563 564 #endif // _WIN32 565 566 /** 567 * @brief Function which creates a system error. 568 * 569 * @param in_code The error code. (e.g. 1) 570 * @param in_location The location of the error. 571 * 572 * @return A system error. 573 */ 574 Error systemError(int in_code, const ErrorLocation& in_location); 575 576 /** 577 * @brief Function which creates a system error. 578 * 579 * @param in_code The std system error code. 580 * @param in_location The location of the error. 581 * 582 * @return A system error. 583 */ 584 Error systemError(const std::error_code& in_code, const ErrorLocation& in_location); 585 586 /** 587 * @brief Function which creates a system error. 588 * 589 * @param in_error The system error which occurred. 590 * @param in_location The location of the error. 591 * 592 * @return A system error. 593 */ 594 Error systemError(const std::system_error& in_error, const ErrorLocation& in_location); 595 596 /** 597 * @brief Function which creates a system error. 598 * 599 * @param in_code The error code. (e.g. 1) 600 * @param in_cause The error which caused this error. 601 * @param in_location The location of the error. 602 * 603 * @return A system error. 604 */ 605 Error systemError(int in_code, const Error& in_cause, const ErrorLocation& in_location); 606 607 /** 608 * @brief Function which creates a system error. 609 * 610 * @param in_code The std system error code. 611 * @param in_cause The error which caused this error. 612 * @param in_location The location of the error. 613 * 614 * @return A system error. 615 */ 616 Error systemError(const std::error_code& in_code, const Error& in_cause, const ErrorLocation& in_location); 617 618 /** 619 * @brief Function which creates a system error. 620 * 621 * @param in_error The system error which occurred. 622 * @param in_cause The error which caused this error. 623 * @param in_location The location of the error. 624 * 625 * @return A system error. 626 */ 627 Error systemError(const std::system_error& in_error, const Error& in_cause, const ErrorLocation& in_location); 628 629 /** 630 * @brief Function which creates a system error. 631 * 632 * @param in_code The error code. (e.g. 1) 633 * @param in_description A detailed description of the error. (e.g. "Failed to open socket while attempting to 634 * connect to Kubernetes.") 635 * @param in_location The location of the error. 636 * 637 * @return A system error. 638 */ 639 Error systemError(int in_code, const std::string& in_description, const ErrorLocation& in_location); 640 641 /** 642 * @brief Function which creates a system error. 643 * 644 * @param in_code The std system error code. 645 * @param in_description A detailed description of the error. (e.g. "Failed to open socket while attempting to 646 * connect to Kubernetes.") 647 * @param in_location The location of the error. 648 * 649 * @return A system error. 650 */ 651 Error systemError(const std::error_code& in_code, const std::string& in_description, const ErrorLocation& in_location); 652 653 /** 654 * @brief Function which creates a system error. 655 * 656 * @param in_error The system error which occurred. 657 * @param in_description A detailed description of the error. (e.g. "Failed to open socket while attempting to 658 * connect to Kubernetes.") 659 * @param in_location The location of the error. 660 * 661 * @return A system error. 662 */ 663 Error systemError( 664 const std::system_error& in_error, 665 const std::string& in_description, 666 const ErrorLocation& in_location); 667 668 /** 669 * @brief Function which creates a system error. 670 * 671 * @param in_code The error code. (e.g. 1) 672 * @param in_description A detailed description of the error. (e.g. "Failed to open socket while attempting to 673 * connect to Kubernetes.") 674 * @param in_cause The error which caused this error. 675 * @param in_location The location of the error. 676 * 677 * @return A system error. 678 */ 679 Error systemError( 680 int in_code, 681 const std::string& in_description, 682 const Error& in_cause, 683 const ErrorLocation& in_location); 684 685 /** 686 * @brief Function which creates a system error. 687 * 688 * @param in_code The std system error code. 689 * @param in_description A detailed description of the error. (e.g. "Failed to open socket while attempting to 690 * connect to Kubernetes.") 691 * @param in_cause The error which caused this error. 692 * @param in_location The location of the error. 693 * 694 * @return A system error. 695 */ 696 Error systemError( 697 const std::error_code& in_code, 698 const std::string& in_description, 699 const Error& in_cause, 700 const ErrorLocation& in_location); 701 702 /** 703 * @brief Function which creates a system error. 704 * 705 * @param in_error The system error which occurred. 706 * @param in_description A detailed description of the error. (e.g. "Failed to open socket while attempting to 707 * connect to Kubernetes.") 708 * @param in_cause The error which caused this error. 709 * @param in_location The location of the error. 710 * 711 * @return A system error. 712 */ 713 Error systemError( 714 const std::system_error& in_error, 715 const std::string& in_description, 716 const Error& in_cause, 717 const ErrorLocation& in_location); 718 719 /** 720 * @brief Function which creates a system error associated with a particular system call. 721 * 722 * @param in_function The name of the system call associated with this error. 723 * @param in_code The error code -- typically errno, or an error number returned by the call itself. 724 * @param in_location The location of the error. 725 * 726 * @return A system error. 727 */ 728 Error systemCallError( 729 const std::string& in_function, 730 int in_code, 731 const ErrorLocation& in_location); 732 733 /** 734 * @brief Function which creates a system error associated with a particular system call. 735 * 736 * @param in_function The name of the system call associated with this error. 737 * @param in_code The error code -- typically errno, or an error number returned by the call itself. 738 * @param in_message The error message. Overrides the default error message associated with the error code. 739 * @param in_location The location of the error. 740 * 741 * @return A system error. 742 */ 743 Error systemCallError( 744 const std::string& in_function, 745 int in_code, 746 const std::string& in_message, 747 const ErrorLocation& in_location); 748 749 /** 750 * @brief Function which creates an unknown error. This should be used only when a specific error code cannot be 751 * determined. 752 * 753 * @param in_message The detailed error message. (e.g. "Failed to open socket while attempting to connect to 754 * Kubernetes.") 755 * @param in_cause The error which caused this error. 756 * @param in_location The location of the error. 757 * 758 * @return An unknown error. 759 */ 760 Error unknownError(const std::string& in_message, const ErrorLocation& in_location); 761 762 /** 763 * @brief Function which creates an unknown error. This should be used only when a specific error code cannot be 764 * determined. 765 * 766 * @param in_message The detailed error message. (e.g. "Failed to open socket while attempting to connect to 767 * Kubernetes.") 768 * @param in_cause The error which caused this error. 769 * @param in_location The location of the error. 770 * 771 * @return An unknown error. 772 */ 773 Error unknownError(const std::string& in_message, const Error& in_cause, const ErrorLocation& in_location); 774 775 // return a printable error message from an error (depending on the error this 776 // might require consulting the message, category, or name) 777 std::string errorDescription(const Error& error); 778 std::string errorMessage(const core::Error& error); 779 780 // return the error message associated with a particular system error code 781 std::string systemErrorMessage(int code); 782 783 } // namespace core 784 } // namespace rstudio 785 786 #ifdef STRIPPED_FILENAME 787 #define ERROR_LOCATION rstudio::core::ErrorLocation( \ 788 BOOST_CURRENT_FUNCTION, STRIPPED_FILENAME, __LINE__) 789 #else 790 #define ERROR_LOCATION rstudio::core::ErrorLocation( \ 791 BOOST_CURRENT_FUNCTION, __FILE__, __LINE__) 792 #endif 793 794 #define CATCH_UNEXPECTED_EXCEPTION \ 795 catch(const std::exception& e) \ 796 { \ 797 rstudio::core::log::logErrorMessage(std::string("Unexpected exception: ") + \ 798 e.what(), ERROR_LOCATION); \ 799 } \ 800 catch(...) \ 801 { \ 802 rstudio::core::log::logErrorMessage("Unknown exception", ERROR_LOCATION); \ 803 } 804 805 #endif 806