-
Notifications
You must be signed in to change notification settings - Fork 61
Expand file tree
/
Copy pathcapi.cpp
More file actions
424 lines (366 loc) · 12.8 KB
/
capi.cpp
File metadata and controls
424 lines (366 loc) · 12.8 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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
// Copyright (c) Microsoft Corporation. All rights reserved.
#include "mat/config.h"
#ifdef _WIN32
#define MATSDK_DECLSPEC __declspec(dllexport)
#endif
#if !defined (ANDROID) || defined(ENABLE_CAPI_HTTP_CLIENT)
#include "http/HttpClient_CAPI.hpp"
#endif
#include "LogManagerFactory.hpp"
#include "LogManagerProvider.hpp"
#include "mat.h"
#include "pal/TaskDispatcher_CAPI.hpp"
#include "utils/Utils.hpp"
#include "pal/PAL.hpp"
#include "CommonFields.h"
#include <mutex>
#include <map>
#include <cstdint>
static const char * libSemver = TELEMETRY_EVENTS_VERSION;
using namespace MAT;
static std::mutex mtx;
static std::map<evt_handle_t, capi_client> clients;
/// <summary>
/// Convert from C API handle to internal C API client struct.
///
/// This method may be used for C API flow debugging, i.e. to obtain the
/// underlying instance of ILogManager and attach a debug event callback.
///
/// NOTE: method is not guaranteed to be ABI-stable and should not be used
/// across dynamic / shared library boundary. Underlying ILogManager /
/// ILogConfiguration are implemented in C++ and do not provide ABI-stable
/// guarantee from one SDK version to another.
/// </summary>
capi_client * MAT::capi_get_client(evt_handle_t handle)
{
LOCKGUARD(mtx);
const auto it = clients.find(handle);
return (it != clients.cend()) ? &(it->second) : nullptr;
}
/// <summary>
/// Remove C API handle from active client tracking struct.
/// </summary>
void remove_client(evt_handle_t handle)
{
LOCKGUARD(mtx);
clients.erase(handle);
}
#define VERIFY_CLIENT_HANDLE(client, ctx) \
if (ctx==nullptr) \
{ \
return EFAULT; /* bad address */ \
}; \
auto client = MAT::capi_get_client(ctx->handle); \
if ((client == nullptr) || (client->logmanager == nullptr)) \
{ \
return ENOENT; \
};
evt_status_t mat_open_core(
evt_context_t *ctx,
const char* config,
http_send_fn_t httpSendFn,
http_cancel_fn_t httpCancelFn,
task_dispatcher_queue_fn_t taskDispatcherQueueFn,
task_dispatcher_cancel_fn_t taskDispatcherCancelFn,
task_dispatcher_join_fn_t taskDispatcherJoinFn)
{
if ((config == nullptr) || (config[0] == 0))
{
// Invalid configuration
return EFAULT;
}
evt_handle_t code = static_cast<evt_handle_t>(hashCode(config));
bool isHashFound = false;
// Find the next available spare hashcode
do
{
const auto client = MAT::capi_get_client(code);
if (client != nullptr)
{
if (client->ctx_data == config)
{
// Guest instance with the same config is already open
return EALREADY;
}
// hash code is assigned to another client, increment and retry
// with the next empty slot
code++;
continue;
};
isHashFound = true;
} while (!isHashFound);
// JSON configuration must start with {
if (config[0] == '{')
{
// Create new configuration object from JSON
clients[code].config = MAT::FromJSON(config);
}
else
{
// Assume that the config string is a token, not JSON.
// That approach allows to consume the lightweght C API without JSON parser compiled in.
std::string moduleName = "CAPI-Client-";
moduleName += std::to_string(code);
clients[code].config =
{
{ CFG_STR_FACTORY_NAME, moduleName },
{ "version", "1.0.0" },
{ CFG_MAP_FACTORY_CONFIG,
{
{ CFG_STR_FACTORY_HOST, "*" },
{ CFG_STR_CONTEXT_SCOPE, CONTEXT_SCOPE_NONE }
}
},
{ CFG_STR_PRIMARY_TOKEN, config }
};
}
// Remember the original config string. Needed to avoid hash code collisions
clients[code].ctx_data = config;
#if !defined (ANDROID) || defined(ENABLE_CAPI_HTTP_CLIENT)
// Create custom HttpClient
if (httpSendFn != nullptr && httpCancelFn != nullptr)
{
try
{
auto http = std::make_shared<HttpClient_CAPI>(httpSendFn, httpCancelFn);
clients[code].http = http;
clients[code].config.AddModule(CFG_MODULE_HTTP_CLIENT, http);
}
catch (...)
{
return EFAULT;
}
}
#endif
// Create custom worker thread
if (taskDispatcherQueueFn != nullptr && taskDispatcherCancelFn != nullptr && taskDispatcherJoinFn != nullptr)
{
try
{
auto taskDispatcher = std::make_shared<PAL::TaskDispatcher_CAPI>(taskDispatcherQueueFn, taskDispatcherCancelFn, taskDispatcherJoinFn);
clients[code].taskDispatcher = taskDispatcher;
clients[code].config.AddModule(CFG_MODULE_TASK_DISPATCHER, taskDispatcher);
}
catch (...)
{
return EFAULT;
}
}
status_t status = static_cast<status_t>(EFAULT);
clients[code].logmanager = LogManagerFactory::Get(clients[code].config, status);
// Verify that the instance pointer is valid
if (clients[code].logmanager == nullptr)
{
status = static_cast<status_t>(EFAULT);
}
ctx->result = static_cast<evt_status_t>(status);
ctx->handle = code;
return ctx->result;
}
evt_status_t mat_open(evt_context_t *ctx)
{
if (ctx == nullptr)
{
return EFAULT; /* bad address */
};
char* config = static_cast<char *>(ctx->data);
return mat_open_core(ctx, config, nullptr, nullptr, nullptr, nullptr, nullptr);
}
evt_status_t mat_open_with_params(evt_context_t *ctx)
{
if (ctx == nullptr)
{
return EFAULT; /* bad address */
};
const evt_open_with_params_data_t* data = static_cast<evt_open_with_params_data_t*>(ctx->data);
if ((data == nullptr) || (data->params == nullptr))
{
// Invalid param data
return EFAULT;
}
http_send_fn_t httpSendFn = nullptr;
http_cancel_fn_t httpCancelFn = nullptr;
task_dispatcher_queue_fn_t taskDispatcherQueueFn = nullptr;
task_dispatcher_cancel_fn_t taskDispatcherCancelFn = nullptr;
task_dispatcher_join_fn_t taskDispatcherJoinFn = nullptr;
for (int32_t i = 0; i < data->paramsCount; ++i) {
const evt_open_param_t& param = data->params[i];
switch (param.type) {
case OPEN_PARAM_TYPE_HTTP_HANDLER_SEND:
httpSendFn = reinterpret_cast<http_send_fn_t>(param.data);
break;
case OPEN_PARAM_TYPE_HTTP_HANDLER_CANCEL:
httpCancelFn = reinterpret_cast<http_cancel_fn_t>(param.data);
break;
case OPEN_PARAM_TYPE_TASK_DISPATCHER_QUEUE:
taskDispatcherQueueFn = reinterpret_cast<task_dispatcher_queue_fn_t>(param.data);
break;
case OPEN_PARAM_TYPE_TASK_DISPATCHER_CANCEL:
taskDispatcherCancelFn = reinterpret_cast<task_dispatcher_cancel_fn_t>(param.data);
break;
case OPEN_PARAM_TYPE_TASK_DISPATCHER_JOIN:
taskDispatcherJoinFn = reinterpret_cast<task_dispatcher_join_fn_t>(param.data);
break;
}
}
return mat_open_core(ctx, data->config, httpSendFn, httpCancelFn, taskDispatcherQueueFn, taskDispatcherCancelFn, taskDispatcherJoinFn);
}
/**
* Marashal C struct to C++ API
*/
evt_status_t mat_log(evt_context_t *ctx)
{
VERIFY_CLIENT_HANDLE(client, ctx);
ILogConfiguration & config = client->config;
evt_prop *evt = static_cast<evt_prop*>(ctx->data);
EventProperties props;
props.unpack(evt, ctx->size);
auto m = props.GetProperties();
EventProperty &prop = m[COMMONFIELDS_IKEY];
std::string token = prop.as_string;
props.erase(COMMONFIELDS_IKEY);
// Privacy feature for OTEL C API client:
//
// C API customer that does not explicitly pass down JSON
// config["config]["scope"] = COMMONFIELDS_SCOPE_ALL;
//
// should not be able to capture the host's context vars.
std::string scope = CONTEXT_SCOPE_NONE;
{
MAT::VariantMap &config_map = config[CFG_MAP_FACTORY_CONFIG];
const auto & it = config_map.find(CFG_STR_CONTEXT_SCOPE);
if (it != config_map.cend())
{
scope = static_cast<const char *>(it->second);
// Specifying "*" in JSON config allows Guest C API logger to capture Host context variables
if (scope == CONTEXT_SCOPE_ALL)
{
scope = CONTEXT_SCOPE_EMPTY;
}
}
}
const auto & it = m.find(COMMONFIELDS_EVENT_SOURCE);
std::string source = ((it != m.cend()) && (it->second.type == EventProperty::TYPE_STRING)) ? it->second.as_string : "";
ILogger* logger = client->logmanager->GetLogger(token, source, scope);
if (logger == nullptr)
{
ctx->result = EFAULT; /* invalid address */
}
else
{
logger->SetParentContext(nullptr);
logger->LogEvent(props);
ctx->result = EOK;
}
return ctx->result;
}
evt_status_t mat_close(evt_context_t *ctx)
{
VERIFY_CLIENT_HANDLE(client, ctx);
const auto result = static_cast<evt_status_t>(LogManagerProvider::Release(client->logmanager->GetLogConfiguration()));
if (client->http != nullptr)
{
client->http = nullptr;
}
if (client->taskDispatcher != nullptr)
{
client->taskDispatcher = nullptr;
}
remove_client(ctx->handle);
ctx->result = result;
return result;
}
evt_status_t mat_pause(evt_context_t *ctx)
{
VERIFY_CLIENT_HANDLE(client, ctx);
const auto result = static_cast<evt_status_t>(client->logmanager->PauseTransmission());
ctx->result = result;
return result;
}
evt_status_t mat_resume(evt_context_t *ctx)
{
VERIFY_CLIENT_HANDLE(client, ctx);
const auto result = static_cast<evt_status_t>(client->logmanager->ResumeTransmission());
ctx->result = result;
return result;
}
evt_status_t mat_upload(evt_context_t *ctx)
{
VERIFY_CLIENT_HANDLE(client, ctx);
const auto result = static_cast<evt_status_t>(client->logmanager->UploadNow());
ctx->result = result;
return result;
}
evt_status_t mat_flush(evt_context_t *ctx)
{
VERIFY_CLIENT_HANDLE(client, ctx);
const auto result = static_cast<evt_status_t>(client->logmanager->Flush());
ctx->result = result;
return result;
}
extern "C" {
/**
* Simple stable backwards- / forward- compatible ABI interface
*/
evt_status_t EVTSDK_LIBABI_CDECL evt_api_call_default(evt_context_t *ctx)
{
evt_status_t result = EFAIL;
if (ctx != nullptr)
{
switch (ctx->call)
{
case EVT_OP_LOAD:
result = ENOTSUP;
break;
case EVT_OP_UNLOAD:
result = ENOTSUP;
break;
case EVT_OP_OPEN:
result = mat_open(ctx);
break;
case EVT_OP_OPEN_WITH_PARAMS:
result = mat_open_with_params(ctx);
break;
case EVT_OP_CLOSE:
result = mat_close(ctx);
break;
case EVT_OP_CONFIG:
result = ENOTSUP;
break;
case EVT_OP_LOG:
result = mat_log(ctx);
break;
case EVT_OP_PAUSE:
result = mat_pause(ctx);
break;
case EVT_OP_RESUME:
result = mat_resume(ctx);
break;
case EVT_OP_UPLOAD:
result = mat_upload(ctx);
break;
case EVT_OP_FLUSH:
result = mat_flush(ctx);
break;
case EVT_OP_VERSION:
// TODO: add handling of ctx->data passed by caller inline stub :
// If there is API version mismatch between the stub and lib impl, then
// depending on version passed down to SDK - lib may need to figure out
// how to handle the mismatch. For now the onus of verifying for SDK
// compatibility is on API caller.
// Inlined stub version passed by the caller - compiled in executable.
LOG_TRACE("header version: %s", ctx->data);
// Actual SDK library implementation version - compiled in the library.
ctx->data = (void*)libSemver;
LOG_TRACE("library version: %s", ctx->data);
result = STATUS_SUCCESS;
break;
// Add more OPs here
default:
result = ENOTSUP;
break;
}
}
return result;
}
}