This repository has been archived by the owner on Aug 30, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 126
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
396 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
cmake_minimum_required(VERSION 3.3) | ||
PROJECT(cdemo C CXX) | ||
|
||
add_executable(cdemo cdemo.c cdemo_tracing.cpp) | ||
|
||
target_link_libraries(cdemo jaegertracing) | ||
|
||
target_include_directories(cdemo PRIVATE | ||
$<BUILD_INTERFACE:${jaegertracing_SOURCE_DIR}/src> | ||
$<BUILD_INTERFACE:${jaegertracing_BINARY_DIR}/src>) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
#include "cdemo_tracing.h" | ||
|
||
#include <stdlib.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <assert.h> | ||
|
||
/* | ||
* Fake a RPC call using a subprocess to demonstrate marshalling and unmarshalling | ||
* of context info across process boundaries. | ||
*/ | ||
static void | ||
send_fake_rpc(CDemoTraceSpanContext *trace_ctx, const char *endpoint, int depth) | ||
{ | ||
CDemoTraceSpan *span = NULL; | ||
CDemoTraceSpanContext *innerctx = NULL; | ||
char *ctx_arg_hexstr; | ||
#define CDEMO_MAX_CMDLINE_LENGTH 300 | ||
char cmdline[CDEMO_MAX_CMDLINE_LENGTH]; | ||
|
||
span = cdemo_trace_start(trace_ctx, "send_fake_rpc", NULL); | ||
innerctx = cdemo_trace_context_from_span(span); | ||
|
||
/* | ||
* Marshal the trace context for sending as a command line argument. | ||
* | ||
* But since this example doesn't know what your app needs, it just | ||
* gets a binary context, then munges it to hex for sending on a command | ||
* line. | ||
*/ | ||
ctx_arg_hexstr = cdemo_trace_context_to_hex(trace_ctx); | ||
|
||
if (strchr(endpoint, '\"')) | ||
{ | ||
/* We should escape the endpoint path but meh, we'll bail out */ | ||
fprintf(stderr, "paths containing double quotes not handled here"); | ||
exit(1); | ||
} | ||
|
||
/* | ||
* Make the command line to fake up the "RPC" and | ||
* run the | ||
*/ | ||
snprintf(cmdline, CDEMO_MAX_CMDLINE_LENGTH, | ||
"\"%s\" childproc %d \"%s\"", endpoint, depth, ctx_arg_hexstr); | ||
cmdline[CDEMO_MAX_CMDLINE_LENGTH-1] = '\0'; | ||
|
||
fprintf(stderr, "invoking %s\n", cmdline); | ||
|
||
/* | ||
* And invoke the child proc synchronously. We fire and forget here, | ||
* and don't care if it succeeded or not. | ||
*/ | ||
system(cmdline); | ||
|
||
/* | ||
* Hopefully whatever framework/tool you're using has cleanup hooks or | ||
* other helpers so you don't have to do this manual cleanup, but this demo | ||
* is pure C, so: | ||
*/ | ||
free(ctx_arg_hexstr); | ||
cdemo_trace_context_delete(innerctx); | ||
cdemo_trace_done(span); | ||
} | ||
|
||
static void | ||
usage_die(void) | ||
{ | ||
fprintf(stderr, "usage: cdemo tagstring [ncalls [opentracing-context-as-hex]]\n"); | ||
exit(1); | ||
} | ||
|
||
int main(int argc, const char* argv[]) | ||
{ | ||
const char * tagval = NULL; | ||
CDemoTraceSpan *span = NULL; | ||
CDemoTraceSpanContext *imported_ctx = NULL; | ||
CDemoTraceSpanContext *ctx = NULL; | ||
int depth; | ||
|
||
/* Bring up the tracer */ | ||
cdemo_tracing_start(); | ||
|
||
if (argc < 2) | ||
usage_die(); | ||
|
||
tagval = argv[1]; | ||
fprintf(stderr, "cdemo invoked with tag \"%s\"", tagval); | ||
|
||
if (argc > 2) | ||
{ | ||
char *endptr; | ||
depth = strtol(argv[2], &endptr, 10); | ||
if (endptr == argv[2]) | ||
usage_die(); | ||
} | ||
else | ||
{ | ||
/* Default to single level calls */ | ||
depth = 1; | ||
} | ||
fprintf(stderr, " at depth %d", depth); | ||
|
||
if (argc > 3) | ||
{ | ||
/* | ||
* We're being called with an exported trace context. Import it | ||
* to use as the parent context for our trace. | ||
*/ | ||
imported_ctx = cdemo_trace_context_from_hex(argv[3]); | ||
assert(imported_ctx); | ||
fprintf(stderr, " and with context \"%s\"", argv[3]); | ||
} | ||
fprintf(stderr, "\n"); | ||
|
||
span = cdemo_trace_start(imported_ctx, "main", tagval); | ||
ctx = cdemo_trace_context_from_span(span); | ||
|
||
if (depth > 0) | ||
send_fake_rpc(ctx, argv[0], depth - 1); | ||
|
||
cdemo_trace_context_delete(ctx); | ||
cdemo_trace_done(span); | ||
|
||
/* Work around bug #54 by sleeping for >> buffer flush interval */ | ||
fprintf(stderr, "sleeping to ensure buffers flushed... "); | ||
sleep(5); | ||
fprintf(stderr, "done\n"); | ||
|
||
/* Shut down the tracer */ | ||
cdemo_tracing_finish(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
#include "jaegertracing/Tracer.h" | ||
|
||
#include "cdemo_tracing.h" | ||
|
||
#include <iostream> | ||
#include <sstream> | ||
|
||
using std::string; | ||
|
||
/* | ||
* You'll want to load your configuration from a file | ||
* or use the C++ programmatic configuration interfaces, but | ||
* this is a demo: | ||
*/ | ||
|
||
const string cfgstr {R"endyaml( | ||
disabled: false | ||
sampler: | ||
type: const | ||
param: 1 | ||
reporter: | ||
queueSize: 100 | ||
bufferFlushInterval: 2 | ||
logSpans: false | ||
localAgentHostPort: 127.0.0.1:6831 | ||
headers: | ||
jaegerDebugHeader: debug-id | ||
jaegerBaggageHeader: baggage | ||
TraceContextHeaderName: trace-id | ||
traceBaggageHeaderPrefix: "testctx-" | ||
baggage_restrictions: | ||
denyBaggageOnInitializationFailure: false | ||
hostPort: 127.0.0.1:5778 | ||
refreshInterval: 60 | ||
)endyaml"}; | ||
|
||
/* | ||
* You'll probably want a C++ class or set of classes to contain your trace | ||
* functionality, logic around application scopes, etc. But we're thunking | ||
* opentracing to C++ as thinly as we can, so: | ||
*/ | ||
static std::shared_ptr<opentracing::Tracer> tracer; | ||
|
||
extern "C" void | ||
cdemo_tracing_start(void) | ||
{ | ||
const auto config = jaegertracing::Config::parse(YAML::Load(cfgstr)); | ||
tracer = jaegertracing::Tracer::make("cdemo", config); | ||
} | ||
|
||
extern "C" void | ||
cdemo_tracing_finish(void) | ||
{ | ||
if (tracer) | ||
{ | ||
tracer->Close(); | ||
tracer.reset(); | ||
} | ||
} | ||
|
||
/* | ||
* You'll probably want multiple trace functions to take different arguments. | ||
* Code generation for the thunks would be wise. | ||
* | ||
* You'll likely also need some integration with some sort of application | ||
* context/scope information. Or a Span stack you keep in the tracing module. | ||
* | ||
* But for demo purposes we pass around the info manually. | ||
* | ||
* Look into StartSpanWithOptions for how to set up relationships dynamically, | ||
* pass sets of tags generated from helper functions, etc. | ||
*/ | ||
extern "C" CDemoTraceSpan* | ||
cdemo_trace_start(CDemoTraceSpanContext *ctx, | ||
const char *oprname, const char *something_for_a_tag) | ||
{ | ||
assert(oprname != NULL); | ||
auto span = tracer->StartSpan(oprname, {opentracing::ChildOf(ctx)}); | ||
if (something_for_a_tag) | ||
span->SetTag("some_tag", something_for_a_tag); | ||
|
||
/* It's a std::unique_ptr, and we're taking over memory management now */ | ||
return span.release(); | ||
} | ||
|
||
/* | ||
* Finishing a span will schedule it for sending to the trace collector. You | ||
* may add additional tags before finishing if desired, as well as Log entries | ||
* associated with the span, etc. | ||
*/ | ||
extern "C" void | ||
cdemo_trace_done(CDemoTraceSpan *span) | ||
{ | ||
assert(span); | ||
span->Finish(); | ||
delete span; | ||
} | ||
|
||
extern "C" char* | ||
cdemo_trace_context_to_hex(CDemoTraceSpanContext *ctx) | ||
{ | ||
std::stringstream ss (std::ios::out | std::ios::binary); | ||
char *hexout, *hexout_it; | ||
|
||
assert(tracer); | ||
|
||
if (!ctx) | ||
return nullptr; | ||
|
||
/* | ||
* Inject doesn't accept an output-itertor so we must copy the context | ||
* into a stream. | ||
*/ | ||
if (!tracer->Inject(*ctx, ss)) | ||
return nullptr; | ||
|
||
/* | ||
* We want the resulting memory to be free()able so malloc() the buffer. We'd | ||
* have to make a copy at some point to hex-format it; if we used C++ streams | ||
* we'd have to copy the resulting std::string's c_str() again. | ||
*/ | ||
hexout = (char*)malloc(ss.str().length() * 2 + 1); | ||
hexout_it = hexout; | ||
|
||
for (auto it = ss.str().begin(); it != ss.str().end(); ++it) | ||
{ | ||
/* | ||
* Not efficient, but your real app won't be using hex strings, | ||
* you'll be sending around binary or using base64 or using | ||
* http headers or whatever. | ||
*/ | ||
snprintf(hexout_it, 3, "%02hhX", (unsigned char)(*it)); | ||
hexout_it += 2; | ||
} | ||
*hexout_it = '\0'; | ||
|
||
return hexout; | ||
} | ||
|
||
extern "C" CDemoTraceSpanContext* | ||
cdemo_trace_context_from_hex(const char *hexbuf) | ||
{ | ||
std::stringstream ss (std::ios::out | std::ios::in | std::ios::binary); | ||
const char * endhexbuf = strchr(hexbuf, '\0'); | ||
|
||
assert(tracer); | ||
|
||
if (hexbuf == NULL) | ||
return nullptr; | ||
|
||
for ( ; hexbuf != endhexbuf; hexbuf += 2) | ||
{ | ||
unsigned char val; | ||
if (sscanf(hexbuf, "%hhX", &val) != 1) | ||
{ | ||
assert(false); | ||
std::cerr << "bad parse of " << hexbuf << " as hex" << std::endl; | ||
return nullptr; | ||
} | ||
ss << val; | ||
} | ||
|
||
assert(ss.str().length() == 37); | ||
|
||
ss.seekg(0); | ||
auto ctx = tracer->Extract(ss); | ||
if (!ctx) | ||
{ | ||
assert(false); | ||
return nullptr; | ||
} | ||
|
||
assert ((*ctx) != nullptr); | ||
|
||
return (*ctx).release(); | ||
} | ||
|
||
extern "C" CDemoTraceSpanContext* | ||
cdemo_trace_context_from_span(CDemoTraceSpan *span) | ||
{ | ||
/* | ||
* This is a bit of an API defect in opentracing: it doesn't require a copy | ||
* ctor for SpanContext, and the context returned from | ||
* opentracing::Span::context() is returned by-value so it won't be valid | ||
* outside this scope. | ||
*/ | ||
assert(span); | ||
jaegertracing::Span * const jspan = static_cast<jaegertracing::Span*>(span); | ||
return new jaegertracing::SpanContext(jspan->context()); | ||
} | ||
|
||
/* | ||
* You MUST NOT free() memory allocated from C++. This is true even if there's | ||
* no dtor, as it can corrupt the C and C++ runtime libraries' states on some | ||
* platforms. But it'll also fail to fire any dtors. So just don't do it. | ||
* | ||
* (In fact, you must also avoid free()ing memory that was malloc()'d in | ||
* another shared library on some platforms, so this is a good habit even | ||
* without C/C++ mixing). | ||
*/ | ||
extern "C" void | ||
cdemo_trace_context_delete(CDemoTraceSpanContext* ctx) | ||
{ | ||
if (ctx) | ||
delete ctx; | ||
} |
Oops, something went wrong.