Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# Copyright 2019, OpenTelemetry Authors
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someone please remind me: do we need the license comment on basically every file?

#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

package(default_visibility = ["//visibility:public"])

cc_library(
Expand Down
74 changes: 74 additions & 0 deletions api/include/opentelemetry/trace/span_id.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2019, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <cstdint>
#include <cstring>

#include "opentelemetry/nostd/span.h"

namespace opentelemetry
{
namespace trace
{

class SpanId final
{
public:
// The size in bytes of the SpanId.
static constexpr int kSize = 8;

// An invalid SpanId (all zeros).
SpanId() noexcept : rep_{0} {}

// Creates a SpanId with the given ID.
explicit SpanId(nostd::span<const uint8_t, kSize> id) noexcept { memcpy(rep_, id.data(), kSize); }

// Populates the buffer with the lowercase base16 representation of the ID.
void ToLowerBase16(nostd::span<char, 2 * kSize> buffer) const noexcept
{
constexpr char kHex[] = "0123456789ABCDEF";
for (int i = 0; i < kSize; ++i)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not comfortable with non-trivial implementations in headers. Is this okay?

I don't know what the alternative is, if we stick to the requirement that the api is strictly header-only.

opentelemetry::trace::SpanId* id = GetGlobalTracer::MakeSpanId(args...); seems a bit extreme.

Can we make basic parts of the data model (eg SpanId, TraceId, Attribute) have concrete implementations in the API that can't be overridden by e.g. a dynamically loaded tracer at runtime.

{
buffer[i * 2 + 0] = kHex[(rep_[i] >> 4) & 0xF];
buffer[i * 2 + 1] = kHex[(rep_[i] >> 0) & 0xF];
}
}

// Returns a nostd::span of the ID.
nostd::span<const uint8_t, kSize> Id() const noexcept
{
return nostd::span<const uint8_t, kSize>(rep_);
}

bool operator==(const SpanId &that) const noexcept { return memcmp(rep_, that.rep_, kSize) == 0; }

bool operator!=(const SpanId &that) const noexcept { return !(*this == that); }

// Returns false if the SpanId is all zeros.
bool IsValid() const noexcept { return *this != SpanId(); }

// Copies the opaque SpanId data to dest.
void CopyBytesTo(nostd::span<uint8_t, kSize> dest) const noexcept
{
memcpy(dest.data(), rep_, kSize);
}

private:
uint8_t rep_[kSize];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we're using an array of 8-bit numbers to represent this type instead of a uint64_t?

A char array like this will have a byte-level alignment (e.g. alignof(SpanId) == 1)) and will be less performant for doing basic operations like equality check, assignment, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to avoid implying that this is an integer, and thereby avoid endianness issues. SpanId is an opaque 8-byte binary blob.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been discussed in the protocol repository, fwiw: open-telemetry/opentelemetry-proto#52

(It looks like we'll end up on using a byte array in the protocol, which doesn't preclude using 64-bit integers here.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we defer this as an implementation detail, or do we treat alignment as part of the ABI, and therefore have to set it in stone?

};

} // namespace trace
} // namespace opentelemetry
11 changes: 11 additions & 0 deletions api/test/trace/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,14 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "span_id_test",
srcs = [
"span_id_test.cc",
],
deps = [
"//api",
"@com_google_googletest//:gtest_main",
],
)
5 changes: 5 additions & 0 deletions api/test/trace/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ add_executable(noop_test noop_test.cc)
target_link_libraries(noop_test ${GTEST_BOTH_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} opentelemetry_api)
gtest_add_tests(TARGET noop_test TEST_PREFIX trace. TEST_LIST noop_test)

add_executable(span_id_test span_id_test.cc)
target_link_libraries(span_id_test ${GTEST_BOTH_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} opentelemetry_api)
gtest_add_tests(TARGET span_id_test TEST_PREFIX trace. TEST_LIST span_id_test)
45 changes: 45 additions & 0 deletions api/test/trace/span_id_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "opentelemetry/trace/span_id.h"

#include <cstring>
#include <string>

#include <gtest/gtest.h>

namespace
{

using opentelemetry::trace::SpanId;

std::string Hex(const opentelemetry::trace::SpanId &span)
{
char buf[16];
span.ToLowerBase16(buf);
return std::string(buf, sizeof(buf));
}

TEST(SpanIdTest, DefaultConstruction)
{
SpanId id;
EXPECT_FALSE(id.IsValid());
EXPECT_EQ("0000000000000000", Hex(id));
}

TEST(SpanIdTest, ValidId)
{
constexpr uint8_t buf[] = {1, 2, 3, 4, 5, 6, 7, 8};
SpanId id(buf);
EXPECT_TRUE(id.IsValid());
EXPECT_EQ("0102030405060708", Hex(id));
EXPECT_NE(SpanId(), id);
EXPECT_EQ(SpanId(buf), id);
}

TEST(SpanIdTest, CopyBytesTo)
{
constexpr uint8_t src[] = {1, 2, 3, 4, 5, 6, 7, 8};
SpanId id(src);
uint8_t buf[8];
id.CopyBytesTo(buf);
EXPECT_TRUE(memcmp(src, buf, 8) == 0);
}
} // namespace