aboutsummaryrefslogtreecommitdiffstats
path: root/yt/yt/core/misc/error_code.cpp
blob: 7178d2480386db7da9f4735c9c982afcef6b6c56 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include "error_code.h"

#include <yt/yt/core/logging/log.h>

#include <yt/yt/core/misc/singleton.h>

#include <util/string/split.h>

#include <util/system/type_name.h>

namespace NYT {

////////////////////////////////////////////////////////////////////////////////

// TODO(achulkov2): Remove this once we find all duplicate error codes.
static NLogging::TLogger GetLogger()
{
    static NLogging::TLogger logger("ErrorCode");
    return logger;
}

////////////////////////////////////////////////////////////////////////////////

bool TErrorCodeRegistry::TErrorCodeInfo::operator==(const TErrorCodeInfo& rhs) const
{
    return Namespace == rhs.Namespace && Name == rhs.Name;
}

TErrorCodeRegistry* TErrorCodeRegistry::Get()
{
    return LeakySingleton<TErrorCodeRegistry>();
}

TErrorCodeRegistry::TErrorCodeInfo TErrorCodeRegistry::Get(int code) const
{
    auto it = CodeToInfo_.find(code);
    if (it != CodeToInfo_.end()) {
        return it->second;
    }
    for (const auto& range : ErrorCodeRanges_) {
        if (range.Contains(code)) {
            return range.Get(code);
        }
    }
    return {"NUnknown", Format("ErrorCode%v", code)};
}

THashMap<int, TErrorCodeRegistry::TErrorCodeInfo> TErrorCodeRegistry::GetAllErrorCodes() const
{
    return CodeToInfo_;
}

std::vector<TErrorCodeRegistry::TErrorCodeRangeInfo> TErrorCodeRegistry::GetAllErrorCodeRanges() const
{
    return ErrorCodeRanges_;
}

void TErrorCodeRegistry::RegisterErrorCode(int code, const TErrorCodeInfo& errorCodeInfo)
{
    if (!CodeToInfo_.insert({code, errorCodeInfo}).second) {
        // TODO(achulkov2): Deal with duplicate TransportError in NRpc and NBus.
        if (code == 100) {
            return;
        }
        // TODO(yuryalekseev): Deal with duplicate SslError in NRpc and NBus.
        if (code == 119) {
            return;
        }
        auto Logger = GetLogger();
        YT_LOG_FATAL(
            "Duplicate error code (Code: %v, StoredCodeInfo: %v, NewCodeInfo: %v)",
            code,
            CodeToInfo_[code],
            errorCodeInfo);
    }
}

TErrorCodeRegistry::TErrorCodeInfo TErrorCodeRegistry::TErrorCodeRangeInfo::Get(int code) const
{
    return {Namespace, Formatter(code)};
}

bool TErrorCodeRegistry::TErrorCodeRangeInfo::Intersects(const TErrorCodeRangeInfo& other) const
{
    return std::max(From, other.From) <= std::min(To, other.To);
}

bool TErrorCodeRegistry::TErrorCodeRangeInfo::Contains(int value) const
{
    return From <= value && value <= To;
}

void TErrorCodeRegistry::RegisterErrorCodeRange(int from, int to, TString namespaceName, std::function<TString(int)> formatter)
{
    YT_VERIFY(from <= to);

    TErrorCodeRangeInfo newRange{from, to, std::move(namespaceName), std::move(formatter)};
    auto Logger = GetLogger();
    for (const auto& range : ErrorCodeRanges_) {
        YT_LOG_FATAL_IF(
            range.Intersects(newRange),
            "Intersecting error code ranges registered (FirstRange: %v, SecondRange: %v)",
            range,
            newRange);
    }
    ErrorCodeRanges_.push_back(std::move(newRange));
    CheckCodesAgainstRanges();
}

void TErrorCodeRegistry::CheckCodesAgainstRanges() const
{
    auto Logger = GetLogger();
    for (const auto& [code, info] : CodeToInfo_) {
        for (const auto& range : ErrorCodeRanges_) {
            YT_LOG_FATAL_IF(
                range.Contains(code),
                "Error code range contains another registered code "
                "(Range: %v, Code: %v, RangeCodeInfo: %v, StandaloneCodeInfo: %v)",
                range,
                code,
                range.Get(code),
                info);
        }
    }
}

TString TErrorCodeRegistry::ParseNamespace(const std::type_info& errorCodeEnumTypeInfo)
{
    TString name;
    // Ensures that "EErrorCode" is found as a substring in the type name and stores the prefix before
    // the first occurrence into #name.
    YT_VERIFY(StringSplitter(
        TypeName(errorCodeEnumTypeInfo)).SplitByString("EErrorCode").Limit(2).TryCollectInto(&name, &std::ignore));

    // TypeName returns name in form "enum ErrorCode" on Windows
    if (name.StartsWith("enum ")) {
        name.remove(0, 5);
    }

    // If the enum was declared directly in the global namespace, #name should be empty.
    // Otherwise, #name should end with "::".
    if (!name.empty()) {
        YT_VERIFY(name.EndsWith("::"));
        name.resize(name.size() - 2);
    }
    return name;
}

TString ToString(const TErrorCodeRegistry::TErrorCodeInfo& errorCodeInfo)
{
    if (errorCodeInfo.Namespace.empty()) {
        return Format("EErrorCode::%v", errorCodeInfo.Name);
    }
    return Format("%v::EErrorCode::%v", errorCodeInfo.Namespace, errorCodeInfo.Name);
}

TString ToString(const TErrorCodeRegistry::TErrorCodeRangeInfo& errorCodeRangeInfo)
{
    return Format("%v-%v", errorCodeRangeInfo.From, errorCodeRangeInfo.To);
}

////////////////////////////////////////////////////////////////////////////////

} // namespace NYT