-
Notifications
You must be signed in to change notification settings - Fork 443
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] W3C tracecontext #9
Conversation
In my reading of https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/library-layout.md, W3C tracecontext is |
Hmm, I think the data structure is part of In Python we had similar discussion, and we ended up with:
Happy to hear different ideas. |
#pragma pack(pop) | ||
|
||
/* unchecked version */ | ||
status_t _trace_parent_from_string(TraceParent* trace_parent, const char* s); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The unchecked version doesn't validate input (e.g. nullptr), which gives perf benefit when used internally (e.g. the caller already validated the input).
Let me know if you think this is weird. (I probably learned this style from driver/kernel development).
status_t _trace_parent_from_string(TraceParent* trace_parent, const char* s) | ||
{ | ||
unsigned int value; | ||
/* if this function failed, trace_parent could be polluted, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should establish the convention across APIs.
return STATUS_ERROR_UNKNOWN; | ||
} | ||
|
||
status_t trace_parent_from_string(TraceParent* trace_parent, const char* s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I'm in favor of PascalCase
instead of snake_case
. Should we vote? 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should base these functions off of a back-ported string_view instead of using null terminated strings (i.e. we copy an implementation of string_view that's compatible with c++17's string_view; we can even opt in to std::string_view like absl if they're using c++17+).
Lots of systems don't use null terminated strings (for example nginx, lua)
So having the api require a null-terminated c-style string would require users to make a copy before calling the function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I'm in favor of PascalCase instead of snake_case
Instead of debating convention on a case-by-case basis, could we use #5 to adopt one of the standard naming conventions globally and then redo the code to match that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm very much in favor of string_view.
Also in favor of PascalCase, we can discuss that in #5 if it's more appropriate.
extern "C" { | ||
#endif | ||
|
||
#pragma pack(push, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might not work with some compilers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we shouldn't rely on pragma pack
.
This shouldn't be an issue since no-one should be serializing data by memcpy
ing a struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's purpose of packing TraceParent struct?
I think the TraceContext data structure belongs in
|
|
||
#include <stdint.h> | ||
|
||
typedef int32_t status_t; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going to use error codes, I'd rather use the standard library's std::error_code.
It's somewhat of a pain to work with, but IMO it's still better than rolling your own
#ifndef OPENTELEMETRY_STATUS_H_ | ||
#define OPENTELEMETRY_STATUS_H_ | ||
|
||
#include <stdint.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think s/#include <stdint.h>/#include <cstdint>/
and have the c api as a separate project without dual purpose headers.
|
||
#include "traceparent.h" | ||
|
||
status_t _uint_from_hex_string(unsigned int* value, const char* s) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically "status_t" not a POSIX standard type..
While anything that has _t in its type name is reserved for POSIX.
Cit.: https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html
See section about the "Suffix" -- _t reserved for ANY HEADER..
I think many good open source libraries don't necessarily follow that rule, e.g.
- nginx widely uses _t suffixes.
- Android Framework uses status_t suffix for a non-POSIX type.. In fact ours would then clash with Android's definition!
- Windows SDK uses _t suffix for C-types in several SDK headers..
But the best practice would probably either avoid using t suffix, or ensure that all C-API types have a unique prefix, such as otel or at least ot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I vote for not using *_t suffix on our datatypes. I'd prefer opentelemetry::Status
.
|
||
/* unchecked version */ | ||
status_t _trace_parent_from_string(TraceParent* trace_parent, const char* s); | ||
status_t _trace_parent_to_string(const TraceParent* trace_parent, char* s); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that function names starting with underscore are reserved (by naming convention) for "standard library" functions.
@@ -0,0 +1,106 @@ | |||
/* Copyright 2019, OpenTelemetry Authors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like the implementation to be in C++, not C.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. This is C++ SIG and implementations should preferably be in C++.
|
||
#include "traceparent.h" | ||
|
||
status_t _uint_from_hex_string(unsigned int* value, const char* s) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I vote for not using *_t suffix on our datatypes. I'd prefer opentelemetry::Status
.
return STATUS_ERROR_UNKNOWN; | ||
} | ||
|
||
status_t trace_parent_from_string(TraceParent* trace_parent, const char* s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm very much in favor of string_view.
Also in favor of PascalCase, we can discuss that in #5 if it's more appropriate.
|
||
#include "../status.h" | ||
|
||
#ifdef __cplusplus |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like the headers to be C++. The C wrapper should live somewhere else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re. headers - there is no official Cpp Core Guideline on how to name the headers (some people even prefer .h for c++ code). But I personally find it easier to navigate thru source code directory when the filename is telling what compiler it's gonna be compiled with: like .c|.h for C, .cpp|.hpp for C++ (or .cc|.hh, or .cxx|.hxx, depending on one's personal preference). I agree that the headers must be c++ by default, with an optional pure C projection of some sort. And the header filename should explicitly express what language it is for..
limitations under the License. | ||
*/ | ||
|
||
#ifndef OPENTELEMETRY_CONTEXT_TRACEPARENT_H_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(this came up in the meeting) At least Ryan and I are fans of #pragma once
Do we need to support older compilers where we need to use the header guard? If so, which ones?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've never seen a compiler that doesn't support it. Looking at the portability section of https://en.wikipedia.org/wiki/Pragma_once even old Sun and IBM compilers work with it.
I'd say adopt it -- you can always change to include guards if someone complains.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I vote for the pragma then. :)
extern "C" { | ||
#endif | ||
|
||
#pragma pack(push, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we shouldn't rely on pragma pack
.
This shouldn't be an issue since no-one should be serializing data by memcpy
ing a struct.
uint8_t trace_id[16]; | ||
uint8_t parent_id[8]; | ||
uint8_t trace_flags; | ||
} TraceParent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be a class SpanContext
and there should be a helper function like:
SpanContext FromTraceParentHeader(string_view header);
status_t _trace_parent_to_string(const TraceParent* trace_parent, char* s); | ||
|
||
/* checked version */ | ||
status_t trace_parent_from_string(TraceParent* trace_parent, const char* s); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest keeping the public API small and only having the checked version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. All the public API should validate input.
|
||
#include "traceparent.h" | ||
|
||
status_t _uint_from_hex_string(unsigned int* value, const char* s) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Pointers to strings without lengths are IMO problematic. If you don't want to incur a runtime cost in the release version at the minimum I would
assert
the length and !nullptr fors
. - However instead of
char*
I think we should be using C++ and either std::string or std::string_view (char* should be a justified exception).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Pointers to strings without lengths are IMO problematic. If you don't want to incur a runtime cost in the release version at the minimum I would
assert
the length and !nullptr fors
.
Length check is covered here (by checking if c is not '\0'
).
nullptr
check is not covered since this function is considered as unchecked (not public), it is intended to be called only by the OpenTelemetry C++ API/SDK code, which has already done the nullptr
check.
- However instead of
char*
I think we should be using C++ and either std::string or std::string_view (char* should be a justified exception).
std::string
requires memory allocation if the input is a plain buffer, I was trying to avoid memory allocation here.
std::string_view
requires very recent version of C++ 17.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ | ||
result += c - '0'; | ||
} | ||
else if (c >= 'a' && c <= 'f') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- What about uppercase hex digits?
- I'd make hexdigit2int a function and let compiler inline it for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- What about uppercase hex digits?
Traceparent only allows lower case hex digits: https://www.w3.org/TR/trace-context/#traceparent-header-field-values
- I'd make hexdigit2int a function and let compiler inline it for me.
Good idea. I was originally trying to see if we want to target C89 (which doesn't have inline).
If we're moving to pure C++, will use inline.
extern "C" { | ||
#endif | ||
|
||
#pragma pack(push, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's purpose of packing TraceParent struct?
status_t trace_parent_from_string(TraceParent* trace_parent, const char* s); | ||
status_t trace_parent_to_string(const TraceParent* trace_parent, char* s); | ||
|
||
#ifdef __cplusplus |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't there a decision that this is going to be a C++ library and a C library can be implemented later which will wrap this one?
Is it a stated goal that the headers must compile and be usable both for C++ and C?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was unclear at the moment I was preparing the PR.
Now we've discussed and agreed that we should have C++ only. C support should be a separate wrapper (which can be hosted in this project for now, later we might want to move it to a separate repo).
s += 2; | ||
IF_FALSE_RETURN('-' == *s, STATUS_ERROR_INPUT_ILLEGAL) | ||
s += 1; | ||
for (int i = 0; i < 16; i++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use sizeof(trace_parent->trace_id)/sizeof(trace_parent->trace_id[0])
instead of 16. I know it is defined in w3c standard but I still prefer code correctness not to depend on some declaration far away to match what I think it should be.
} | ||
|
||
status_t trace_parent_from_string(TraceParent* trace_parent, const char* s) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this intended to be the public version which checks the input arguments? I don't see length checks for s
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Length check is covered by detecting '\0'
.
Origin/propagators
Change OTLP HTTP content_type default to binary (open-telemetry#2558)
Trying to get initial feedback (I know there will be a LOT) before I continue on the implementation.
Feel free to use this initial PR to discuss whatever topic you want - styling, naming, C or C++, macro, convention..