Skip to content

Commit b93bdde

Browse files
authored
feat: add C language binding (#286)
* feat: add C language binding * refactor: address PR review feedback for C binding * refactor: address second round of PR review feedback for C binding * refactor: address third round of PR review feedback for C binding * fix: simplify dd_error_t doc comment
1 parent a4312d4 commit b93bdde

8 files changed

Lines changed: 877 additions & 5 deletions

File tree

CMakeLists.txt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ project(
1818

1919
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
2020
option(BUILD_STATIC_LIBS "Build static libraries" ON)
21+
option(DD_TRACE_BUILD_C_BINDING "Build C binding" OFF)
2122

2223
if (WIN32)
2324
option(DD_TRACE_STATIC_CRT "Build dd-trace-cpp with static CRT with MSVC" OFF)
@@ -104,6 +105,10 @@ if (DD_TRACE_BUILD_TOOLS)
104105
add_subdirectory(tools/config-inversion)
105106
endif ()
106107

108+
if (DD_TRACE_BUILD_C_BINDING)
109+
add_subdirectory(binding/c)
110+
endif ()
111+
107112
add_library(dd-trace-cpp-objects OBJECT)
108113
add_library(dd-trace-cpp::obj ALIAS dd-trace-cpp-objects)
109114

@@ -294,11 +299,11 @@ if (BUILD_SHARED_LIBS)
294299
)
295300

296301
install(
297-
TARGETS dd-trace-cpp-shared
302+
TARGETS dd-trace-cpp-shared
298303
EXPORT dd-trace-cpp-targets
299304
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
300305
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
301-
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
306+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
302307
)
303308
endif ()
304309

@@ -350,7 +355,7 @@ if (BUILD_STATIC_LIBS)
350355
EXPORT dd-trace-cpp-targets
351356
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
352357
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
353-
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
358+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
354359
)
355360
endif ()
356361

bin/check-format

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#!/bin/sh
22

3-
find include/ src/ examples/ test/ -type f \( -name '*.h' -o -name '*.cpp' \) -print0 | \
3+
find binding/ examples/ fuzz/ include/ src/ test/ -type f \( -name '*.h' -o -name '*.cpp' \) -print0 | \
44
xargs -0 clang-format-14 --style=file --dry-run -Werror

bin/format

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ formatter=clang-format-$version
1818
formatter_options="--style=file -i $*"
1919

2020
find_sources() {
21-
find include/ src/ examples/ test/ fuzz/ -type f \( -name '*.h' -o -name '*.cpp' \) "$@"
21+
find binding/ examples/ fuzz/ include/ src/ test/ -type f \( -name '*.h' -o -name '*.cpp' \) "$@"
2222
}
2323

2424
# If the correct version of clang-format is installed, then use it and quit.

binding/c/CMakeLists.txt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
add_library(dd_trace_c)
2+
add_library(dd-trace-cpp::c_binding ALIAS dd_trace_c)
3+
4+
target_compile_definitions(dd_trace_c PRIVATE DD_TRACE_C_BUILDING)
5+
6+
target_sources(dd_trace_c
7+
PRIVATE
8+
$<TARGET_OBJECTS:dd-trace-cpp::obj>
9+
src/tracer.cpp
10+
)
11+
12+
if (DD_TRACE_TRANSPORT STREQUAL "curl")
13+
target_sources(dd_trace_c
14+
PRIVATE
15+
${CMAKE_SOURCE_DIR}/src/datadog/curl.cpp
16+
${CMAKE_SOURCE_DIR}/src/datadog/default_http_client_curl.cpp
17+
)
18+
19+
target_link_libraries(dd_trace_c
20+
PRIVATE
21+
CURL::libcurl_shared
22+
)
23+
else ()
24+
target_sources(dd_trace_c
25+
PRIVATE
26+
${CMAKE_SOURCE_DIR}/src/datadog/default_http_client_null.cpp
27+
)
28+
endif ()
29+
30+
target_include_directories(dd_trace_c
31+
PUBLIC
32+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
33+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
34+
PRIVATE
35+
${CMAKE_SOURCE_DIR}/src
36+
)
37+
38+
target_link_libraries(dd_trace_c
39+
PRIVATE
40+
dd-trace-cpp::obj
41+
dd-trace-cpp::specs
42+
)
43+
44+
install(TARGETS dd_trace_c
45+
EXPORT dd-trace-cpp-targets
46+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
47+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
48+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
49+
)
50+
51+
install(
52+
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/datadog
53+
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
54+
)
55+
56+
if (DD_TRACE_BUILD_TESTING)
57+
target_sources(tests PRIVATE
58+
${CMAKE_CURRENT_SOURCE_DIR}/test/test_c_binding.cpp
59+
)
60+
61+
target_include_directories(tests PRIVATE
62+
${CMAKE_CURRENT_SOURCE_DIR}/include
63+
)
64+
65+
target_link_libraries(tests PRIVATE dd_trace_c)
66+
endif ()

binding/c/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# C Binding for dd-trace-cpp
2+
3+
A C binding interface on the C++ tracing library for
4+
integration from C-based projects.
5+
6+
## Building
7+
8+
```sh
9+
cmake -B build -DDD_TRACE_BUILD_C_BINDING=ON -DDD_TRACE_BUILD_TESTING=ON
10+
cmake --build build -j
11+
```
12+
13+
## Running Tests
14+
15+
```sh
16+
./build/test/tests "[c_binding]"
17+
```
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
#pragma once
2+
3+
#include <stddef.h>
4+
5+
#if defined(_WIN32)
6+
#if defined(DD_TRACE_C_BUILDING)
7+
#define DD_TRACE_C_API __declspec(dllexport)
8+
#else
9+
#define DD_TRACE_C_API __declspec(dllimport)
10+
#endif
11+
#elif defined(__GNUC__) || defined(__clang__)
12+
#define DD_TRACE_C_API __attribute__((visibility("default")))
13+
#else
14+
#define DD_TRACE_C_API
15+
#endif
16+
17+
#if defined(__cplusplus)
18+
extern "C" {
19+
#endif
20+
21+
// Callback used during trace context extraction. The tracer calls this
22+
// function for each propagation header it needs to read (e.g. "x-datadog-*").
23+
//
24+
// @param key Header name to look up
25+
// @return Header value, or NULL if the header is not present.
26+
// The returned pointer must remain valid until
27+
// dd_tracer_extract_or_create_span returns.
28+
typedef const char* (*dd_context_read_callback)(const char* key);
29+
30+
// Callback used during trace context injection. The tracer calls this
31+
// function for each propagation header it needs to write.
32+
//
33+
// @param key Header name to set
34+
// @param value Header value to set
35+
typedef void (*dd_context_write_callback)(const char* key, const char* value);
36+
37+
typedef enum {
38+
DD_OPT_SERVICE_NAME = 0,
39+
DD_OPT_ENV = 1,
40+
DD_OPT_VERSION = 2,
41+
DD_OPT_AGENT_URL = 3,
42+
DD_OPT_INTEGRATION_NAME = 4,
43+
DD_OPT_INTEGRATION_VERSION = 5
44+
} dd_tracer_option;
45+
46+
// Options for creating a span. Unset fields default to NULL.
47+
typedef struct {
48+
const char* name;
49+
const char* resource;
50+
const char* service;
51+
const char* service_type;
52+
const char* environment;
53+
const char* version;
54+
} dd_span_options_t;
55+
56+
// Error codes returned by the C binding.
57+
typedef enum {
58+
DD_ERROR_OK = 0,
59+
DD_ERROR_NULL_ARGUMENT = 1,
60+
DD_ERROR_INVALID_CONFIG = 2,
61+
DD_ERROR_ALLOCATION_FAILURE = 3
62+
} dd_error_code;
63+
64+
// Error details populated on failure.
65+
typedef struct {
66+
dd_error_code code;
67+
char message[256];
68+
} dd_error_t;
69+
70+
typedef struct dd_conf_s dd_conf_t;
71+
typedef struct dd_tracer_s dd_tracer_t;
72+
typedef struct dd_span_s dd_span_t;
73+
74+
// Creates a tracer configuration instance.
75+
//
76+
// @return Configuration handle, or NULL on allocation failure
77+
DD_TRACE_C_API dd_conf_t* dd_tracer_conf_new(void);
78+
79+
// Release a tracer configuration. Safe to call with NULL.
80+
//
81+
// @param handle Configuration handle to release
82+
DD_TRACE_C_API void dd_tracer_conf_free(dd_conf_t* handle);
83+
84+
// Set or update a configuration field. No-op if handle is NULL.
85+
//
86+
// @param handle Configuration handle
87+
// @param option Configuration option
88+
// @param value Configuration value (interpretation depends on option)
89+
DD_TRACE_C_API void dd_tracer_conf_set(dd_conf_t* handle,
90+
dd_tracer_option option,
91+
const void* value);
92+
93+
// Creates a tracer instance. The configuration handle may be freed with
94+
// dd_tracer_conf_free after this call returns.
95+
//
96+
// @param conf_handle Configuration handle (not modified)
97+
// @param error If non-NULL, filled with error details on failure.
98+
// The caller must allocate the dd_error_t struct.
99+
//
100+
// @return Tracer handle, or NULL on error
101+
DD_TRACE_C_API dd_tracer_t* dd_tracer_new(const dd_conf_t* conf_handle,
102+
dd_error_t* error);
103+
104+
// Release a tracer instance. Safe to call with NULL.
105+
//
106+
// @param tracer_handle Tracer handle to release
107+
DD_TRACE_C_API void dd_tracer_free(dd_tracer_t* tracer_handle);
108+
109+
// Create a span using a Tracer.
110+
//
111+
// @param tracer_handle Tracer handle
112+
// @param options Span options (name must not be NULL)
113+
//
114+
// @return Span handle, or NULL on error
115+
DD_TRACE_C_API dd_span_t* dd_tracer_create_span(dd_tracer_t* tracer_handle,
116+
dd_span_options_t options);
117+
118+
// Extract trace context from incoming headers, or create a new root span
119+
// if extraction fails. Never returns an error span; on extraction failure
120+
// a fresh root span is created.
121+
//
122+
// @param tracer_handle Tracer handle
123+
// @param on_context_read Callback invoked to read propagation headers
124+
// @param options Span options (name must not be NULL)
125+
//
126+
// @return Span handle, or NULL if arguments are invalid
127+
DD_TRACE_C_API dd_span_t* dd_tracer_extract_or_create_span(
128+
dd_tracer_t* tracer_handle, dd_context_read_callback on_context_read,
129+
dd_span_options_t options);
130+
131+
// Release a span instance. Safe to call with NULL.
132+
// If the span has not been finished with dd_span_finish, it is
133+
// automatically finished (its end time is recorded) before being freed.
134+
//
135+
// @param span_handle Span handle
136+
DD_TRACE_C_API void dd_span_free(dd_span_t* span_handle);
137+
138+
// Set a tag (key-value pair) on a span. No-op if any argument is NULL.
139+
//
140+
// @param span_handle Span handle
141+
// @param key Tag key
142+
// @param value Tag value
143+
DD_TRACE_C_API void dd_span_set_tag(dd_span_t* span_handle, const char* key,
144+
const char* value);
145+
146+
// Mark a span as erroneous. No-op if span_handle is NULL.
147+
//
148+
// @param span_handle Span handle
149+
// @param error_value Non-zero to mark as error, zero to clear
150+
DD_TRACE_C_API void dd_span_set_error(dd_span_t* span_handle, int error_value);
151+
152+
// Set an error message on a span. No-op if any argument is NULL.
153+
//
154+
// @param span_handle Span handle
155+
// @param error_message Error message string
156+
DD_TRACE_C_API void dd_span_set_error_message(dd_span_t* span_handle,
157+
const char* error_message);
158+
159+
// Inject trace context into outgoing headers via callback.
160+
// No-op if any argument is NULL.
161+
//
162+
// @param span_handle Span handle
163+
// @param on_context_write Callback invoked per propagation header
164+
DD_TRACE_C_API void dd_span_inject(dd_span_t* span_handle,
165+
dd_context_write_callback on_context_write);
166+
167+
// Create a child span. Returns NULL if any required argument is NULL.
168+
//
169+
// @param span_handle Parent span handle
170+
// @param options Span options (name must not be NULL)
171+
//
172+
// @return Child span handle, or NULL
173+
DD_TRACE_C_API dd_span_t* dd_span_create_child(dd_span_t* span_handle,
174+
dd_span_options_t options);
175+
176+
// Finish a span by recording its end time. No-op if span_handle is NULL.
177+
// After finishing, the span should be freed with dd_span_free.
178+
//
179+
// @param span_handle Span handle
180+
DD_TRACE_C_API void dd_span_finish(dd_span_t* span_handle);
181+
182+
// Get the trace ID as a zero-padded hex string.
183+
//
184+
// @param span_handle Span handle
185+
// @param buffer Output buffer (at least 33 bytes for 128-bit IDs)
186+
// @param buffer_size Size of the buffer
187+
// @return Number of characters written, or -1 on error
188+
DD_TRACE_C_API int dd_span_get_trace_id(dd_span_t* span_handle, char* buffer,
189+
size_t buffer_size);
190+
191+
// Get the span ID as a zero-padded hex string.
192+
//
193+
// @param span_handle Span handle
194+
// @param buffer Output buffer (at least 17 bytes)
195+
// @param buffer_size Size of the buffer
196+
// @return Number of characters written (16), or -1 on error
197+
DD_TRACE_C_API int dd_span_get_span_id(dd_span_t* span_handle, char* buffer,
198+
size_t buffer_size);
199+
200+
// Set the resource name on a span. No-op if any argument is NULL.
201+
//
202+
// @param span_handle Span handle
203+
// @param resource Resource name
204+
DD_TRACE_C_API void dd_span_set_resource(dd_span_t* span_handle,
205+
const char* resource);
206+
207+
// Set the service name on a span. No-op if any argument is NULL.
208+
//
209+
// @param span_handle Span handle
210+
// @param service Service name
211+
DD_TRACE_C_API void dd_span_set_service(dd_span_t* span_handle,
212+
const char* service);
213+
214+
#if defined(__cplusplus)
215+
}
216+
#endif

0 commit comments

Comments
 (0)