-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdiagnostics.hpp
More file actions
183 lines (159 loc) · 6.69 KB
/
Copy pathdiagnostics.hpp
File metadata and controls
183 lines (159 loc) · 6.69 KB
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
//
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2025-Present Datadog, Inc.
#pragma once
#include <cinttypes>
#include <climits>
#include <initializer_list>
#include <string_view>
#include <utility>
#include <variant>
#include "datadog/attribute.hpp"
#include "datadog/core.h"
#include "datadog/core.hpp"
#include "datadog/timestamp.hpp"
#include "datadog/uuid.hpp"
#include "datadog/impl/core/types.hpp"
namespace datadog::impl {
/**
* Union of the possible primitive value types that can be included in diagnostic log
* messages as named attributes.
*/
using DiagnosticAttributeValue =
std::variant<bool, int64_t, uint64_t, double, Timestamp, UUID, std::string_view>;
namespace detail {
/**
* Constructs a DiagnosticAttributeValue from an arbitrary value. The integer overloads
* exist because, on 32-bit targets (e.g. armv7), `size_t` is `unsigned int`, which
* matches neither `int64_t` nor `uint64_t` in the variant; routing `int`/`unsigned int`
* through `int64_t` resolves the otherwise-ambiguous variant construction.
*/
template <typename T>
DiagnosticAttributeValue ToDiagnosticValue(T&& value) {
return DiagnosticAttributeValue(std::forward<T>(value));
}
#if UINT_MAX != UINT64_MAX
inline DiagnosticAttributeValue ToDiagnosticValue(int value) {
return DiagnosticAttributeValue(static_cast<int64_t>(value));
}
inline DiagnosticAttributeValue ToDiagnosticValue(unsigned int value) {
return DiagnosticAttributeValue(static_cast<int64_t>(value));
}
#endif
} // namespace detail
/**
* A single named attribute in a diagnostic log message.
*
* Call sites construct these via brace-initialization, e.g. `{{"key", value}, ...}`. We
* use a dedicated type rather than std::pair because brace-initializing
* `std::pair<std::string_view, DiagnosticAttributeValue>` fails to compile with Clang
* 15 on 32-bit targets (e.g. armv7); constructing the variant explicitly in the
* constructor avoids that.
*/
struct DiagnosticAttribute {
std::string_view key;
DiagnosticAttributeValue value;
template <typename T>
DiagnosticAttribute(std::string_view in_key, T&& in_value)
: key(in_key), value(detail::ToDiagnosticValue(std::forward<T>(in_value))) {}
};
/**
* A static list of attribute values to include in a specific log message. Serialized as
* a JSON object; see `json/diagnostic_attribute.hpp`.
*/
using DiagnosticAttributeList = std::initializer_list<DiagnosticAttribute>;
/**
* Wraps a user-provided (or SDK-default) diagnostic message handler callback, providing
* an interface that lets the SDK signal to the client application when warnings,
* errors, or status updates occur.
*
* DiagnosticLogger encapsulates simple, local-only logging to a user-provided callback;
* it does not provide more complex telemetry functionality; nor does it overlap with
* the SDK's Logging feature in any way.
*
* We use DiagnosticLogger throughout the API layer, to signal API usage errors
* irrespective of the internal state of the SDK. DiagnosticLogger is also used within
* the implementation layer when we need to signal warnings via the same callback
* mechanism.
*
* As DiagnosticLogger simply wraps a `std::function` and an enum value, it is trivially
* constructible and copyable.
*/
class DiagnosticLogger {
DiagnosticHandler handler;
DiagnosticLevel threshold;
public:
// A default-initialized DiagnosticLogger handles log calls as no-ops
DiagnosticLogger() : handler(nullptr), threshold(DiagnosticLevel::Error) {}
// A DiagnosticLogger is copyable and movable
DiagnosticLogger(const DiagnosticLogger&) = default;
DiagnosticLogger& operator=(const DiagnosticLogger&) = default;
DiagnosticLogger(DiagnosticLogger&&) = default;
DiagnosticLogger& operator=(DiagnosticLogger&&) = default;
/**
* Initializes a new diagnostic-logger interface given a message-handler callback and
* the threshold at which the callback should be invoked.
*/
explicit DiagnosticLogger(DiagnosticHandler in_handler, DiagnosticLevel in_threshold)
: handler(std::move(in_handler)), threshold(in_threshold) {}
/**
* Initializes a new diagnostic-logger interface from a C-style callback. The C API
* accepts a raw function pointer, with an additional `void* userdata` parameter to
* provide a means of accessing persistent state.
*/
static DiagnosticLogger FromC(
dd_diagnostic_handler_t c_handler,
void* c_handler_userdata,
dd_diagnostic_level_t c_threshold
) {
// Capture the callback and the userdata value to bind a std::function that will
// handle messages emitted from the C++ implementation layer, translating them to
// equivalent C type and invoking the C callback
DiagnosticHandler cpp_handler = [=](const DiagnosticMessage& cpp_message) {
if (c_handler) {
dd_diagnostic_message_t c_message = DiagnosticMessage_ToC(cpp_message);
c_handler(&c_message, c_handler_userdata);
}
};
// Threshold check occurs _before_ the callback is invoked, in DiagnosticLogger
DiagnosticLevel cpp_threshold = DiagnosticLevel_FromC(c_threshold);
return DiagnosticLogger{cpp_handler, cpp_threshold};
}
/**
* Logs a debug message. A debug message provides verbose, low-level details about the
* state of the SDK.
*/
void Debug(const char* text, DiagnosticAttributeList attributes = {}) const {
Emit(DiagnosticLevel::Debug, text, attributes);
}
/**
* Logs a status message. A status message provides relatively infrequent and succinct
* feedback about the state of the SDK, such as the results of upload attempts.
*/
void Status(const char* text, DiagnosticAttributeList attributes = {}) const {
Emit(DiagnosticLevel::Status, text, attributes);
}
/**
* Logs a warning message. A warning usually indicates that the SDK could not fully
* comply with an API call made from the application (e.g. because the application
* supplied invalid arguments, the SDK was not in a supported state to handle the
* operation, etc.) but the SDK continues to operate normally otherwise.
*/
void Warning(const char* text, DiagnosticAttributeList attributes = {}) const {
Emit(DiagnosticLevel::Warning, text, attributes);
}
/**
* Logs an error message. An error usually indicates that the SDK could not be
* initialized or has ceased to function as intended.
*/
void Error(const char* text, DiagnosticAttributeList attributes = {}) const {
Emit(DiagnosticLevel::Error, text, attributes);
}
private:
void Emit(
DiagnosticLevel level, const char* text, DiagnosticAttributeList attributes
) const;
};
} // namespace datadog::impl