Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f495fcf
claude prototype
lia-viam Apr 8, 2026
6e99eb9
Merge branch 'main' of github.com:viamrobotics/viam-cpp-sdk into open…
lia-viam Apr 29, 2026
4cf739f
put cpp file in private too
lia-viam Apr 29, 2026
e284039
use unique_ptr
lia-viam Apr 30, 2026
11dd175
claude cleanup
lia-viam Apr 30, 2026
b7205c2
more claude cleanup
lia-viam May 1, 2026
bda835a
refactor tracing components
lia-viam May 1, 2026
8cdb369
fix duplicate registration
lia-viam May 5, 2026
a21823f
fix url
lia-viam May 6, 2026
e0e4d52
add sendtraces exporter
lia-viam May 7, 2026
05e7293
add env var false helper
lia-viam May 8, 2026
c09d612
do runtime check to not initialize env var
lia-viam May 8, 2026
7dac5e0
opentel is conan only off by default
lia-viam May 8, 2026
2c67c16
Merge branch 'main' of github.com:viamrobotics/viam-cpp-sdk into open…
lia-viam May 8, 2026
0496ac0
explain more
lia-viam May 8, 2026
b7efc9d
conan default
lia-viam May 8, 2026
26fed90
space
lia-viam May 8, 2026
ed2d05f
Merge branch 'main' into opentel-prototype
lia-viam May 11, 2026
56db458
nolint the no-op impl
lia-viam May 11, 2026
87ce7c5
rewrite truth/false tests
lia-viam May 11, 2026
4497008
move the nolint
lia-viam May 11, 2026
058d74a
disable opentel on windows PR builds
lia-viam May 11, 2026
fde2120
conan build fixes
lia-viam May 12, 2026
8929c0b
Update conanfile.py
lia-viam May 12, 2026
1e48257
Merge branch 'main' of github.com:viamrobotics/viam-cpp-sdk into open…
lia-viam May 12, 2026
53bc981
Merge branch 'opentel-prototype' of github.com:lia-viam/viam-cpp-sdk …
lia-viam May 12, 2026
94cc725
conan changes
lia-viam May 12, 2026
8f95e33
remove shared match
lia-viam May 13, 2026
d21e6b7
unconditionally match sharedness
lia-viam May 13, 2026
495e267
revert shared block and bound windows
lia-viam May 13, 2026
15550d9
Merge branch 'main' of github.com:viamrobotics/viam-cpp-sdk into open…
lia-viam May 13, 2026
b93360c
higher range
lia-viam May 13, 2026
19749ab
Merge branch 'main' of github.com:viamrobotics/viam-cpp-sdk into open…
lia-viam May 14, 2026
cea4082
fix use cases not covered by helpers
lia-viam May 14, 2026
ac24daf
remove pimpl in private headers
lia-viam May 14, 2026
00ed4cb
static initialize_propagator
lia-viam May 14, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ jobs:
Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1
refreshenv
conan profile detect
conan install . --build=missing
conan install . --build=missing -o "&:opentelemetry_tracing=False"
cmake . --preset conan-default -DVIAMCPPSDK_BUILD_EXAMPLES=ON
cmake --build --preset=conan-release --target ALL_BUILD install -j 8
env:
Expand Down
13 changes: 13 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@ option(VIAMCPPSDK_SANITIZED_BUILD "Build with address and UB sanitizers (less pe
#
option(VIAMCPPSDK_CLANG_TIDY "Run the clang-tidy linter" OFF)


# - `VIAMCPPSDK_OPENTELEMETRY_TRACING`
#
# When enabled, links against opentelemetry-cpp and compiles in
# W3C Trace Context propagation for all gRPC client and server calls.
# This is off by default and effectively conan-only, because apt packages are not generally available.
#
option(VIAMCPPSDK_OPENTELEMETRY_TRACING "Compile OpenTelemetry tracing into all gRPC calls" OFF)

# The following options are only defined if this project is not being included as a subproject
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
option(VIAMCPPSDK_BUILD_EXAMPLES "Build the example executables" ON)
Expand Down Expand Up @@ -485,6 +494,10 @@ if(VIAMCPPSDK_GRPCXX_VERSION VERSION_LESS 1.43.0)
set(VIAMCPPSDK_GRPCXX_NO_DIRECT_DIAL 1)
endif()

if (VIAMCPPSDK_OPENTELEMETRY_TRACING)
find_package(opentelemetry-cpp CONFIG REQUIRED)
endif()

include(FetchContent)

FetchContent_Declare(
Expand Down
39 changes: 39 additions & 0 deletions conanfile.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
from conan.tools.build import valid_max_cppstd
from conan.tools.files import load
Expand All @@ -18,11 +19,13 @@ class ViamCppSdkRecipe(ConanFile):

options = {
"offline_proto_generation": [True, False],
"opentelemetry_tracing": [True, False],
"shared": [True, False]
}

default_options = {
"offline_proto_generation": True,
"opentelemetry_tracing": True,
"shared": False
}

Expand All @@ -36,6 +39,14 @@ def configure(self):
# Workaround an unfortunately long-standing boost/conan issue which breaks C++20 builds
self.options["boost"].without_cobalt = True

if self.options.opentelemetry_tracing:
self.options["opentelemetry-cpp"].with_otlp_grpc = True
# Disable every HTTP-based exporter so libcurl never enters the
# dependency graph; we only use OTLP/gRPC.
self.options["opentelemetry-cpp"].with_otlp_http = False
self.options["opentelemetry-cpp"].with_zipkin = False
self.options["opentelemetry-cpp"].with_elasticsearch = False

if self.options.shared:
# See https://github.com/conan-io/conan-center-index/issues/25107
self.options["grpc"].secure = True
Expand All @@ -46,6 +57,13 @@ def configure(self):
for lib in ["grpc", "protobuf", "abseil"]:
self.options[lib].shared = True

def validate(self):
if self.options.opentelemetry_tracing:
if not self.dependencies["opentelemetry-cpp"].options.with_otlp_grpc:
raise ConanInvalidConfiguration("opentelemetry_tracing option requires opentelemetry-cpp/*:with_otlp_grpc")



def _xtensor_requires(self):
if valid_max_cppstd(self, 14, False):
return 'xtensor/[>=0.24.3 <0.26.0]'
Expand All @@ -71,6 +89,15 @@ def requirements(self):
self.requires('protobuf/[>=3.17.1 <6.30.0]')
self.requires(self._xtensor_requires(), transitive_headers=True)

if self.options.opentelemetry_tracing:
# Oldest maintained conan package and first version with proper CMake support
if self.settings.os == "Windows":
# v1.25+ builds opentelemetry_proto as a DLL on Windows without
# exporting its protobuf-generated globals, breaking consumer link.
self.requires('opentelemetry-cpp/[>=1.21.0 <1.25.0]')
else:
self.requires('opentelemetry-cpp/[>=1.21.0]')

def build_requirements(self):
if self.options.offline_proto_generation:
self.tool_requires(self._grpc_requires())
Expand All @@ -83,6 +110,7 @@ def generate(self):
tc = CMakeToolchain(self)

tc.cache_variables["VIAMCPPSDK_OFFLINE_PROTO_GENERATION"] = self.options.offline_proto_generation
tc.cache_variables["VIAMCPPSDK_OPENTELEMETRY_TRACING"] = self.options.opentelemetry_tracing
tc.cache_variables["VIAMCPPSDK_USE_DYNAMIC_PROTOS"] = True

# We don't want to constrain these for conan builds because we
Expand Down Expand Up @@ -130,6 +158,11 @@ def package_info(self):

self.cpp_info.components["viamapi"].includedirs.append("include/viam/api")

if self.options.opentelemetry_tracing:
self.cpp_info.components["viamapi"].requires.append(
"opentelemetry-cpp::opentelemetry_proto"
)

if self.options.shared:
self.cpp_info.components["viamapi"].libs = ["viamapi"]
else:
Expand All @@ -156,6 +189,12 @@ def package_info(self):
"viamapi",
])

if self.options.opentelemetry_tracing:
self.cpp_info.components["viamsdk"].requires.extend([
"opentelemetry-cpp::opentelemetry_trace",
"opentelemetry-cpp::opentelemetry_exporter_otlp_grpc",
])

self.cpp_info.components["viamsdk"].requires.extend([
"viam_rust_utils"
])
Expand Down
26 changes: 20 additions & 6 deletions src/viam/api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -365,12 +365,6 @@ target_sources(viamapi
${PROTO_GEN_DIR}/google/api/http.pb.cc
${PROTO_GEN_DIR}/google/api/httpbody.pb.cc
${PROTO_GEN_DIR}/google/rpc/status.pb.cc
${PROTO_GEN_DIR}/opentelemetry/proto/common/v1/common.grpc.pb.cc
${PROTO_GEN_DIR}/opentelemetry/proto/common/v1/common.pb.cc
${PROTO_GEN_DIR}/opentelemetry/proto/resource/v1/resource.grpc.pb.cc
${PROTO_GEN_DIR}/opentelemetry/proto/resource/v1/resource.pb.cc
${PROTO_GEN_DIR}/opentelemetry/proto/trace/v1/trace.grpc.pb.cc
${PROTO_GEN_DIR}/opentelemetry/proto/trace/v1/trace.pb.cc
${PROTO_GEN_DIR}/module/v1/module.grpc.pb.cc
${PROTO_GEN_DIR}/module/v1/module.pb.cc
${PROTO_GEN_DIR}/robot/v1/robot.grpc.pb.cc
Expand Down Expand Up @@ -507,6 +501,26 @@ target_link_libraries(viamapi
PRIVATE Threads::Threads
)

# `robot.proto` imports `opentelemetry/proto/trace/v1/trace.proto`, so the
# OTel proto descriptors are part of viamapi's ABI. When tracing is enabled,
# get them from the system `opentelemetry-cpp::proto` library to avoid
# double-registering them alongside `opentelemetry-cpp::otlp_grpc_exporter`
# (which links the same library transitively into viamsdk). When tracing is
# disabled, compile our locally-generated copies into viamapi instead.
Comment on lines +504 to +509
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are there version skew concerns here? Or does it all just work out because if we are building with otel then we don't care what buf did anyway?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think the latter

if (VIAMCPPSDK_OPENTELEMETRY_TRACING)
target_link_libraries(viamapi PUBLIC opentelemetry-cpp::proto)
else()
target_sources(viamapi
PRIVATE
${PROTO_GEN_DIR}/opentelemetry/proto/common/v1/common.grpc.pb.cc
${PROTO_GEN_DIR}/opentelemetry/proto/common/v1/common.pb.cc
${PROTO_GEN_DIR}/opentelemetry/proto/resource/v1/resource.grpc.pb.cc
${PROTO_GEN_DIR}/opentelemetry/proto/resource/v1/resource.pb.cc
${PROTO_GEN_DIR}/opentelemetry/proto/trace/v1/trace.grpc.pb.cc
${PROTO_GEN_DIR}/opentelemetry/proto/trace/v1/trace.pb.cc
)
endif()

install(
TARGETS viamapi
EXPORT viamapi
Expand Down
10 changes: 10 additions & 0 deletions src/viam/sdk/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ target_sources(viamsdk
common/version_metadata.cpp
common/world_state.cpp
common/private/service_helper.cpp
tracing/private/span_guard.cpp
tracing/private/tracer.cpp
components/arm.cpp
components/audio_in.cpp
components/audio_out.cpp
Expand Down Expand Up @@ -306,6 +308,14 @@ target_link_libraries(viamsdk
PRIVATE Threads::Threads
)

if (VIAMCPPSDK_OPENTELEMETRY_TRACING)
target_compile_definitions(viamsdk PRIVATE VIAMCPPSDK_OPENTELEMETRY_TRACING)
target_link_libraries(viamsdk
PRIVATE opentelemetry-cpp::trace
PRIVATE opentelemetry-cpp::exporter_otlp_grpc
)
endif()

# if the `viam_rust_utils` target exists then we should use it. If not then
# we're probably on a non-x86_64 windows build or some other platform without
# automated `rust-utils` builds, so we should use the stubs instead.
Expand Down
2 changes: 2 additions & 0 deletions src/viam/sdk/common/client_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <viam/sdk/common/private/utils.hpp>
#include <viam/sdk/common/private/version_metadata.hpp>
#include <viam/sdk/log/logging.hpp>
#include <viam/sdk/tracing/private/span_guard.hpp>

namespace viam {
namespace sdk {
Expand Down Expand Up @@ -46,6 +47,7 @@ ClientContext::ClientContext(const ViamChannel& channel) : ClientContext() {
if (channel.auth_token().has_value()) {
wrapped_context_->AddMetadata("authorization", "Bearer " + *channel.auth_token());
}
impl::inject_trace_context(wrapped_context_.get());
}

ClientContext::~ClientContext() = default;
Expand Down
2 changes: 2 additions & 0 deletions src/viam/sdk/common/instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <viam/sdk/common/exception.hpp>
#include <viam/sdk/common/private/instance.hpp>
#include <viam/sdk/registry/registry.hpp>
#include <viam/sdk/tracing/private/tracer.hpp>

namespace viam {
namespace sdk {
Expand All @@ -28,6 +29,7 @@ Instance::Instance() {
impl_ = std::make_unique<Instance::Impl>();
impl_->registry.initialize();
impl_->log_mgr.init_logging();
impl::Tracer::initialize_propagator();
}

Instance::~Instance() {
Expand Down
5 changes: 5 additions & 0 deletions src/viam/sdk/common/instance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
namespace viam {
namespace sdk {

namespace impl {
class Tracer;
} // namespace impl

/// @brief Instance management for Viam C++ SDK applications.
/// This is a single instance class which is responsible for global setup and teardown related to
/// the SDK. An Instance must be constructed before doing anything else in a program, and it must
Expand All @@ -29,6 +33,7 @@ class Instance {
private:
friend class Registry;
friend class LogManager;
friend class impl::Tracer;

struct Impl;
std::unique_ptr<Impl> impl_;
Expand Down
2 changes: 2 additions & 0 deletions src/viam/sdk/common/private/instance.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
#include <viam/sdk/common/instance.hpp>
#include <viam/sdk/log/logging.hpp>
#include <viam/sdk/registry/registry.hpp>
#include <viam/sdk/tracing/private/tracer.hpp>

namespace viam {
namespace sdk {

struct Instance::Impl {
Registry registry;
LogManager log_mgr;
impl::Tracer tracer;
};

} // namespace sdk
Expand Down
8 changes: 7 additions & 1 deletion src/viam/sdk/common/private/service_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <viam/sdk/resource/resource_server_base.hpp>
#include <viam/sdk/rpc/private/grpc_context_observer.hpp>
#include <viam/sdk/tracing/private/span_guard.hpp>

namespace viam {
namespace sdk {
Expand All @@ -30,6 +31,10 @@ class ServiceHelperBase {
protected:
explicit ServiceHelperBase(const char* method) noexcept : method_{method} {}

const char* method_name() const noexcept {
return method_;
}

private:
const char* method_;
};
Expand All @@ -56,7 +61,8 @@ class ServiceHelper : public ServiceHelperBase {
return failNoResource(request_->name());
}
const GrpcContextObserver::Enable enable{*context_};
return invoke_(std::forward<Callable>(callable), std::move(resource));
impl::ServerSpanGuard span_guard{context_, method_name()};
return span_guard.commit(invoke_(std::forward<Callable>(callable), std::move(resource)));
} catch (const std::exception& xcp) {
return failStdException(xcp);
} catch (...) {
Expand Down
24 changes: 19 additions & 5 deletions src/viam/sdk/common/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,30 @@ boost::optional<std::string> get_env(const char* var) {
}

bool is_env_var_true(const char* var) {
static constexpr const std::array<const char*, 5> truth_vals{
{"true", "yes", "1", "TRUE", "YES"}};

if (const auto& val = get_env(var)) {
for (const char* truth : {"true", "yes", "1", "TRUE", "YES"}) {
if (*val == truth) {
return true;
}
}
return std::any_of(truth_vals.begin(), truth_vals.end(), [&val](const char* truth) {
return *val == truth;
});
}

return false;
}

bool is_env_var_false(const char* var) {
static constexpr const std::array<const char*, 5> false_vals{
{"false", "no", "0", "FALSE", "NO"}};

if (const auto& val = get_env(var)) {
return std::any_of(false_vals.begin(), false_vals.end(), [&val](const char* untruth) {
return *val == untruth;
});
} else {
return true;
}
}

} // namespace sdk
} // namespace viam
4 changes: 4 additions & 0 deletions src/viam/sdk/common/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,9 @@ boost::optional<std::string> get_env(const char* var);
/// "true", "yes", "1", "TRUE", or "YES"
bool is_env_var_true(const char* var);

/// @brief Returns whether the environment variable named @param var is unset, or set and equal to
/// "false", "no", "0", "FALSE", or "NO"
bool is_env_var_false(const char* var);

} // namespace sdk
} // namespace viam
Loading
Loading