mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-12 10:34:21 +00:00
print extra info in exception message
This commit is contained in:
parent
6625536236
commit
75d10f4160
@ -9,6 +9,9 @@
|
||||
#include <IO/ReadBufferFromString.h>
|
||||
#include <common/demangle.h>
|
||||
#include <Common/config_version.h>
|
||||
#include <Common/formatReadable.h>
|
||||
#include <Storages/MergeTree/DiskSpaceMonitor.h>
|
||||
#include <filesystem>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -68,7 +71,66 @@ void tryLogCurrentException(Poco::Logger * logger, const std::string & start_of_
|
||||
}
|
||||
}
|
||||
|
||||
std::string getCurrentExceptionMessage(bool with_stacktrace, bool check_embedded_stacktrace)
|
||||
void getEnospcInfoMessage(std::filesystem::path path, std::string & msg)
|
||||
{
|
||||
/// It's possible to get ENOSPC for non existent file (e.g. if there are no free inodes and creat() fails)
|
||||
/// So try to get info for existent parent directory.
|
||||
while (!std::filesystem::exists(path) && path.has_relative_path())
|
||||
path = path.parent_path();
|
||||
|
||||
/// Most likely path is invalid
|
||||
if (!path.has_relative_path())
|
||||
return;
|
||||
|
||||
auto fs = DiskSpaceMonitor::getStatvfs(path);
|
||||
msg += "\nTotal space: " + formatReadableSizeWithBinarySuffix(fs.f_blocks * fs.f_bsize)
|
||||
+ "\nAvailable space: " + formatReadableSizeWithBinarySuffix(fs.f_bavail * fs.f_bsize)
|
||||
+ "\nTotal inodes: " + formatReadableQuantity(fs.f_files)
|
||||
+ "\nAvailable inodes: " + formatReadableQuantity(fs.f_favail);
|
||||
|
||||
auto mount_point = DiskSpaceMonitor::getMountPoint(path).string();
|
||||
msg += "\nMount point: " + mount_point;
|
||||
msg += "\nFilesystem: " + DiskSpaceMonitor::getFilesystemName(mount_point);
|
||||
}
|
||||
|
||||
std::string getAdditionalExceptionInfo(const std::exception & e)
|
||||
{
|
||||
String msg;
|
||||
try
|
||||
{
|
||||
if (auto file_exception = dynamic_cast<const Poco::FileException *>(&e))
|
||||
{
|
||||
if (file_exception->code() == ENOSPC)
|
||||
getEnospcInfoMessage(file_exception->message(), msg);
|
||||
}
|
||||
else if (auto errno_exception = dynamic_cast<const DB::ErrnoException *>(&e))
|
||||
{
|
||||
if (errno_exception->getErrno() == ENOSPC)
|
||||
{
|
||||
/// Try to extract path from text exception message. Most likely the exception was thrown by
|
||||
/// DB::throwFromErrno("Some message" + filename, ...);
|
||||
/// We suppose "Some message " does not contain '/' and filename is an absolute path starts with '/'.
|
||||
/// throwFromErrno appends ", errno: ..." to the first argument.
|
||||
/// It's ugly hack which may not work correctly. However, getEnospcInfoMessage(...) checks if path exists.
|
||||
size_t likely_path_begin = errno_exception->message().find('/');
|
||||
size_t likely_path_end = errno_exception->message().find(", errno: ", likely_path_begin);
|
||||
if (likely_path_end != std::string::npos)
|
||||
{
|
||||
std::string supposed_to_be_path = errno_exception->message().substr(likely_path_begin,
|
||||
likely_path_end - likely_path_begin);
|
||||
getEnospcInfoMessage(supposed_to_be_path, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
msg += "\nCannot print additional info: " + getCurrentExceptionMessage(false, false, false);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::string getCurrentExceptionMessage(bool with_stacktrace, bool check_embedded_stacktrace /*= false*/, bool with_additional_info /*= true*/)
|
||||
{
|
||||
std::stringstream stream;
|
||||
|
||||
@ -78,7 +140,9 @@ std::string getCurrentExceptionMessage(bool with_stacktrace, bool check_embedded
|
||||
}
|
||||
catch (const Exception & e)
|
||||
{
|
||||
stream << getExceptionMessage(e, with_stacktrace, check_embedded_stacktrace) << " (version " << VERSION_STRING << VERSION_OFFICIAL << ")";
|
||||
stream << getExceptionMessage(e, with_stacktrace, check_embedded_stacktrace)
|
||||
<< (with_additional_info ? getAdditionalExceptionInfo(e) : "")
|
||||
<< " (version " << VERSION_STRING << VERSION_OFFICIAL << ")";
|
||||
}
|
||||
catch (const Poco::Exception & e)
|
||||
{
|
||||
@ -86,7 +150,8 @@ std::string getCurrentExceptionMessage(bool with_stacktrace, bool check_embedded
|
||||
{
|
||||
stream << "Poco::Exception. Code: " << ErrorCodes::POCO_EXCEPTION << ", e.code() = " << e.code()
|
||||
<< ", e.displayText() = " << e.displayText()
|
||||
<< " (version " << VERSION_STRING << VERSION_OFFICIAL << ")";
|
||||
<< (with_additional_info ? getAdditionalExceptionInfo(e) : "")
|
||||
<< " (version " << VERSION_STRING << VERSION_OFFICIAL;
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
@ -100,7 +165,9 @@ std::string getCurrentExceptionMessage(bool with_stacktrace, bool check_embedded
|
||||
if (status)
|
||||
name += " (demangling status: " + toString(status) + ")";
|
||||
|
||||
stream << "std::exception. Code: " << ErrorCodes::STD_EXCEPTION << ", type: " << name << ", e.what() = " << e.what() << ", version = " << VERSION_STRING << VERSION_OFFICIAL;
|
||||
stream << "std::exception. Code: " << ErrorCodes::STD_EXCEPTION << ", type: " << name << ", e.what() = " << e.what()
|
||||
<< (with_additional_info ? getAdditionalExceptionInfo(e) : "")
|
||||
<< ", version = " << VERSION_STRING << VERSION_OFFICIAL;
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
|
@ -87,7 +87,8 @@ void tryLogCurrentException(Poco::Logger * logger, const std::string & start_of_
|
||||
* check_embedded_stacktrace - if DB::Exception has embedded stacktrace then
|
||||
* only this stack trace will be printed.
|
||||
*/
|
||||
std::string getCurrentExceptionMessage(bool with_stacktrace, bool check_embedded_stacktrace = false);
|
||||
std::string getCurrentExceptionMessage(bool with_stacktrace, bool check_embedded_stacktrace = false,
|
||||
bool with_additional_info = true);
|
||||
|
||||
/// Returns error code from ErrorCodes
|
||||
int getCurrentExceptionCode();
|
||||
|
@ -2,7 +2,13 @@
|
||||
|
||||
#include <mutex>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <cstdio>
|
||||
#include <mntent.h>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <common/logger_useful.h>
|
||||
#include <Common/Exception.h>
|
||||
@ -23,6 +29,7 @@ namespace ErrorCodes
|
||||
{
|
||||
extern const int CANNOT_STATVFS;
|
||||
extern const int NOT_ENOUGH_SPACE;
|
||||
extern const int SYSTEM_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@ -96,12 +103,17 @@ public:
|
||||
|
||||
using ReservationPtr = std::unique_ptr<Reservation>;
|
||||
|
||||
static UInt64 getUnreservedFreeSpace(const std::string & path)
|
||||
inline static struct statvfs getStatvfs(const std::string & path)
|
||||
{
|
||||
struct statvfs fs;
|
||||
|
||||
if (statvfs(path.c_str(), &fs) != 0)
|
||||
throwFromErrno("Could not calculate available disk space (statvfs)", ErrorCodes::CANNOT_STATVFS);
|
||||
return fs;
|
||||
}
|
||||
|
||||
static UInt64 getUnreservedFreeSpace(const std::string & path)
|
||||
{
|
||||
struct statvfs fs = getStatvfs(path);
|
||||
|
||||
UInt64 res = fs.f_bfree * fs.f_bsize;
|
||||
|
||||
@ -140,6 +152,52 @@ public:
|
||||
return std::make_unique<Reservation>(size);
|
||||
}
|
||||
|
||||
/// Returns mount point of filesystem where absoulte_path (must exists) is located
|
||||
static std::filesystem::path getMountPoint(std::filesystem::path absolute_path)
|
||||
{
|
||||
if (absolute_path.is_relative())
|
||||
throw Exception("Path is relative. It's a bug.", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
absolute_path = std::filesystem::canonical(absolute_path);
|
||||
|
||||
const auto get_device_id = [](const std::filesystem::path & p)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(p.c_str(), &st))
|
||||
throwFromErrno("Cannot stat " + p.string(), errno);
|
||||
return st.st_dev;
|
||||
};
|
||||
|
||||
/// If /some/path/to/dir/ and /some/path/to/ have different device id,
|
||||
/// then device which contains /some/path/to/dir/filename is mounted to /some/path/to/dir/
|
||||
auto device_id = get_device_id(absolute_path);
|
||||
while (absolute_path.has_relative_path())
|
||||
{
|
||||
auto parent = absolute_path.parent_path();
|
||||
auto parent_device_id = get_device_id(parent);
|
||||
if (device_id != parent_device_id)
|
||||
return absolute_path;
|
||||
absolute_path = parent;
|
||||
device_id = parent_device_id;
|
||||
}
|
||||
|
||||
return absolute_path;
|
||||
}
|
||||
|
||||
/// Returns name of filesystem mounted to mount_point
|
||||
static std::string getFilesystemName(const std::string & mount_point)
|
||||
{
|
||||
auto mounted_filesystems = setmntent("/etc/mtab", "r");
|
||||
mntent fs_info;
|
||||
constexpr size_t buf_size = 4096; /// The same as buffer used for getmntent in glibc. It can happen that it's not enough
|
||||
char buf[buf_size];
|
||||
while (getmntent_r(mounted_filesystems, &fs_info, buf, buf_size) && fs_info.mnt_dir != mount_point);
|
||||
endmntent(mounted_filesystems);
|
||||
if (fs_info.mnt_dir != mount_point)
|
||||
throw DB::Exception("Cannot find name of filesystem by mount point " + mount_point, ErrorCodes::SYSTEM_ERROR);
|
||||
return fs_info.mnt_fsname;
|
||||
}
|
||||
|
||||
private:
|
||||
static UInt64 reserved_bytes;
|
||||
static UInt64 reservation_count;
|
||||
|
Loading…
Reference in New Issue
Block a user