osquery-1/include/osquery/error.h
Alexander 5fa1ebad13
Output stream like operator << for Error (#4670)
To create an error human readable message should be provided among other argmunts.
Which is good to better understanding what happend by log records.
To make it more informative user in most cases should put in those message some data (numbers, strings etc.).
This operator will help us to avoid using verbose constructions like boost::format or std::ostringstream or something similar to format a proper error message.
We will be able just to "stream" in a created error any "printable" variables from the context.

Additionaly we will be able to use "fancy" tools for streams like boost::io::quoted or std::hex to format messages.

Example:
```c++
createError(SystemErorr::NoSuchFile, "Could not read pidfile: ")
  << boost::io::quoted(pidfile_path)
  << " " << read_status.toString();
```
2018-07-05 16:12:18 +01:00

180 lines
4.9 KiB
C++

/**
* Copyright (c) 2018-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the Apache 2.0 license (found in the
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
* in the COPYING file in the root directory of this source tree).
* You may select, at your option, one of the above-listed licenses.
*/
#pragma once
#include <boost/core/demangle.hpp>
#include <memory>
#include <new>
#include <sstream>
#include <string>
#include <typeinfo>
namespace osquery {
class ErrorBase {
public:
virtual std::string getShortMessage() const = 0;
virtual std::string getFullMessage() const = 0;
virtual std::string getShortMessageRecursive() const = 0;
virtual std::string getFullMessageRecursive() const = 0;
virtual ~ErrorBase(){};
};
template <typename ErrorCodeEnumType>
class Error final : public ErrorBase {
private:
static std::string getErrorTypeName() {
return boost::core::demangle(typeid(ErrorCodeEnumType).name());
}
public:
using SelfType = Error<ErrorCodeEnumType>;
explicit Error(ErrorCodeEnumType error_code,
std::string message,
std::unique_ptr<ErrorBase> underlying_error = nullptr)
: errorCode_(error_code),
message_(std::move(message)),
underlyingError_(std::move(underlying_error)) {}
virtual ~Error() = default;
Error(Error&& other) = default;
Error(const Error& other) = delete;
Error& operator=(Error&& other) = default;
Error& operator=(const Error& other) = delete;
ErrorCodeEnumType getErrorCode() const {
return errorCode_;
}
bool hasUnderlyingError() const {
return underlyingError_ != nullptr;
}
const ErrorBase& getUnderlyingError() const {
return *underlyingError_;
}
std::unique_ptr<ErrorBase> takeUnderlyingError() const {
return std::move(underlyingError_);
}
std::string getShortMessage() const override {
return getErrorTypeName() + " " +
std::to_string(static_cast<int>(errorCode_));
}
std::string getFullMessage() const override {
std::string full_message = getShortMessage();
if (message_.size() > 0) {
full_message += " (" + message_ + ")";
}
return full_message;
}
std::string getShortMessageRecursive() const override {
std::string full_message = getShortMessage();
if (underlyingError_) {
full_message += " <- " + underlyingError_->getShortMessageRecursive();
}
return full_message;
}
std::string getFullMessageRecursive() const override {
std::string full_message = getFullMessage();
if (underlyingError_) {
full_message += " <- " + underlyingError_->getFullMessageRecursive();
}
return full_message;
}
void appendToMessage(const std::string& text) {
message_.append(text);
}
private:
ErrorCodeEnumType errorCode_;
std::string message_;
std::unique_ptr<ErrorBase> underlyingError_;
};
template <class T>
inline bool operator==(const Error<T>& lhs, const Error<T>& rhs) {
return lhs.getErrorCode() == rhs.getErrorCode();
}
template <class T>
inline bool operator==(const Error<T>* lhs, const T rhs) {
return lhs->getErrorCode() == rhs;
}
template <class T>
inline bool operator==(const Error<T>& lhs, const T rhs) {
return lhs.getErrorCode() == rhs;
}
template <class T>
inline bool operator==(const ErrorBase& lhs, const T rhs) {
try {
const Error<T>& casted_lhs = dynamic_cast<const Error<T>&>(lhs);
return casted_lhs == rhs;
} catch (std::bad_cast _) {
return false;
}
}
template <class T>
inline bool operator==(const ErrorBase* lhs, const T rhs) {
auto casted_lhs = dynamic_cast<const Error<T>*>(lhs);
return casted_lhs != nullptr && casted_lhs == rhs;
}
inline std::ostream& operator<<(std::ostream& out, const ErrorBase& error) {
out << error.getFullMessageRecursive();
return out;
}
template <typename ErrorCodeEnumType>
Error<ErrorCodeEnumType> createError(
ErrorCodeEnumType error_code,
std::string message,
std::unique_ptr<ErrorBase> underlying_error = nullptr) {
return Error<ErrorCodeEnumType>(
error_code, std::move(message), std::move(underlying_error));
}
template <typename ErrorCodeEnumType, typename OtherErrorCodeEnumType>
Error<ErrorCodeEnumType> createError(
ErrorCodeEnumType error_code,
std::string message,
Error<OtherErrorCodeEnumType> underlying_error) {
return Error<ErrorCodeEnumType>(
error_code,
std::move(message),
std::make_unique<Error<OtherErrorCodeEnumType>>(
std::move(underlying_error)));
}
template <typename ErrorType,
typename ValueType,
typename = typename std::enable_if<
std::is_base_of<ErrorBase, ErrorType>::value>::type>
inline ErrorType operator<<(ErrorType&& error, const ValueType& value) {
std::ostringstream ostr{};
ostr << value;
error.appendToMessage(ostr.str());
return std::forward<ErrorType>(error);
}
} // namespace osquery