master
c 314 lines 9.36 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "json.h"
4
5 /**
6 * Initialize JSON connector instance
7 *
8 * @param instance an instance data structure.
9 * @return Returns 0 on success, 1 on failure.
10 */
11 int init_json_instance(struct instance *instance)
12 {
13 instance->worker = simple_connector_worker;
14
15 struct simple_connector_config *connector_specific_config = callocz(1, sizeof(struct simple_connector_config));
16 instance->config.connector_specific_config = (void *)connector_specific_config;
17 connector_specific_config->default_port = 5448;
18
19 struct simple_connector_data *connector_specific_data = callocz(1, sizeof(struct simple_connector_data));
20 instance->connector_specific_data = connector_specific_data;
21
22 instance->start_batch_formatting = NULL;
23 instance->start_host_formatting = format_host_labels_json_plaintext;
24 instance->start_chart_formatting = NULL;
25
26 if (EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AS_COLLECTED)
27 instance->metric_formatting = format_dimension_collected_json_plaintext;
28 else
29 instance->metric_formatting = format_dimension_stored_json_plaintext;
30
31 instance->end_chart_formatting = NULL;
32 instance->variables_formatting = NULL;
33 instance->end_host_formatting = flush_host_labels;
34 instance->end_batch_formatting = simple_connector_end_batch;
35
36 instance->prepare_header = NULL;
37
38 instance->check_response = exporting_discard_response;
39
40 instance->buffer = (void *)buffer_create(0, &netdata_buffers_statistics.buffers_exporters);
41 if (!instance->buffer) {
42 netdata_log_error("EXPORTING: cannot create buffer for json exporting connector instance %s", instance->config.name);
43 return 1;
44 }
45
46 simple_connector_init(instance);
47
48 if (netdata_mutex_init(&instance->mutex))
49 return 1;
50 if (netdata_cond_init(&instance->cond_var))
51 return 1;
52
53 return 0;
54 }
55
56 /**
57 * Initialize JSON connector instance for HTTP protocol
58 *
59 * @param instance an instance data structure.
60 * @return Returns 0 on success, 1 on failure.
61 */
62 int init_json_http_instance(struct instance *instance)
63 {
64 instance->worker = simple_connector_worker;
65
66 struct simple_connector_config *connector_specific_config = callocz(1, sizeof(struct simple_connector_config));
67 instance->config.connector_specific_config = (void *)connector_specific_config;
68 connector_specific_config->default_port = 5448;
69
70 struct simple_connector_data *connector_specific_data = callocz(1, sizeof(struct simple_connector_data));
71 instance->connector_specific_data = connector_specific_data;
72
73 connector_specific_data->ssl = NETDATA_SSL_UNSET_CONNECTION;
74 if (instance->config.options & EXPORTING_OPTION_USE_TLS) {
75 netdata_ssl_initialize_ctx(NETDATA_SSL_EXPORTING_CTX);
76 }
77
78 instance->start_batch_formatting = open_batch_json_http;
79 instance->start_host_formatting = format_host_labels_json_plaintext;
80 instance->start_chart_formatting = NULL;
81
82 if (EXPORTING_OPTIONS_DATA_SOURCE(instance->config.options) == EXPORTING_SOURCE_DATA_AS_COLLECTED)
83 instance->metric_formatting = format_dimension_collected_json_plaintext;
84 else
85 instance->metric_formatting = format_dimension_stored_json_plaintext;
86
87 instance->end_chart_formatting = NULL;
88 instance->variables_formatting = NULL;
89 instance->end_host_formatting = flush_host_labels;
90 instance->end_batch_formatting = close_batch_json_http;
91
92 instance->prepare_header = json_http_prepare_header;
93
94 instance->check_response = exporting_discard_response;
95
96 instance->buffer = (void *)buffer_create(0, &netdata_buffers_statistics.buffers_exporters);
97
98 simple_connector_init(instance);
99
100 if (netdata_mutex_init(&instance->mutex))
101 return 1;
102 if (netdata_cond_init(&instance->cond_var))
103 return 1;
104
105 return 0;
106 }
107
108 /**
109 * Format host labels for JSON connector
110 *
111 * @param instance an instance data structure.
112 * @param host a data collecting host.
113 * @return Always returns 0.
114 */
115
116 int format_host_labels_json_plaintext(struct instance *instance, RRDHOST *host)
117 {
118 if (!instance->labels_buffer)
119 instance->labels_buffer = buffer_create(1024, &netdata_buffers_statistics.buffers_exporters);
120
121 if (unlikely(!sending_labels_configured(instance)))
122 return 0;
123
124 buffer_strcat(instance->labels_buffer, "\"labels\":{");
125 rrdlabels_to_buffer(host->rrdlabels, instance->labels_buffer, "", ":", "\"", ",",
126 exporting_labels_filter_callback, instance,
127 NULL, sanitize_json_string);
128 buffer_strcat(instance->labels_buffer, "},");
129
130 return 0;
131 }
132
133 /**
134 * Format dimension using collected data for JSON connector
135 *
136 * @param instance an instance data structure.
137 * @param rd a dimension.
138 * @return Always returns 0.
139 */
140 int format_dimension_collected_json_plaintext(struct instance *instance, RRDDIM *rd)
141 {
142 RRDSET *st = rd->rrdset;
143 RRDHOST *host = st->rrdhost;
144
145 if (instance->config.type == EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
146 if (buffer_strlen((BUFFER *)instance->buffer) > 2)
147 buffer_strcat(instance->buffer, ",\n");
148 }
149
150 buffer_sprintf(
151 instance->buffer,
152
153 "{"
154 "\"prefix\":\"%s\","
155 "\"hostname\":\"%s\","
156 "%s"
157
158 "\"chart_id\":\"%s\","
159 "\"chart_name\":\"%s\","
160 "\"chart_family\":\"%s\","
161 "\"chart_context\":\"%s\","
162 "\"chart_type\":\"%s\","
163 "\"units\":\"%s\","
164
165 "\"id\":\"%s\","
166 "\"name\":\"%s\","
167 "\"value\":",
168
169 instance->config.prefix,
170 (host == localhost) ? instance->config.hostname : rrdhost_hostname(host),
171 instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : "",
172
173 rrdset_id(st),
174 rrdset_name(st),
175 rrdset_family(st),
176 rrdset_context(st),
177 rrdset_parts_type(st),
178 rrdset_units(st),
179 rrddim_id(rd),
180 rrddim_name(rd));
181
182 if(rrddim_is_float(rd))
183 buffer_sprintf(instance->buffer, NETDATA_DOUBLE_FORMAT, rrddim_last_collected_as_double(rd));
184 else
185 buffer_sprintf(instance->buffer, COLLECTED_NUMBER_FORMAT, (collected_number)rrddim_last_collected_raw_int(rd));
186
187 buffer_sprintf(instance->buffer, ",\"timestamp\":%llu}",
188 (unsigned long long)rd->collector.last_collected_time.tv_sec);
189
190 if (instance->config.type != EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
191 buffer_strcat(instance->buffer, "\n");
192 }
193
194 return 0;
195 }
196
197 /**
198 * Format dimension using a calculated value from stored data for JSON connector
199 *
200 * @param instance an instance data structure.
201 * @param rd a dimension.
202 * @return Always returns 0.
203 */
204 int format_dimension_stored_json_plaintext(struct instance *instance, RRDDIM *rd)
205 {
206 RRDSET *st = rd->rrdset;
207 RRDHOST *host = st->rrdhost;
208
209 time_t last_t;
210 NETDATA_DOUBLE value = exporting_calculate_value_from_stored_data(instance, rd, &last_t);
211
212 if(isnan(value))
213 return 0;
214
215 if (instance->config.type == EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
216 if (buffer_strlen((BUFFER *)instance->buffer) > 2)
217 buffer_strcat(instance->buffer, ",\n");
218 }
219
220 buffer_sprintf(
221 instance->buffer,
222 "{"
223 "\"prefix\":\"%s\","
224 "\"hostname\":\"%s\","
225 "%s"
226
227 "\"chart_id\":\"%s\","
228 "\"chart_name\":\"%s\","
229 "\"chart_family\":\"%s\","
230 "\"chart_context\": \"%s\","
231 "\"chart_type\":\"%s\","
232 "\"units\": \"%s\","
233
234 "\"id\":\"%s\","
235 "\"name\":\"%s\","
236 "\"value\":" NETDATA_DOUBLE_FORMAT ","
237
238 "\"timestamp\": %llu}",
239
240 instance->config.prefix,
241 (host == localhost) ? instance->config.hostname : rrdhost_hostname(host),
242 instance->labels_buffer ? buffer_tostring(instance->labels_buffer) : "",
243
244 rrdset_id(st),
245 rrdset_name(st),
246 rrdset_family(st),
247 rrdset_context(st),
248 rrdset_parts_type(st),
249 rrdset_units(st),
250 rrddim_id(rd),
251 rrddim_name(rd),
252 value,
253
254 (unsigned long long)last_t);
255
256 if (instance->config.type != EXPORTING_CONNECTOR_TYPE_JSON_HTTP) {
257 buffer_strcat(instance->buffer, "\n");
258 }
259
260 return 0;
261 }
262
263 /**
264 * Open a JSON list for a bach
265 *
266 * @param instance an instance data structure.
267 * @return Always returns 0.
268 */
269 int open_batch_json_http(struct instance *instance)
270 {
271 buffer_strcat(instance->buffer, "[\n");
272
273 return 0;
274 }
275
276 /**
277 * Close a JSON list for a bach and update buffered bytes counter
278 *
279 * @param instance an instance data structure.
280 * @return Always returns 0.
281 */
282 int close_batch_json_http(struct instance *instance)
283 {
284 buffer_strcat(instance->buffer, "\n]\n");
285
286 simple_connector_end_batch(instance);
287
288 return 0;
289 }
290
291 /**
292 * Prepare HTTP header
293 *
294 * @param instance an instance data structure.
295 * @return Returns 0 on success, 1 on failure.
296 */
297 void json_http_prepare_header(struct instance *instance)
298 {
299 struct simple_connector_data *simple_connector_data = instance->connector_specific_data;
300
301 buffer_sprintf(
302 simple_connector_data->last_buffer->header,
303 "POST /api/put HTTP/1.1\r\n"
304 "Host: %s\r\n"
305 "%s"
306 "Content-Type: application/json\r\n"
307 "Content-Length: %lu\r\n"
308 "\r\n",
309 instance->config.destination,
310 simple_connector_data->auth_string ? simple_connector_data->auth_string : "",
311 (unsigned long int) buffer_strlen(simple_connector_data->last_buffer->buffer));
312
313 return;
314 }