From 31437c36d4fd2d8db05685d5511e6f1588cac828 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 18 Feb 2025 11:02:17 -0500
Subject: [PATCH 01/86] initial commit of instance object

---
 src/viam/sdk/CMakeLists.txt      |  2 ++
 src/viam/sdk/common/instance.cpp | 11 +++++++++++
 src/viam/sdk/common/instance.hpp | 24 ++++++++++++++++++++++++
 3 files changed, 37 insertions(+)
 create mode 100644 src/viam/sdk/common/instance.cpp
 create mode 100644 src/viam/sdk/common/instance.hpp

diff --git a/src/viam/sdk/CMakeLists.txt b/src/viam/sdk/CMakeLists.txt
index cd7992f2a..e42db9ae5 100644
--- a/src/viam/sdk/CMakeLists.txt
+++ b/src/viam/sdk/CMakeLists.txt
@@ -50,6 +50,7 @@ target_sources(viamsdk
   PRIVATE
     common/client_helper.cpp
     common/exception.cpp
+    common/instance.cpp
     common/linear_algebra.cpp
     common/pose.cpp
     common/proto_value.cpp
@@ -142,6 +143,7 @@ target_sources(viamsdk
     FILES
       ../../viam/sdk/common/client_helper.hpp
       ../../viam/sdk/common/exception.hpp
+      ../../viam/sdk/common/instance.hpp
       ../../viam/sdk/common/linear_algebra.hpp
       ../../viam/sdk/common/pose.hpp
       ../../viam/sdk/common/proto_convert.hpp
diff --git a/src/viam/sdk/common/instance.cpp b/src/viam/sdk/common/instance.cpp
new file mode 100644
index 000000000..1ec93d01f
--- /dev/null
+++ b/src/viam/sdk/common/instance.cpp
@@ -0,0 +1,11 @@
+#include <viam/sdk/common/instance.hpp>
+
+namespace viam {
+namespace sdk {
+
+Instance::Instance() {
+    registry_.initialize();
+}
+
+}  // namespace sdk
+}  // namespace viam
diff --git a/src/viam/sdk/common/instance.hpp b/src/viam/sdk/common/instance.hpp
new file mode 100644
index 000000000..8297b74e4
--- /dev/null
+++ b/src/viam/sdk/common/instance.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <viam/sdk/registry/registry.hpp>
+
+namespace viam {
+namespace sdk {
+
+/// @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
+/// remain alive in a valid state for the duration of the program. Creating multiple overlapping
+/// Instance objects is an error.
+class Instance {
+   public:
+    Instance();
+
+    Registry& registry();
+
+   private:
+    Registry registry_;
+};
+
+}  // namespace sdk
+}  // namespace viam

From 932ebaa1885b2e238cf339d0642c681ebf653b7a Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 18 Feb 2025 11:04:40 -0500
Subject: [PATCH 02/86] wip: initial commit to get sdk building with instance

---
 src/viam/sdk/module/service.cpp    | 38 ++++++++++--------
 src/viam/sdk/module/service.hpp    |  7 +++-
 src/viam/sdk/registry/registry.cpp | 62 ++++++++++++++----------------
 src/viam/sdk/registry/registry.hpp | 51 ++++++++++++------------
 src/viam/sdk/robot/client.cpp      | 27 +++++++------
 src/viam/sdk/robot/client.hpp      | 21 ++++++++--
 src/viam/sdk/rpc/server.cpp        |  5 +--
 src/viam/sdk/rpc/server.hpp        |  4 +-
 8 files changed, 119 insertions(+), 96 deletions(-)

diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp
index 53f1fe9bc..526beecb5 100644
--- a/src/viam/sdk/module/service.cpp
+++ b/src/viam/sdk/module/service.cpp
@@ -57,7 +57,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         std::shared_ptr<Resource> res;
         const Dependencies deps = parent.get_dependencies_(&request->dependencies(), cfg.name());
         const std::shared_ptr<const ModelRegistration> reg =
-            Registry::lookup_model(cfg.api(), cfg.model());
+            parent.registry_.lookup_model(cfg.api(), cfg.model());
         if (reg) {
             try {
                 res = reg->construct_resource(deps, cfg);
@@ -111,7 +111,8 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
             BOOST_LOG_TRIVIAL(error) << "unable to stop resource: " << err.what();
         }
 
-        const std::shared_ptr<const ModelRegistration> reg = Registry::lookup_model(cfg.name());
+        const std::shared_ptr<const ModelRegistration> reg =
+            parent.registry_.lookup_model(cfg.name());
         if (reg) {
             try {
                 const std::shared_ptr<Resource> res = reg->construct_resource(deps, cfg);
@@ -131,7 +132,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         ResourceConfig cfg = from_proto(proto);
 
         const std::shared_ptr<const ModelRegistration> reg =
-            Registry::lookup_model(cfg.api(), cfg.model());
+            parent.registry_.lookup_model(cfg.api(), cfg.model());
         if (!reg) {
             return grpc::Status(grpc::UNKNOWN,
                                 "unable to validate resource " + cfg.resource_name().name() +
@@ -207,31 +208,36 @@ Dependencies ModuleService::get_dependencies_(
 
 std::shared_ptr<Resource> ModuleService::get_parent_resource_(const Name& name) {
     if (!parent_) {
-        parent_ = RobotClient::at_local_socket(parent_addr_, {0, boost::none});
+        parent_ = RobotClient::at_local_socket(parent_addr_, {0, boost::none}, registry_);
     }
 
     return parent_->resource_by_name(name);
 }
 
-ModuleService::ModuleService(std::string addr)
-    : module_(std::make_unique<Module>(std::move(addr))), server_(std::make_unique<Server>()) {
+ModuleService::ModuleService(std::string addr, Registry& registry)
+    : registry_(registry),
+      module_(std::make_unique<Module>(std::move(addr))),
+      server_(std::make_unique<Server>(&registry_)) {
     impl_ = std::make_unique<ServiceImpl>(*this);
 }
 
 ModuleService::ModuleService(int argc,
                              char** argv,
-                             const std::vector<std::shared_ptr<ModelRegistration>>& registrations)
-    : ModuleService([argc, argv] {
-          if (argc < 2) {
-              throw Exception(ErrorCondition::k_connection,
-                              "Need socket path as command line argument");
-          }
-          return argv[1];
-      }()) {
+                             const std::vector<std::shared_ptr<ModelRegistration>>& registrations,
+                             Registry& registry)
+    : ModuleService(
+          [argc, argv] {
+              if (argc < 2) {
+                  throw Exception(ErrorCondition::k_connection,
+                                  "Need socket path as command line argument");
+              }
+              return argv[1];
+          }(),
+          registry) {
     set_logger_severity_from_args(argc, argv);
 
     for (auto&& mr : registrations) {
-        Registry::register_model(mr);
+        registry_.register_model(mr);
         add_model_from_registry(mr->api(), mr->model());
     }
 }
@@ -274,7 +280,7 @@ void ModuleService::add_model_from_registry_inlock_(API api,
                                                     Model model,
                                                     const std::lock_guard<std::mutex>&) {
     const std::shared_ptr<const ResourceServerRegistration> creator =
-        Registry::lookup_resource_server(api);
+        registry_.lookup_resource_server(api);
     std::string name;
     if (creator && creator->service_descriptor()) {
         name = creator->service_descriptor()->full_name();
diff --git a/src/viam/sdk/module/service.hpp b/src/viam/sdk/module/service.hpp
index 799ba1ab5..cffc47fe7 100644
--- a/src/viam/sdk/module/service.hpp
+++ b/src/viam/sdk/module/service.hpp
@@ -31,7 +31,7 @@ class ModuleService {
    public:
     /// @brief Creates a new ModuleService that can serve on the provided socket.
     /// @param addr Address of socket to serve on.
-    explicit ModuleService(std::string addr);
+    explicit ModuleService(std::string addr, Registry& registry);
 
     /// @brief Creates a new ModuleService. Socket path and log level will be
     /// inferred from passed in command line arguments, and passed in model
@@ -41,7 +41,8 @@ class ModuleService {
     /// @param registrations Models to register and add to the module.
     explicit ModuleService(int argc,
                            char** argv,
-                           const std::vector<std::shared_ptr<ModelRegistration>>& registrations);
+                           const std::vector<std::shared_ptr<ModelRegistration>>& registrations,
+                           Registry& registry);
     ~ModuleService();
 
     /// @brief Starts module. serve will return when SIGINT or SIGTERM is received
@@ -64,6 +65,8 @@ class ModuleService {
                                    std::string const& resource_name);
     std::shared_ptr<Resource> get_parent_resource_(const Name& name);
 
+    Registry& registry_;
+
     std::mutex lock_;
     std::unique_ptr<Module> module_;
     std::shared_ptr<RobotClient> parent_;
diff --git a/src/viam/sdk/registry/registry.cpp b/src/viam/sdk/registry/registry.cpp
index 57e857f00..71e3e7711 100644
--- a/src/viam/sdk/registry/registry.cpp
+++ b/src/viam/sdk/registry/registry.cpp
@@ -122,7 +122,7 @@ void Registry::register_resource_client_(
 }
 
 std::shared_ptr<const ModelRegistration> Registry::lookup_model_inlock_(
-    const std::string& name, const std::lock_guard<std::mutex>&) {
+    const std::string& name, const std::lock_guard<std::mutex>&) const {
     if (resources_.find(name) == resources_.end()) {
         return nullptr;
     }
@@ -130,19 +130,20 @@ std::shared_ptr<const ModelRegistration> Registry::lookup_model_inlock_(
     return resources_.at(name);
 }
 
-std::shared_ptr<const ModelRegistration> Registry::lookup_model(const std::string& name) {
+std::shared_ptr<const ModelRegistration> Registry::lookup_model(const std::string& name) const {
     const std::lock_guard<std::mutex> lock(lock_);
     return lookup_model_inlock_(name, lock);
 }
 
 std::shared_ptr<const ModelRegistration> Registry::lookup_model(const API& api,
-                                                                const Model& model) {
+                                                                const Model& model) const {
     const std::lock_guard<std::mutex> lock(lock_);
     const std::string name = api.to_string() + "/" + model.to_string();
     return lookup_model_inlock_(name, lock);
 }
 
-std::shared_ptr<const ResourceServerRegistration> Registry::lookup_resource_server(const API& api) {
+std::shared_ptr<const ResourceServerRegistration> Registry::lookup_resource_server(
+    const API& api) const {
     const std::lock_guard<std::mutex> lock(lock_);
     if (server_apis_.find(api) == server_apis_.end()) {
         return nullptr;
@@ -151,7 +152,8 @@ std::shared_ptr<const ResourceServerRegistration> Registry::lookup_resource_serv
     return server_apis_.at(api);
 }
 
-std::shared_ptr<const ResourceClientRegistration> Registry::lookup_resource_client(const API& api) {
+std::shared_ptr<const ResourceClientRegistration> Registry::lookup_resource_client(
+    const API& api) const {
     const std::lock_guard<std::mutex> lock(lock_);
     if (client_apis_.find(api) == client_apis_.end()) {
         return nullptr;
@@ -161,7 +163,7 @@ std::shared_ptr<const ResourceClientRegistration> Registry::lookup_resource_clie
 }
 
 const google::protobuf::ServiceDescriptor* Registry::get_service_descriptor_(
-    const char* service_full_name) {
+    const char* service_full_name) const {
     const google::protobuf::DescriptorPool* p = google::protobuf::DescriptorPool::generated_pool();
     const google::protobuf::ServiceDescriptor* sd = p->FindServiceByName(service_full_name);
     if (!sd) {
@@ -171,12 +173,12 @@ const google::protobuf::ServiceDescriptor* Registry::get_service_descriptor_(
 }
 
 const std::unordered_map<API, std::shared_ptr<const ResourceServerRegistration>>&
-Registry::registered_resource_servers() {
+Registry::registered_resource_servers() const {
     return server_apis_;
 }
 
 const std::unordered_map<std::string, std::shared_ptr<const ModelRegistration>>&
-Registry::registered_models() {
+Registry::registered_models() const {
     return resources_;
 }
 
@@ -184,28 +186,28 @@ const google::protobuf::ServiceDescriptor* ResourceServerRegistration::service_d
     return service_descriptor_;
 }
 
-void register_resources() {
+void Registry::register_resources() {
     // Register all components
-    Registry::register_resource<impl::ArmClient, impl::ArmServer>();
-    Registry::register_resource<impl::BaseClient, impl::BaseServer>();
-    Registry::register_resource<impl::BoardClient, impl::BoardServer>();
-    Registry::register_resource<impl::CameraClient, impl::CameraServer>();
-    Registry::register_resource<impl::EncoderClient, impl::EncoderServer>();
-    Registry::register_resource<impl::GantryClient, impl::GantryServer>();
-    Registry::register_resource<impl::GenericComponentClient, impl::GenericComponentServer>();
-    Registry::register_resource<impl::GripperClient, impl::GripperServer>();
-    Registry::register_resource<impl::MotorClient, impl::MotorServer>();
-    Registry::register_resource<impl::MovementSensorClient, impl::MovementSensorServer>();
-    Registry::register_resource<impl::PoseTrackerClient, impl::PoseTrackerServer>();
-    Registry::register_resource<impl::PowerSensorClient, impl::PowerSensorServer>();
-    Registry::register_resource<impl::SensorClient, impl::SensorServer>();
-    Registry::register_resource<impl::ServoClient, impl::ServoServer>();
+    register_resource<impl::ArmClient, impl::ArmServer>();
+    register_resource<impl::BaseClient, impl::BaseServer>();
+    register_resource<impl::BoardClient, impl::BoardServer>();
+    register_resource<impl::CameraClient, impl::CameraServer>();
+    register_resource<impl::EncoderClient, impl::EncoderServer>();
+    register_resource<impl::GantryClient, impl::GantryServer>();
+    register_resource<impl::GenericComponentClient, impl::GenericComponentServer>();
+    register_resource<impl::GripperClient, impl::GripperServer>();
+    register_resource<impl::MotorClient, impl::MotorServer>();
+    register_resource<impl::MovementSensorClient, impl::MovementSensorServer>();
+    register_resource<impl::PoseTrackerClient, impl::PoseTrackerServer>();
+    register_resource<impl::PowerSensorClient, impl::PowerSensorServer>();
+    register_resource<impl::SensorClient, impl::SensorServer>();
+    register_resource<impl::ServoClient, impl::ServoServer>();
 
     // Register all services
-    Registry::register_resource<impl::GenericServiceClient, impl::GenericServiceServer>();
-    Registry::register_resource<impl::MLModelServiceClient, impl::MLModelServiceServer>();
-    Registry::register_resource<impl::MotionClient, impl::MotionServer>();
-    Registry::register_resource<impl::NavigationClient, impl::NavigationServer>();
+    register_resource<impl::GenericServiceClient, impl::GenericServiceServer>();
+    register_resource<impl::MLModelServiceClient, impl::MLModelServiceServer>();
+    register_resource<impl::MotionClient, impl::MotionServer>();
+    register_resource<impl::NavigationClient, impl::NavigationServer>();
 }
 
 void Registry::initialize() {
@@ -220,11 +222,5 @@ void Registry::initialize() {
     grpc::reflection::InitProtoReflectionServerBuilderPlugin();
 }
 
-std::unordered_map<std::string, std::shared_ptr<const ModelRegistration>> Registry::resources_;
-std::unordered_map<API, std::shared_ptr<const ResourceClientRegistration>> Registry::client_apis_;
-std::unordered_map<API, std::shared_ptr<const ResourceServerRegistration>> Registry::server_apis_;
-std::mutex Registry::lock_;
-bool Registry::initialized_{false};
-
 }  // namespace sdk
 }  // namespace viam
diff --git a/src/viam/sdk/registry/registry.hpp b/src/viam/sdk/registry/registry.hpp
index b00d98833..4431575d6 100644
--- a/src/viam/sdk/registry/registry.hpp
+++ b/src/viam/sdk/registry/registry.hpp
@@ -106,23 +106,22 @@ class Registry {
     /// @brief Registers a resource with the Registry.
     /// @param resource An object containing resource registration information.
     /// @throws `Exception` if the resource has already been registered.
-    static void register_model(std::shared_ptr<const ModelRegistration> resource);
+    void register_model(std::shared_ptr<const ModelRegistration> resource);
 
     /// @brief Lookup a given registered resource.
     /// @param name The name of the resource to lookup.
     /// @return a `shared_ptr` to the resource's registration data.
-    static std::shared_ptr<const ModelRegistration> lookup_model(const std::string& name);
+    std::shared_ptr<const ModelRegistration> lookup_model(const std::string& name) const;
 
     /// @brief Lookup a given registered resource.
     /// @param api The api of the resource to lookup.
     /// @param model The model of the resource to lookup.
     /// @return a `shared_ptr` to the resource's registration data.
-    static std::shared_ptr<const ModelRegistration> lookup_model(const API& api,
-                                                                 const Model& model);
+    std::shared_ptr<const ModelRegistration> lookup_model(const API& api, const Model& model) const;
 
     /// @brief Register a resource client constructor
     template <typename ResourceClientT>
-    static void register_resource_client() {
+    void register_resource_client() {
         class ResourceClientRegistration2 final : public ResourceClientRegistration {
            public:
             using ResourceClientRegistration::ResourceClientRegistration;
@@ -139,7 +138,7 @@ class Registry {
 
     /// @brief Register a resource server constructor.
     template <typename ResourceServerT>
-    static void register_resource_server() {
+    void register_resource_server() {
         class ResourceServerRegistration2 final : public ResourceServerRegistration {
            public:
             using ResourceServerRegistration::ResourceServerRegistration;
@@ -159,7 +158,7 @@ class Registry {
 
     /// @brief Register resource client and server constructors
     template <typename ResourceClientT, typename ResourceServerT>
-    static void register_resource() {
+    void register_resource() {
         register_resource_client<ResourceClientT>();
         register_resource_server<ResourceServerT>();
     }
@@ -167,44 +166,46 @@ class Registry {
     /// @brief Lookup a registered server api.
     /// @param api The api to lookup.
     /// @return A `shared_ptr` to the registered api's `ResourceServerRegistration`.
-    static std::shared_ptr<const ResourceServerRegistration> lookup_resource_server(const API& api);
+    std::shared_ptr<const ResourceServerRegistration> lookup_resource_server(const API& api) const;
 
     /// @brief Lookup a registered client api.
     /// @param api The api to lookup.
     /// @return A `shared_ptr` to the registered api's `ResourceClientRegistration`.
-    static std::shared_ptr<const ResourceClientRegistration> lookup_resource_client(const API& api);
+    std::shared_ptr<const ResourceClientRegistration> lookup_resource_client(const API& api) const;
 
     /// @brief Provide information on registered resource models.
     /// @return A map from name to `ModelRegistration` of all registered resource models.
-    static const std::unordered_map<std::string, std::shared_ptr<const ModelRegistration>>&
-    registered_models();
+    const std::unordered_map<std::string, std::shared_ptr<const ModelRegistration>>&
+    registered_models() const;
 
     /// @brief Provide access to registered resources.
     /// @return A map from `API` to `ResourceServerRegistration` of all registered resources.
-    static const std::unordered_map<API, std::shared_ptr<const ResourceServerRegistration>>&
-    registered_resource_servers();
+    const std::unordered_map<API, std::shared_ptr<const ResourceServerRegistration>>&
+    registered_resource_servers() const;
 
     /// @brief Initialized the Viam registry. No-op if it has already been called.
-    static void initialize();
+    void initialize();
 
    private:
-    static std::mutex lock_;
-    static bool initialized_;
-    static std::unordered_map<std::string, std::shared_ptr<const ModelRegistration>> resources_;
-    static std::unordered_map<API, std::shared_ptr<const ResourceClientRegistration>> client_apis_;
-    static std::unordered_map<API, std::shared_ptr<const ResourceServerRegistration>> server_apis_;
+    mutable std::mutex lock_;
+    bool initialized_{false};
+    std::unordered_map<std::string, std::shared_ptr<const ModelRegistration>> resources_;
+    std::unordered_map<API, std::shared_ptr<const ResourceClientRegistration>> client_apis_;
+    std::unordered_map<API, std::shared_ptr<const ResourceServerRegistration>> server_apis_;
 
-    static void register_resource_server_(
+    void register_resources();
+
+    void register_resource_server_(
         API api, std::shared_ptr<ResourceServerRegistration> resource_registration);
 
-    static void register_resource_client_(
+    void register_resource_client_(
         API api, std::shared_ptr<ResourceClientRegistration> resource_registration);
 
-    static const google::protobuf::ServiceDescriptor* get_service_descriptor_(
-        const char* service_full_name);
+    const google::protobuf::ServiceDescriptor* get_service_descriptor_(
+        const char* service_full_name) const;
 
-    static std::shared_ptr<const ModelRegistration> lookup_model_inlock_(
-        const std::string& name, const std::lock_guard<std::mutex>&);
+    std::shared_ptr<const ModelRegistration> lookup_model_inlock_(
+        const std::string& name, const std::lock_guard<std::mutex>&) const;
 };
 
 }  // namespace sdk
diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp
index fff691fd2..6ac049eba 100644
--- a/src/viam/sdk/robot/client.cpp
+++ b/src/viam/sdk/robot/client.cpp
@@ -178,7 +178,7 @@ void RobotClient::refresh() {
         // are being properly registered from name.subtype(), or update what we're
         // using for lookup
         const std::shared_ptr<const ResourceClientRegistration> rs =
-            Registry::lookup_resource_client({name.namespace_(), name.type(), name.subtype()});
+            registry_.lookup_resource_client({name.namespace_(), name.type(), name.subtype()});
         if (rs) {
             try {
                 const std::shared_ptr<Resource> rpc_client =
@@ -221,13 +221,12 @@ void RobotClient::refresh_every() {
     }
 };
 
-RobotClient::RobotClient(std::shared_ptr<ViamChannel> channel)
-    : channel_(channel->channel()),
+RobotClient::RobotClient(std::shared_ptr<ViamChannel> channel, Registry& registry)
+    : registry_(registry),
+      channel_(channel->channel()),
       viam_channel_(std::move(channel)),
       should_close_channel_(false),
-      impl_(std::make_unique<impl>(RobotService::NewStub(channel_))) {
-    Registry::initialize();
-}
+      impl_(std::make_unique<impl>(RobotService::NewStub(channel_))) {}
 
 std::vector<Name> RobotClient::resource_names() const {
     const std::lock_guard<std::mutex> lock(lock_);
@@ -235,8 +234,10 @@ std::vector<Name> RobotClient::resource_names() const {
 }
 
 std::shared_ptr<RobotClient> RobotClient::with_channel(std::shared_ptr<ViamChannel> channel,
-                                                       const Options& options) {
-    std::shared_ptr<RobotClient> robot = std::make_shared<RobotClient>(std::move(channel));
+                                                       const Options& options,
+                                                       Registry& registry) {
+    std::shared_ptr<RobotClient> robot =
+        std::make_shared<RobotClient>(std::move(channel), registry);
     robot->refresh_interval_ = options.refresh_interval();
     robot->should_refresh_ = (robot->refresh_interval_ > 0);
     if (robot->should_refresh_) {
@@ -254,23 +255,25 @@ std::shared_ptr<RobotClient> RobotClient::with_channel(std::shared_ptr<ViamChann
 };
 
 std::shared_ptr<RobotClient> RobotClient::at_address(const std::string& address,
-                                                     const Options& options) {
+                                                     const Options& options,
+                                                     Registry& registry) {
     const char* uri = address.c_str();
     auto channel = ViamChannel::dial(uri, options.dial_options());
-    std::shared_ptr<RobotClient> robot = RobotClient::with_channel(channel, options);
+    std::shared_ptr<RobotClient> robot = RobotClient::with_channel(channel, options, registry);
     robot->should_close_channel_ = true;
 
     return robot;
 };
 
 std::shared_ptr<RobotClient> RobotClient::at_local_socket(const std::string& address,
-                                                          const Options& options) {
+                                                          const Options& options,
+                                                          Registry& registry) {
     const std::string addr = "unix://" + address;
     const char* uri = addr.c_str();
     const std::shared_ptr<grpc::Channel> channel =
         sdk::impl::create_viam_channel(uri, grpc::InsecureChannelCredentials());
     auto viam_channel = std::make_shared<ViamChannel>(channel, address.c_str(), nullptr);
-    std::shared_ptr<RobotClient> robot = RobotClient::with_channel(viam_channel, options);
+    std::shared_ptr<RobotClient> robot = RobotClient::with_channel(viam_channel, options, registry);
     robot->should_close_channel_ = true;
 
     return robot;
diff --git a/src/viam/sdk/robot/client.hpp b/src/viam/sdk/robot/client.hpp
index 9d63bd14b..09834e1d8 100644
--- a/src/viam/sdk/robot/client.hpp
+++ b/src/viam/sdk/robot/client.hpp
@@ -68,7 +68,8 @@ class RobotClient {
     /// @param address The address of the robot (IP address, URI, URL, etc.)
     /// @param options Options for connecting and refreshing.
     static std::shared_ptr<RobotClient> at_address(const std::string& address,
-                                                   const Options& options);
+                                                   const Options& options,
+                                                   Registry& registry);
 
     /// @brief Creates a robot client connected to the robot at the provided local socket.
     /// @param address The local socket of the robot (a .sock file, etc.).
@@ -76,7 +77,8 @@ class RobotClient {
     /// Creates a direct connection to the robot using the `unix://` scheme.
     /// Only useful for connecting to robots across Unix sockets.
     static std::shared_ptr<RobotClient> at_local_socket(const std::string& address,
-                                                        const Options& options);
+                                                        const Options& options,
+                                                        Registry& registry);
 
     /// @brief Creates a robot client connected to the provided channel.
     /// @param channel The channel to connect with.
@@ -84,8 +86,11 @@ class RobotClient {
     /// Connects directly to a pre-existing channel. A robot created this way must be
     /// `close()`d manually.
     static std::shared_ptr<RobotClient> with_channel(std::shared_ptr<ViamChannel> channel,
-                                                     const Options& options);
-    RobotClient(std::shared_ptr<ViamChannel> channel);
+                                                     const Options& options,
+                                                     Registry& registry);
+
+    RobotClient(std::shared_ptr<ViamChannel> channel, Registry& registry);
+
     std::vector<Name> resource_names() const;
 
     /// @brief Lookup and return a `shared_ptr` to a resource.
@@ -147,17 +152,25 @@ class RobotClient {
     status get_machine_status() const;
 
    private:
+    Registry& registry_;
+
     std::vector<std::shared_ptr<std::thread>> threads_;
+
     std::atomic<bool> should_refresh_;
     unsigned int refresh_interval_;
+
     std::shared_ptr<GrpcChannel> channel_;
     std::shared_ptr<ViamChannel> viam_channel_;
     bool should_close_channel_;
+
     struct impl;
     std::unique_ptr<impl> impl_;
+
     mutable std::mutex lock_;
+
     std::vector<Name> resource_names_;
     ResourceManager resource_manager_;
+
     void refresh_every();
 };
 
diff --git a/src/viam/sdk/rpc/server.cpp b/src/viam/sdk/rpc/server.cpp
index 408f23bf6..b2c46c695 100644
--- a/src/viam/sdk/rpc/server.cpp
+++ b/src/viam/sdk/rpc/server.cpp
@@ -15,12 +15,11 @@
 namespace viam {
 namespace sdk {
 
-Server::Server() : builder_(std::make_unique<grpc::ServerBuilder>()) {
+Server::Server(const Registry* registry) : builder_(std::make_unique<grpc::ServerBuilder>()) {
     builder_->SetMaxReceiveMessageSize(kMaxMessageSize);
     builder_->SetMaxSendMessageSize(kMaxMessageSize);
     builder_->SetMaxMessageSize(kMaxMessageSize);
-    Registry::initialize();
-    for (const auto& rr : Registry::registered_resource_servers()) {
+    for (const auto& rr : registry->registered_resource_servers()) {
         auto new_manager = std::make_shared<ResourceManager>();
         auto server = rr.second->create_resource_server(new_manager, *this);
         managed_servers_.emplace(rr.first, std::move(server));
diff --git a/src/viam/sdk/rpc/server.hpp b/src/viam/sdk/rpc/server.hpp
index e735a6351..5f868b779 100644
--- a/src/viam/sdk/rpc/server.hpp
+++ b/src/viam/sdk/rpc/server.hpp
@@ -23,11 +23,13 @@ class TestServer;
 
 namespace sdk {
 
+class Registry;
+
 /// @class Server server.hpp "rpc/server.hpp"
 /// @brief Defines gRPC `Server` functionality.
 class Server {
    public:
-    Server();
+    Server(const Registry* registry);
     ~Server();
 
     /// @brief Starts the grpc server. Can only be called once.

From 04d0158412c31836d226576668c90ce36234d4f0 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 18 Feb 2025 14:14:58 -0500
Subject: [PATCH 03/86] add missing implementation

---
 src/viam/sdk/common/instance.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/viam/sdk/common/instance.cpp b/src/viam/sdk/common/instance.cpp
index 1ec93d01f..3c277273d 100644
--- a/src/viam/sdk/common/instance.cpp
+++ b/src/viam/sdk/common/instance.cpp
@@ -7,5 +7,9 @@ Instance::Instance() {
     registry_.initialize();
 }
 
+Registry& Instance::registry() {
+    return registry_;
+}
+
 }  // namespace sdk
 }  // namespace viam

From a105cef7426252039a7917153e0a34d3afc0a5a2 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 18 Feb 2025 14:15:56 -0500
Subject: [PATCH 04/86] update examples to use instance

---
 src/viam/examples/modules/simple/client.cpp | 5 ++++-
 src/viam/examples/modules/simple/main.cpp   | 5 ++++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/viam/examples/modules/simple/client.cpp b/src/viam/examples/modules/simple/client.cpp
index fcaab5eff..ac014d1e7 100644
--- a/src/viam/examples/modules/simple/client.cpp
+++ b/src/viam/examples/modules/simple/client.cpp
@@ -2,6 +2,7 @@
 #include <memory>
 #include <string>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/components/sensor.hpp>
 #include <viam/sdk/robot/client.hpp>
@@ -10,6 +11,8 @@
 using namespace viam::sdk;
 
 int main() {
+    Instance inst;
+
     const char* uri = "http://localhost:8080/";  // replace with your URI if connecting securely
     DialOptions dial_options;
     dial_options.set_allow_insecure_downgrade(true);  // set to false if connecting securely
@@ -24,7 +27,7 @@ int main() {
     std::string address(uri);
     Options options(1, opts);
 
-    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options);
+    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options, inst.registry());
 
     // Print resources
     std::cout << "Resources\n";
diff --git a/src/viam/examples/modules/simple/main.cpp b/src/viam/examples/modules/simple/main.cpp
index cfd783f66..b899d3b44 100644
--- a/src/viam/examples/modules/simple/main.cpp
+++ b/src/viam/examples/modules/simple/main.cpp
@@ -3,6 +3,7 @@
 #include <sstream>
 
 #include <viam/sdk/common/exception.hpp>
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/components/sensor.hpp>
 #include <viam/sdk/config/resource.hpp>
@@ -75,6 +76,8 @@ ProtoStruct MySensor::get_readings(const ProtoStruct&) {
 }
 
 int main(int argc, char** argv) try {
+    Instance inst;
+
     Model mysensor_model("viam", "sensor", "mysensor");
 
     std::shared_ptr<ModelRegistration> mr = std::make_shared<ModelRegistration>(
@@ -84,7 +87,7 @@ int main(int argc, char** argv) try {
         &MySensor::validate);
 
     std::vector<std::shared_ptr<ModelRegistration>> mrs = {mr};
-    auto my_mod = std::make_shared<ModuleService>(argc, argv, mrs);
+    auto my_mod = std::make_shared<ModuleService>(argc, argv, mrs, inst.registry());
     my_mod->serve();
 
     return EXIT_SUCCESS;

From 403f31610d1b95a705b466fad0b0873934acec0c Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 18 Feb 2025 16:21:21 -0500
Subject: [PATCH 05/86] only instance can construct registry

---
 src/viam/sdk/registry/registry.hpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/viam/sdk/registry/registry.hpp b/src/viam/sdk/registry/registry.hpp
index 4431575d6..a98e05c6e 100644
--- a/src/viam/sdk/registry/registry.hpp
+++ b/src/viam/sdk/registry/registry.hpp
@@ -187,6 +187,10 @@ class Registry {
     void initialize();
 
    private:
+    friend class Instance;
+
+    Registry() = default;
+
     mutable std::mutex lock_;
     bool initialized_{false};
     std::unordered_map<std::string, std::shared_ptr<const ModelRegistration>> resources_;

From 27eeb1331c28810243859a7c5f0f5606a4e69ba6 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 21 Feb 2025 12:55:42 -0500
Subject: [PATCH 06/86] make tests work with registry member approach

---
 src/viam/sdk/registry/registry.hpp      |  6 +++---
 src/viam/sdk/tests/mocks/mock_robot.cpp |  2 +-
 src/viam/sdk/tests/test_robot.cpp       | 19 +++++++++++--------
 src/viam/sdk/tests/test_utils.hpp       | 17 +++++++++++++++--
 4 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/src/viam/sdk/registry/registry.hpp b/src/viam/sdk/registry/registry.hpp
index a98e05c6e..75e2324e3 100644
--- a/src/viam/sdk/registry/registry.hpp
+++ b/src/viam/sdk/registry/registry.hpp
@@ -183,14 +183,14 @@ class Registry {
     const std::unordered_map<API, std::shared_ptr<const ResourceServerRegistration>>&
     registered_resource_servers() const;
 
-    /// @brief Initialized the Viam registry. No-op if it has already been called.
-    void initialize();
-
    private:
     friend class Instance;
 
     Registry() = default;
 
+    /// @brief Initialized the Viam registry. No-op if it has already been called.
+    void initialize();
+
     mutable std::mutex lock_;
     bool initialized_{false};
     std::unordered_map<std::string, std::shared_ptr<const ModelRegistration>> resources_;
diff --git a/src/viam/sdk/tests/mocks/mock_robot.cpp b/src/viam/sdk/tests/mocks/mock_robot.cpp
index cdef0fe78..8e28b944b 100644
--- a/src/viam/sdk/tests/mocks/mock_robot.cpp
+++ b/src/viam/sdk/tests/mocks/mock_robot.cpp
@@ -26,7 +26,7 @@ std::vector<Name> registered_models_for_resource(const std::shared_ptr<Resource>
     std::string resource_type;
     std::string resource_subtype;
     std::vector<Name> resource_names;
-    for (const auto& kv : Registry::registered_models()) {
+    for (const auto& kv : GlobalInstance::registry().registered_models()) {
         const std::shared_ptr<const ModelRegistration> reg = kv.second;
         if (reg->api() == resource->api()) {
             resource_type = reg->api().resource_type();
diff --git a/src/viam/sdk/tests/test_robot.cpp b/src/viam/sdk/tests/test_robot.cpp
index 9f1093d49..e7ccc0de1 100644
--- a/src/viam/sdk/tests/test_robot.cpp
+++ b/src/viam/sdk/tests/test_robot.cpp
@@ -42,7 +42,7 @@ void robot_client_to_mocks_pipeline(F&& test_case) {
     rm->add(std::string("mock_generic"), generic::MockGenericComponent::get_mock_generic());
     rm->add(std::string("mock_motor"), motor::MockMotor::get_mock_motor());
     rm->add(std::string("mock_camera"), camera::MockCamera::get_mock_camera());
-    auto server = std::make_shared<sdk::Server>();
+    auto server = std::make_shared<sdk::Server>(&GlobalInstance::registry());
     MockRobotService service(rm, *server);
     server->start();
 
@@ -51,7 +51,8 @@ void robot_client_to_mocks_pipeline(F&& test_case) {
     auto test_server = TestServer(server);
     auto grpc_channel = test_server.grpc_in_process_channel();
     auto viam_channel = std::make_shared<ViamChannel>(grpc_channel, "", nullptr);
-    auto client = RobotClient::with_channel(viam_channel, Options(0, boost::none));
+    auto client = RobotClient::with_channel(
+        viam_channel, Options(0, boost::none), GlobalInstance::registry());
 
     // Run the passed-in test case on the created stack and give access to the
     // created RobotClient and MockRobotService.
@@ -62,6 +63,8 @@ void robot_client_to_mocks_pipeline(F&& test_case) {
 }
 
 BOOST_AUTO_TEST_CASE(test_registering_resources) {
+    auto& registry = GlobalInstance::registry();
+
     // To test with mock resources we need to be able to create them, which means registering
     // constructors. This tests that we register correctly.
     Model camera_model("fake", "fake", "mock_camera");
@@ -70,7 +73,7 @@ BOOST_AUTO_TEST_CASE(test_registering_resources) {
         camera_model,
         [](Dependencies, ResourceConfig cfg) { return camera::MockCamera::get_mock_camera(); },
         [](ResourceConfig cfg) -> std::vector<std::string> { return {}; });
-    Registry::register_model(cr);
+    registry.register_model(cr);
 
     Model generic_model("fake", "fake", "mock_generic");
     std::shared_ptr<ModelRegistration> gr = std::make_shared<ModelRegistration>(
@@ -80,7 +83,7 @@ BOOST_AUTO_TEST_CASE(test_registering_resources) {
             return generic::MockGenericComponent::get_mock_generic();
         },
         [](ResourceConfig cfg) -> std::vector<std::string> { return {}; });
-    Registry::register_model(gr);
+    registry.register_model(gr);
 
     Model motor_model("fake", "fake", "mock_motor");
     std::shared_ptr<ModelRegistration> mr = std::make_shared<ModelRegistration>(
@@ -88,11 +91,11 @@ BOOST_AUTO_TEST_CASE(test_registering_resources) {
         motor_model,
         [](Dependencies, ResourceConfig cfg) { return motor::MockMotor::get_mock_motor(); },
         [](ResourceConfig cfg) -> std::vector<std::string> { return {}; });
-    Registry::register_model(mr);
+    registry.register_model(mr);
 
-    BOOST_CHECK(Registry::lookup_model(API::get<Camera>(), camera_model));
-    BOOST_CHECK(Registry::lookup_model(API::get<GenericComponent>(), generic_model));
-    BOOST_CHECK(Registry::lookup_model(API::get<Motor>(), motor_model));
+    BOOST_CHECK(registry.lookup_model(API::get<Camera>(), camera_model));
+    BOOST_CHECK(registry.lookup_model(API::get<GenericComponent>(), generic_model));
+    BOOST_CHECK(registry.lookup_model(API::get<Motor>(), motor_model));
 }
 
 BOOST_AUTO_TEST_CASE(test_resource_names) {
diff --git a/src/viam/sdk/tests/test_utils.hpp b/src/viam/sdk/tests/test_utils.hpp
index 400bc54a2..c24bed482 100644
--- a/src/viam/sdk/tests/test_utils.hpp
+++ b/src/viam/sdk/tests/test_utils.hpp
@@ -2,6 +2,7 @@
 
 #include <grpcpp/grpcpp.h>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/config/resource.hpp>
 #include <viam/sdk/registry/registry.hpp>
 #include <viam/sdk/resource/resource.hpp>
@@ -10,6 +11,17 @@
 namespace viam {
 namespace sdktests {
 
+struct GlobalInstance {
+    static sdk::Instance& get() {
+        static sdk::Instance inst;
+        return inst;
+    }
+
+    static sdk::Registry& registry() {
+        return get().registry();
+    }
+};
+
 using namespace viam::sdk;
 
 ProtoStruct fake_map();
@@ -49,7 +61,7 @@ class TestServer {
 // The passed in test_case function will have access to the created ResourceClient.
 template <typename ResourceType, typename F>
 void client_to_mock_pipeline(std::shared_ptr<Resource> mock, F&& test_case) {
-    auto server = std::make_shared<sdk::Server>();
+    auto server = std::make_shared<sdk::Server>(&GlobalInstance::registry());
 
     // normally the high level server service (either robot or module) handles adding managed
     // resources, but in this case we must do it ourselves.
@@ -61,7 +73,8 @@ void client_to_mock_pipeline(std::shared_ptr<Resource> mock, F&& test_case) {
     auto test_server = TestServer(server);
     auto grpc_channel = test_server.grpc_in_process_channel();
 
-    auto resource_client = Registry::lookup_resource_client(API::get<ResourceType>())
+    auto resource_client = GlobalInstance::registry()
+                               .lookup_resource_client(API::get<ResourceType>())
                                ->create_rpc_client(mock->name(), std::move(grpc_channel));
 
     // Run the passed-in test case on the created stack and give access to the

From ce2aa8c54852cb2e88ff53dbc831331dd0081e02 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 24 Feb 2025 16:17:18 -0500
Subject: [PATCH 07/86] pimpl registry and update examples

---
 src/viam/examples/camera/example_camera.cpp      |  8 +++++++-
 src/viam/examples/dial/example_dial.cpp          |  7 ++++++-
 .../dial_api_key/example_dial_api_key.cpp        |  7 ++++++-
 .../example_audio_classification_client.cpp      |  9 +++++++--
 src/viam/examples/modules/complex/client.cpp     | 11 ++++++++---
 src/viam/examples/modules/complex/main.cpp       | 11 ++++++++---
 .../modules/complex/test_complex_module.cpp      |  5 +++--
 src/viam/examples/motor/example_motor.cpp        |  8 +++++++-
 src/viam/sdk/common/instance.cpp                 | 16 ++++++++++++----
 src/viam/sdk/common/instance.hpp                 | 10 +++++++---
 src/viam/sdk/module/service.cpp                  | 16 ++++++++--------
 src/viam/sdk/module/service.hpp                  |  6 +++---
 src/viam/sdk/registry/registry.hpp               |  1 -
 src/viam/sdk/robot/client.cpp                    | 10 +++++-----
 src/viam/sdk/robot/client.hpp                    | 14 ++++++++------
 src/viam/sdk/tests/mocks/mock_robot.cpp          |  2 +-
 src/viam/sdk/tests/test_robot.cpp                |  4 ++--
 src/viam/sdk/tests/test_utils.hpp                |  6 +++---
 18 files changed, 101 insertions(+), 50 deletions(-)

diff --git a/src/viam/examples/camera/example_camera.cpp b/src/viam/examples/camera/example_camera.cpp
index bc1b0ab9a..b0202a27e 100644
--- a/src/viam/examples/camera/example_camera.cpp
+++ b/src/viam/examples/camera/example_camera.cpp
@@ -3,6 +3,7 @@
 #include <unistd.h>
 #include <vector>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/camera.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -13,6 +14,11 @@ int main() {
     using std::endl;
     namespace vs = ::viam::sdk;
     try {
+        // Every Viam C++ SDK program must have one and only one Instance object which is created
+        // before
+        // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+        vs::Instance inst;
+
         // If you want to connect to a remote robot, this should be the url of the robot
         // Ex: xxx.xxx.viam.cloud
         std::string robot_address("localhost:8080");
@@ -35,7 +41,7 @@ int main() {
 
         std::shared_ptr<vs::RobotClient> robot;
         try {
-            robot = vs::RobotClient::at_address(robot_address, options);
+            robot = vs::RobotClient::at_address(robot_address, options, inst.registry());
             cout << "Successfully connected to the robot" << endl;
         } catch (const std::exception& e) {
             cerr << "Failed to connect to the robot. Exiting." << endl;
diff --git a/src/viam/examples/dial/example_dial.cpp b/src/viam/examples/dial/example_dial.cpp
index efa783dfb..28ccd7659 100644
--- a/src/viam/examples/dial/example_dial.cpp
+++ b/src/viam/examples/dial/example_dial.cpp
@@ -9,6 +9,7 @@
 
 #include <boost/optional.hpp>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/generic.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -16,6 +17,10 @@
 using namespace viam::sdk;
 
 int main() {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    Instance inst;
+
     const char* uri = "<your robot URI here>";
     DialOptions dial_options;
     std::string type = "<your authentication type>";
@@ -27,7 +32,7 @@ int main() {
     Options options(1, opts);
 
     // connect to robot, ensure we can refresh it
-    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options);
+    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options, inst.registry());
 
     // ensure we can query resources
     std::vector<Name> resource_names = robot->resource_names();
diff --git a/src/viam/examples/dial_api_key/example_dial_api_key.cpp b/src/viam/examples/dial_api_key/example_dial_api_key.cpp
index 4c47beffe..7c9e82fef 100644
--- a/src/viam/examples/dial_api_key/example_dial_api_key.cpp
+++ b/src/viam/examples/dial_api_key/example_dial_api_key.cpp
@@ -10,6 +10,7 @@
 #include <boost/optional.hpp>
 #include <boost/program_options.hpp>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
 
@@ -18,6 +19,10 @@ using namespace viam::sdk;
 namespace po = boost::program_options;
 
 int main(int argc, char* argv[]) {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    Instance inst;
+
     po::options_description desc("Allowed options");
     desc.add_options()("help", "List options and exit")(
         "uri", po::value<std::string>(), "URI of robot")(
@@ -41,7 +46,7 @@ int main(int argc, char* argv[]) {
 
     // connect to robot, ensure we can refresh it
     std::shared_ptr<RobotClient> robot =
-        RobotClient::at_address(vm["uri"].as<std::string>(), options);
+        RobotClient::at_address(vm["uri"].as<std::string>(), options, inst.registry());
 
     // ensure we can query resources
     std::vector<Name> resource_names = robot->resource_names();
diff --git a/src/viam/examples/mlmodel/example_audio_classification_client.cpp b/src/viam/examples/mlmodel/example_audio_classification_client.cpp
index 95e26f57c..ab06a082e 100644
--- a/src/viam/examples/mlmodel/example_audio_classification_client.cpp
+++ b/src/viam/examples/mlmodel/example_audio_classification_client.cpp
@@ -29,6 +29,7 @@
 #include <boost/program_options.hpp>
 #include <boost/variant/get.hpp>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/services/mlmodel.hpp>
 
@@ -78,6 +79,10 @@ constexpr char kRobotConfigTemplate[] = R"(
 }  // namespace
 
 int main(int argc, char* argv[]) try {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    viam::sdk::Instance inst;
+
     // Build up our command line options. The example operates in two
     // modes. In the "--generate" mode, it takes command line
     // parameters needed to satisfy the interpolation points in the
@@ -234,8 +239,8 @@ int main(int argc, char* argv[]) try {
         dial_options.set_entity(opt_api_key_id.get());
         dial_options.set_credentials(viam::sdk::Credentials("api-key", opt_api_key.get()));
 
-        auto robot =
-            vsdk::RobotClient::at_address(opt_robot_host.get(), {0, {std::move(dial_options)}});
+        auto robot = vsdk::RobotClient::at_address(
+            opt_robot_host.get(), {0, {std::move(dial_options)}}, inst.registry());
 
         // Obtain a handle to the MLModelService module on the robot. Note that the string
         // `yamnet_classification_tflite` is arbitrary. It just matches what was used to name the
diff --git a/src/viam/examples/modules/complex/client.cpp b/src/viam/examples/modules/complex/client.cpp
index ed321abd5..ea3444844 100644
--- a/src/viam/examples/modules/complex/client.cpp
+++ b/src/viam/examples/modules/complex/client.cpp
@@ -11,6 +11,7 @@
 #include <grpcpp/grpcpp.h>
 #include <grpcpp/support/status.h>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/motor.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -21,6 +22,10 @@
 using namespace viam::sdk;
 
 int main() {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    Instance inst;
+
     const char* uri = "http://localhost:8080/";  // replace with your URI if connecting securely
     DialOptions dial_options;
     dial_options.set_allow_insecure_downgrade(true);  // set to false if connecting securely
@@ -37,11 +42,11 @@ int main() {
 
     // Register custom gizmo and summation clients so robot client can access resources
     // of that type from the server.
-    Registry::register_resource_client<GizmoClient>();
-    Registry::register_resource_client<SummationClient>();
+    inst.registry()->register_resource_client<GizmoClient>();
+    inst.registry()->register_resource_client<SummationClient>();
 
     // Connect to robot.
-    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options);
+    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options, inst.registry());
     // Print resources.
     std::cout << "Resources" << std::endl;
     std::vector<Name> resource_names = robot->resource_names();
diff --git a/src/viam/examples/modules/complex/main.cpp b/src/viam/examples/modules/complex/main.cpp
index 59f590733..5d3f85f67 100644
--- a/src/viam/examples/modules/complex/main.cpp
+++ b/src/viam/examples/modules/complex/main.cpp
@@ -5,6 +5,7 @@
 #include <grpcpp/grpcpp.h>
 #include <grpcpp/server_context.h>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/base.hpp>
 #include <viam/sdk/components/component.hpp>
 #include <viam/sdk/config/resource.hpp>
@@ -24,11 +25,15 @@
 using namespace viam::sdk;
 
 int main(int argc, char** argv) {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    Instance inst;
+
     Model mybase_model("viam", "base", "mybase");
 
     // Make sure to explicity register resources with custom APIs.
-    Registry::register_resource_server<GizmoServer>();
-    Registry::register_resource_server<SummationServer>();
+    inst.registry()->register_resource_server<GizmoServer>();
+    inst.registry()->register_resource_server<SummationServer>();
 
     std::shared_ptr<ModelRegistration> mybase_mr = std::make_shared<ModelRegistration>(
         API::get<Base>(),
@@ -51,7 +56,7 @@ int main(int argc, char** argv) {
         });
 
     std::vector<std::shared_ptr<ModelRegistration>> mrs = {mybase_mr, mygizmo_mr, mysummation_mr};
-    auto my_mod = std::make_shared<ModuleService>(argc, argv, mrs);
+    auto my_mod = std::make_shared<ModuleService>(argc, argv, mrs, inst.registry());
     my_mod->serve();
 
     return EXIT_SUCCESS;
diff --git a/src/viam/examples/modules/complex/test_complex_module.cpp b/src/viam/examples/modules/complex/test_complex_module.cpp
index 4984df8f0..228ef96c7 100644
--- a/src/viam/examples/modules/complex/test_complex_module.cpp
+++ b/src/viam/examples/modules/complex/test_complex_module.cpp
@@ -24,8 +24,9 @@ using namespace viam::sdktests;
 
 struct RegisterGizmoAndSummationFixture {
     RegisterGizmoAndSummationFixture() {
-        Registry::register_resource<GizmoClient, GizmoServer>();
-        Registry::register_resource<SummationClient, SummationServer>();
+        auto* registry = GlobalInstance::registry();
+        registry->register_resource<GizmoClient, GizmoServer>();
+        registry->register_resource<SummationClient, SummationServer>();
     }
 
     // Test teardown is a noop;
diff --git a/src/viam/examples/motor/example_motor.cpp b/src/viam/examples/motor/example_motor.cpp
index c4d8023d5..1a5ff50b7 100644
--- a/src/viam/examples/motor/example_motor.cpp
+++ b/src/viam/examples/motor/example_motor.cpp
@@ -6,6 +6,7 @@
 #include <string>
 #include <vector>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/motor.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -27,6 +28,11 @@ int main() {
     namespace vs = ::viam::sdk;
 
     try {
+        // Every Viam C++ SDK program must have one and only one Instance object which is created
+        // before any other C++ SDK objects and stays alive until all Viam C++ SDK objects are
+        // destroyed.
+        vs::Instance inst;
+
         // If you want to connect to a remote robot, this should be the url of the robot
         // Ex: xxx.xxx.viam.cloud
         std::string robot_address("localhost:8080");
@@ -49,7 +55,7 @@ int main() {
 
         std::shared_ptr<vs::RobotClient> robot;
         try {
-            robot = vs::RobotClient::at_address(robot_address, options);
+            robot = vs::RobotClient::at_address(robot_address, options, inst.registry());
             cout << "Successfully connected to the robot" << endl;
         } catch (const std::exception& e) {
             cerr << "Failed to connect to the robot. Exiting." << endl;
diff --git a/src/viam/sdk/common/instance.cpp b/src/viam/sdk/common/instance.cpp
index 3c277273d..c356aee54 100644
--- a/src/viam/sdk/common/instance.cpp
+++ b/src/viam/sdk/common/instance.cpp
@@ -1,14 +1,22 @@
 #include <viam/sdk/common/instance.hpp>
 
+#include <viam/sdk/registry/registry.hpp>
+
 namespace viam {
 namespace sdk {
 
-Instance::Instance() {
-    registry_.initialize();
+struct Instance::Impl {
+    Registry registry;
+};
+
+Instance::Instance() : impl_(std::make_unique<Instance::Impl>()) {
+    impl_->registry.initialize();
 }
 
-Registry& Instance::registry() {
-    return registry_;
+Instance::~Instance() = default;
+
+Registry* Instance::registry() {
+    return &(impl_->registry);
 }
 
 }  // namespace sdk
diff --git a/src/viam/sdk/common/instance.hpp b/src/viam/sdk/common/instance.hpp
index 8297b74e4..0702d513f 100644
--- a/src/viam/sdk/common/instance.hpp
+++ b/src/viam/sdk/common/instance.hpp
@@ -1,10 +1,12 @@
 #pragma once
 
-#include <viam/sdk/registry/registry.hpp>
+#include <memory>
 
 namespace viam {
 namespace sdk {
 
+class Registry;
+
 /// @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
@@ -13,11 +15,13 @@ namespace sdk {
 class Instance {
    public:
     Instance();
+    ~Instance();
 
-    Registry& registry();
+    Registry* registry();
 
    private:
-    Registry registry_;
+    struct Impl;
+    std::unique_ptr<Impl> impl_;
 };
 
 }  // namespace sdk
diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp
index 526beecb5..041fb90b3 100644
--- a/src/viam/sdk/module/service.cpp
+++ b/src/viam/sdk/module/service.cpp
@@ -57,7 +57,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         std::shared_ptr<Resource> res;
         const Dependencies deps = parent.get_dependencies_(&request->dependencies(), cfg.name());
         const std::shared_ptr<const ModelRegistration> reg =
-            parent.registry_.lookup_model(cfg.api(), cfg.model());
+            parent.registry_->lookup_model(cfg.api(), cfg.model());
         if (reg) {
             try {
                 res = reg->construct_resource(deps, cfg);
@@ -112,7 +112,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         }
 
         const std::shared_ptr<const ModelRegistration> reg =
-            parent.registry_.lookup_model(cfg.name());
+            parent.registry_->lookup_model(cfg.name());
         if (reg) {
             try {
                 const std::shared_ptr<Resource> res = reg->construct_resource(deps, cfg);
@@ -132,7 +132,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         ResourceConfig cfg = from_proto(proto);
 
         const std::shared_ptr<const ModelRegistration> reg =
-            parent.registry_.lookup_model(cfg.api(), cfg.model());
+            parent.registry_->lookup_model(cfg.api(), cfg.model());
         if (!reg) {
             return grpc::Status(grpc::UNKNOWN,
                                 "unable to validate resource " + cfg.resource_name().name() +
@@ -214,17 +214,17 @@ std::shared_ptr<Resource> ModuleService::get_parent_resource_(const Name& name)
     return parent_->resource_by_name(name);
 }
 
-ModuleService::ModuleService(std::string addr, Registry& registry)
+ModuleService::ModuleService(std::string addr, Registry* registry)
     : registry_(registry),
       module_(std::make_unique<Module>(std::move(addr))),
-      server_(std::make_unique<Server>(&registry_)) {
+      server_(std::make_unique<Server>(registry_)) {
     impl_ = std::make_unique<ServiceImpl>(*this);
 }
 
 ModuleService::ModuleService(int argc,
                              char** argv,
                              const std::vector<std::shared_ptr<ModelRegistration>>& registrations,
-                             Registry& registry)
+                             Registry* registry)
     : ModuleService(
           [argc, argv] {
               if (argc < 2) {
@@ -237,7 +237,7 @@ ModuleService::ModuleService(int argc,
     set_logger_severity_from_args(argc, argv);
 
     for (auto&& mr : registrations) {
-        registry_.register_model(mr);
+        registry_->register_model(mr);
         add_model_from_registry(mr->api(), mr->model());
     }
 }
@@ -280,7 +280,7 @@ void ModuleService::add_model_from_registry_inlock_(API api,
                                                     Model model,
                                                     const std::lock_guard<std::mutex>&) {
     const std::shared_ptr<const ResourceServerRegistration> creator =
-        registry_.lookup_resource_server(api);
+        registry_->lookup_resource_server(api);
     std::string name;
     if (creator && creator->service_descriptor()) {
         name = creator->service_descriptor()->full_name();
diff --git a/src/viam/sdk/module/service.hpp b/src/viam/sdk/module/service.hpp
index cffc47fe7..0927d26a8 100644
--- a/src/viam/sdk/module/service.hpp
+++ b/src/viam/sdk/module/service.hpp
@@ -31,7 +31,7 @@ class ModuleService {
    public:
     /// @brief Creates a new ModuleService that can serve on the provided socket.
     /// @param addr Address of socket to serve on.
-    explicit ModuleService(std::string addr, Registry& registry);
+    explicit ModuleService(std::string addr, Registry* registry);
 
     /// @brief Creates a new ModuleService. Socket path and log level will be
     /// inferred from passed in command line arguments, and passed in model
@@ -42,7 +42,7 @@ class ModuleService {
     explicit ModuleService(int argc,
                            char** argv,
                            const std::vector<std::shared_ptr<ModelRegistration>>& registrations,
-                           Registry& registry);
+                           Registry* registry);
     ~ModuleService();
 
     /// @brief Starts module. serve will return when SIGINT or SIGTERM is received
@@ -65,7 +65,7 @@ class ModuleService {
                                    std::string const& resource_name);
     std::shared_ptr<Resource> get_parent_resource_(const Name& name);
 
-    Registry& registry_;
+    Registry* registry_;
 
     std::mutex lock_;
     std::unique_ptr<Module> module_;
diff --git a/src/viam/sdk/registry/registry.hpp b/src/viam/sdk/registry/registry.hpp
index 75e2324e3..43fa5c38f 100644
--- a/src/viam/sdk/registry/registry.hpp
+++ b/src/viam/sdk/registry/registry.hpp
@@ -185,7 +185,6 @@ class Registry {
 
    private:
     friend class Instance;
-
     Registry() = default;
 
     /// @brief Initialized the Viam registry. No-op if it has already been called.
diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp
index 6ac049eba..cb72451c7 100644
--- a/src/viam/sdk/robot/client.cpp
+++ b/src/viam/sdk/robot/client.cpp
@@ -178,7 +178,7 @@ void RobotClient::refresh() {
         // are being properly registered from name.subtype(), or update what we're
         // using for lookup
         const std::shared_ptr<const ResourceClientRegistration> rs =
-            registry_.lookup_resource_client({name.namespace_(), name.type(), name.subtype()});
+            registry_->lookup_resource_client({name.namespace_(), name.type(), name.subtype()});
         if (rs) {
             try {
                 const std::shared_ptr<Resource> rpc_client =
@@ -221,7 +221,7 @@ void RobotClient::refresh_every() {
     }
 };
 
-RobotClient::RobotClient(std::shared_ptr<ViamChannel> channel, Registry& registry)
+RobotClient::RobotClient(std::shared_ptr<ViamChannel> channel, Registry* registry)
     : registry_(registry),
       channel_(channel->channel()),
       viam_channel_(std::move(channel)),
@@ -235,7 +235,7 @@ std::vector<Name> RobotClient::resource_names() const {
 
 std::shared_ptr<RobotClient> RobotClient::with_channel(std::shared_ptr<ViamChannel> channel,
                                                        const Options& options,
-                                                       Registry& registry) {
+                                                       Registry* registry) {
     std::shared_ptr<RobotClient> robot =
         std::make_shared<RobotClient>(std::move(channel), registry);
     robot->refresh_interval_ = options.refresh_interval();
@@ -256,7 +256,7 @@ std::shared_ptr<RobotClient> RobotClient::with_channel(std::shared_ptr<ViamChann
 
 std::shared_ptr<RobotClient> RobotClient::at_address(const std::string& address,
                                                      const Options& options,
-                                                     Registry& registry) {
+                                                     Registry* registry) {
     const char* uri = address.c_str();
     auto channel = ViamChannel::dial(uri, options.dial_options());
     std::shared_ptr<RobotClient> robot = RobotClient::with_channel(channel, options, registry);
@@ -267,7 +267,7 @@ std::shared_ptr<RobotClient> RobotClient::at_address(const std::string& address,
 
 std::shared_ptr<RobotClient> RobotClient::at_local_socket(const std::string& address,
                                                           const Options& options,
-                                                          Registry& registry) {
+                                                          Registry* registry) {
     const std::string addr = "unix://" + address;
     const char* uri = addr.c_str();
     const std::shared_ptr<grpc::Channel> channel =
diff --git a/src/viam/sdk/robot/client.hpp b/src/viam/sdk/robot/client.hpp
index 09834e1d8..0a9610ac2 100644
--- a/src/viam/sdk/robot/client.hpp
+++ b/src/viam/sdk/robot/client.hpp
@@ -12,14 +12,16 @@
 #include <viam/sdk/common/utils.hpp>
 #include <viam/sdk/common/world_state.hpp>
 #include <viam/sdk/components/component.hpp>
-#include <viam/sdk/registry/registry.hpp>
 #include <viam/sdk/resource/resource.hpp>
+#include <viam/sdk/resource/resource_manager.hpp>
 #include <viam/sdk/rpc/dial.hpp>
 #include <viam/sdk/services/service.hpp>
 
 namespace viam {
 namespace sdk {
 
+class Registry;
+
 /// @defgroup Robot Classes related to a Robot representation.
 
 /// @class RobotClient client.hpp "robot/client.hpp"
@@ -69,7 +71,7 @@ class RobotClient {
     /// @param options Options for connecting and refreshing.
     static std::shared_ptr<RobotClient> at_address(const std::string& address,
                                                    const Options& options,
-                                                   Registry& registry);
+                                                   Registry* registry);
 
     /// @brief Creates a robot client connected to the robot at the provided local socket.
     /// @param address The local socket of the robot (a .sock file, etc.).
@@ -78,7 +80,7 @@ class RobotClient {
     /// Only useful for connecting to robots across Unix sockets.
     static std::shared_ptr<RobotClient> at_local_socket(const std::string& address,
                                                         const Options& options,
-                                                        Registry& registry);
+                                                        Registry* registry);
 
     /// @brief Creates a robot client connected to the provided channel.
     /// @param channel The channel to connect with.
@@ -87,9 +89,9 @@ class RobotClient {
     /// `close()`d manually.
     static std::shared_ptr<RobotClient> with_channel(std::shared_ptr<ViamChannel> channel,
                                                      const Options& options,
-                                                     Registry& registry);
+                                                     Registry* registry);
 
-    RobotClient(std::shared_ptr<ViamChannel> channel, Registry& registry);
+    RobotClient(std::shared_ptr<ViamChannel> channel, Registry* registry);
 
     std::vector<Name> resource_names() const;
 
@@ -152,7 +154,7 @@ class RobotClient {
     status get_machine_status() const;
 
    private:
-    Registry& registry_;
+    Registry* registry_;
 
     std::vector<std::shared_ptr<std::thread>> threads_;
 
diff --git a/src/viam/sdk/tests/mocks/mock_robot.cpp b/src/viam/sdk/tests/mocks/mock_robot.cpp
index 8e28b944b..fdc068ec3 100644
--- a/src/viam/sdk/tests/mocks/mock_robot.cpp
+++ b/src/viam/sdk/tests/mocks/mock_robot.cpp
@@ -26,7 +26,7 @@ std::vector<Name> registered_models_for_resource(const std::shared_ptr<Resource>
     std::string resource_type;
     std::string resource_subtype;
     std::vector<Name> resource_names;
-    for (const auto& kv : GlobalInstance::registry().registered_models()) {
+    for (const auto& kv : GlobalInstance::registry()->registered_models()) {
         const std::shared_ptr<const ModelRegistration> reg = kv.second;
         if (reg->api() == resource->api()) {
             resource_type = reg->api().resource_type();
diff --git a/src/viam/sdk/tests/test_robot.cpp b/src/viam/sdk/tests/test_robot.cpp
index e7ccc0de1..a7b403359 100644
--- a/src/viam/sdk/tests/test_robot.cpp
+++ b/src/viam/sdk/tests/test_robot.cpp
@@ -42,7 +42,7 @@ void robot_client_to_mocks_pipeline(F&& test_case) {
     rm->add(std::string("mock_generic"), generic::MockGenericComponent::get_mock_generic());
     rm->add(std::string("mock_motor"), motor::MockMotor::get_mock_motor());
     rm->add(std::string("mock_camera"), camera::MockCamera::get_mock_camera());
-    auto server = std::make_shared<sdk::Server>(&GlobalInstance::registry());
+    auto server = std::make_shared<sdk::Server>(GlobalInstance::registry());
     MockRobotService service(rm, *server);
     server->start();
 
@@ -63,7 +63,7 @@ void robot_client_to_mocks_pipeline(F&& test_case) {
 }
 
 BOOST_AUTO_TEST_CASE(test_registering_resources) {
-    auto& registry = GlobalInstance::registry();
+    auto& registry = *GlobalInstance::registry();
 
     // To test with mock resources we need to be able to create them, which means registering
     // constructors. This tests that we register correctly.
diff --git a/src/viam/sdk/tests/test_utils.hpp b/src/viam/sdk/tests/test_utils.hpp
index c24bed482..655b34904 100644
--- a/src/viam/sdk/tests/test_utils.hpp
+++ b/src/viam/sdk/tests/test_utils.hpp
@@ -17,7 +17,7 @@ struct GlobalInstance {
         return inst;
     }
 
-    static sdk::Registry& registry() {
+    static sdk::Registry* registry() {
         return get().registry();
     }
 };
@@ -61,7 +61,7 @@ class TestServer {
 // The passed in test_case function will have access to the created ResourceClient.
 template <typename ResourceType, typename F>
 void client_to_mock_pipeline(std::shared_ptr<Resource> mock, F&& test_case) {
-    auto server = std::make_shared<sdk::Server>(&GlobalInstance::registry());
+    auto server = std::make_shared<sdk::Server>(GlobalInstance::registry());
 
     // normally the high level server service (either robot or module) handles adding managed
     // resources, but in this case we must do it ourselves.
@@ -74,7 +74,7 @@ void client_to_mock_pipeline(std::shared_ptr<Resource> mock, F&& test_case) {
     auto grpc_channel = test_server.grpc_in_process_channel();
 
     auto resource_client = GlobalInstance::registry()
-                               .lookup_resource_client(API::get<ResourceType>())
+                               ->lookup_resource_client(API::get<ResourceType>())
                                ->create_rpc_client(mock->name(), std::move(grpc_channel));
 
     // Run the passed-in test case on the created stack and give access to the

From e5cf6d054c82a4dff634ec64b5eb95e3c2a5426c Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 25 Feb 2025 15:37:51 -0500
Subject: [PATCH 08/86] add instance management infra

---
 .../modules/complex/test_complex_module.cpp   |  2 +-
 src/viam/sdk/common/instance.cpp              | 41 ++++++++++++++++++-
 src/viam/sdk/common/instance.hpp              |  2 +
 src/viam/sdk/tests/mocks/mock_robot.cpp       |  2 +-
 src/viam/sdk/tests/test_robot.cpp             |  6 +--
 src/viam/sdk/tests/test_utils.hpp             | 16 ++------
 6 files changed, 49 insertions(+), 20 deletions(-)

diff --git a/src/viam/examples/modules/complex/test_complex_module.cpp b/src/viam/examples/modules/complex/test_complex_module.cpp
index 228ef96c7..3f4d4ee3d 100644
--- a/src/viam/examples/modules/complex/test_complex_module.cpp
+++ b/src/viam/examples/modules/complex/test_complex_module.cpp
@@ -24,7 +24,7 @@ using namespace viam::sdktests;
 
 struct RegisterGizmoAndSummationFixture {
     RegisterGizmoAndSummationFixture() {
-        auto* registry = GlobalInstance::registry();
+        auto* registry = Instance::current().registry();
         registry->register_resource<GizmoClient, GizmoServer>();
         registry->register_resource<SummationClient, SummationServer>();
     }
diff --git a/src/viam/sdk/common/instance.cpp b/src/viam/sdk/common/instance.cpp
index c356aee54..9b6a7eccc 100644
--- a/src/viam/sdk/common/instance.cpp
+++ b/src/viam/sdk/common/instance.cpp
@@ -1,19 +1,56 @@
 #include <viam/sdk/common/instance.hpp>
 
+#include <viam/sdk/common/exception.hpp>
 #include <viam/sdk/registry/registry.hpp>
 
+#include <atomic>
+
 namespace viam {
 namespace sdk {
 
+namespace {
+
+// Memory region sentinel to check if object is being destroyed.
+std::aligned_storage_t<sizeof(Instance), alignof(Instance)> sentinel;
+
+std::atomic<Instance*> current_instance{nullptr};
+
+}  // namespace
+
 struct Instance::Impl {
     Registry registry;
 };
 
-Instance::Instance() : impl_(std::make_unique<Instance::Impl>()) {
+Instance::Instance() {
+    Instance* expected = nullptr;
+
+    if (!current_instance.compare_exchange_strong(expected, this)) {
+        throw Exception("tried to create duplicate instance");
+    }
+
+    impl_ = std::make_unique<Instance::Impl>();
     impl_->registry.initialize();
 }
 
-Instance::~Instance() = default;
+Instance::~Instance() {
+    current_instance.store(reinterpret_cast<Instance*>(&sentinel));
+    impl_.reset();
+}
+
+Instance& Instance::current() {
+    if (!current_instance.load()) {
+        // This variable declaration calls the default ctor, storing a current instance.
+        static Instance inst;
+    }
+
+    Instance* current = current_instance.load();
+
+    if (current == reinterpret_cast<Instance*>(&sentinel)) {
+        throw Exception("instance was destroyed");
+    }
+
+    return *current;
+}
 
 Registry* Instance::registry() {
     return &(impl_->registry);
diff --git a/src/viam/sdk/common/instance.hpp b/src/viam/sdk/common/instance.hpp
index 0702d513f..c1ab0961f 100644
--- a/src/viam/sdk/common/instance.hpp
+++ b/src/viam/sdk/common/instance.hpp
@@ -17,6 +17,8 @@ class Instance {
     Instance();
     ~Instance();
 
+    static Instance& current();
+
     Registry* registry();
 
    private:
diff --git a/src/viam/sdk/tests/mocks/mock_robot.cpp b/src/viam/sdk/tests/mocks/mock_robot.cpp
index fdc068ec3..5dc03fcc2 100644
--- a/src/viam/sdk/tests/mocks/mock_robot.cpp
+++ b/src/viam/sdk/tests/mocks/mock_robot.cpp
@@ -26,7 +26,7 @@ std::vector<Name> registered_models_for_resource(const std::shared_ptr<Resource>
     std::string resource_type;
     std::string resource_subtype;
     std::vector<Name> resource_names;
-    for (const auto& kv : GlobalInstance::registry()->registered_models()) {
+    for (const auto& kv : Instance::current().registry()->registered_models()) {
         const std::shared_ptr<const ModelRegistration> reg = kv.second;
         if (reg->api() == resource->api()) {
             resource_type = reg->api().resource_type();
diff --git a/src/viam/sdk/tests/test_robot.cpp b/src/viam/sdk/tests/test_robot.cpp
index a7b403359..e3bead0c9 100644
--- a/src/viam/sdk/tests/test_robot.cpp
+++ b/src/viam/sdk/tests/test_robot.cpp
@@ -42,7 +42,7 @@ void robot_client_to_mocks_pipeline(F&& test_case) {
     rm->add(std::string("mock_generic"), generic::MockGenericComponent::get_mock_generic());
     rm->add(std::string("mock_motor"), motor::MockMotor::get_mock_motor());
     rm->add(std::string("mock_camera"), camera::MockCamera::get_mock_camera());
-    auto server = std::make_shared<sdk::Server>(GlobalInstance::registry());
+    auto server = std::make_shared<sdk::Server>(Instance::current().registry());
     MockRobotService service(rm, *server);
     server->start();
 
@@ -52,7 +52,7 @@ void robot_client_to_mocks_pipeline(F&& test_case) {
     auto grpc_channel = test_server.grpc_in_process_channel();
     auto viam_channel = std::make_shared<ViamChannel>(grpc_channel, "", nullptr);
     auto client = RobotClient::with_channel(
-        viam_channel, Options(0, boost::none), GlobalInstance::registry());
+        viam_channel, Options(0, boost::none), Instance::current().registry());
 
     // Run the passed-in test case on the created stack and give access to the
     // created RobotClient and MockRobotService.
@@ -63,7 +63,7 @@ void robot_client_to_mocks_pipeline(F&& test_case) {
 }
 
 BOOST_AUTO_TEST_CASE(test_registering_resources) {
-    auto& registry = *GlobalInstance::registry();
+    auto& registry = *Instance::current().registry();
 
     // To test with mock resources we need to be able to create them, which means registering
     // constructors. This tests that we register correctly.
diff --git a/src/viam/sdk/tests/test_utils.hpp b/src/viam/sdk/tests/test_utils.hpp
index 655b34904..e5c6fb729 100644
--- a/src/viam/sdk/tests/test_utils.hpp
+++ b/src/viam/sdk/tests/test_utils.hpp
@@ -11,17 +11,6 @@
 namespace viam {
 namespace sdktests {
 
-struct GlobalInstance {
-    static sdk::Instance& get() {
-        static sdk::Instance inst;
-        return inst;
-    }
-
-    static sdk::Registry* registry() {
-        return get().registry();
-    }
-};
-
 using namespace viam::sdk;
 
 ProtoStruct fake_map();
@@ -61,7 +50,7 @@ class TestServer {
 // The passed in test_case function will have access to the created ResourceClient.
 template <typename ResourceType, typename F>
 void client_to_mock_pipeline(std::shared_ptr<Resource> mock, F&& test_case) {
-    auto server = std::make_shared<sdk::Server>(GlobalInstance::registry());
+    auto server = std::make_shared<sdk::Server>(sdk::Instance::current().registry());
 
     // normally the high level server service (either robot or module) handles adding managed
     // resources, but in this case we must do it ourselves.
@@ -73,7 +62,8 @@ void client_to_mock_pipeline(std::shared_ptr<Resource> mock, F&& test_case) {
     auto test_server = TestServer(server);
     auto grpc_channel = test_server.grpc_in_process_channel();
 
-    auto resource_client = GlobalInstance::registry()
+    auto resource_client = sdk::Instance::current()
+                               .registry()
                                ->lookup_resource_client(API::get<ResourceType>())
                                ->create_rpc_client(mock->name(), std::move(grpc_channel));
 

From 5024d3a2d6be053ee8b088ede5746475c2298477 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 25 Feb 2025 15:43:04 -0500
Subject: [PATCH 09/86] update tflite module

---
 src/viam/examples/modules/tflite/main.cpp | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/viam/examples/modules/tflite/main.cpp b/src/viam/examples/modules/tflite/main.cpp
index 44cd8e284..e1c806354 100644
--- a/src/viam/examples/modules/tflite/main.cpp
+++ b/src/viam/examples/modules/tflite/main.cpp
@@ -23,6 +23,7 @@
 
 #include <tensorflow/lite/c/c_api.h>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/components/component.hpp>
 #include <viam/sdk/config/resource.hpp>
@@ -723,6 +724,10 @@ class MLModelServiceTFLite : public vsdk::MLModelService,
 };
 
 int serve(const std::string& socket_path) try {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    vsdk::Instance inst;
+
     // Create a new model registration for the service.
     auto module_registration = std::make_shared<vsdk::ModelRegistration>(
         // Identify that this resource offers the MLModelService API
@@ -737,10 +742,10 @@ int serve(const std::string& socket_path) try {
         });
 
     // Register the newly created registration with the Registry.
-    vsdk::Registry::register_model(module_registration);
+    inst.registry()->register_model(module_registration);
 
     // Construct the module service and tell it where to place the socket path.
-    auto module_service = std::make_shared<vsdk::ModuleService>(socket_path);
+    auto module_service = std::make_shared<vsdk::ModuleService>(socket_path, inst.registry());
 
     // Add the server as providing the API and model declared in the
     // registration.

From ef8ec8eff50e91c87c23dcc3a34f26611fe48ccc Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 25 Feb 2025 15:59:35 -0500
Subject: [PATCH 10/86] silence spurious const warning

---
 src/viam/sdk/common/instance.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/viam/sdk/common/instance.cpp b/src/viam/sdk/common/instance.cpp
index 9b6a7eccc..c366ad6c6 100644
--- a/src/viam/sdk/common/instance.cpp
+++ b/src/viam/sdk/common/instance.cpp
@@ -40,7 +40,7 @@ Instance::~Instance() {
 Instance& Instance::current() {
     if (!current_instance.load()) {
         // This variable declaration calls the default ctor, storing a current instance.
-        static Instance inst;
+        static Instance inst;  // NOLINT (misc-const-correctness)
     }
 
     Instance* current = current_instance.load();

From 16113fd4dd38f44e8481c3468eba258b22296941 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 25 Feb 2025 16:27:29 -0500
Subject: [PATCH 11/86] make method static

---
 src/viam/sdk/registry/registry.cpp | 2 +-
 src/viam/sdk/registry/registry.hpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/viam/sdk/registry/registry.cpp b/src/viam/sdk/registry/registry.cpp
index 71e3e7711..db28f628b 100644
--- a/src/viam/sdk/registry/registry.cpp
+++ b/src/viam/sdk/registry/registry.cpp
@@ -163,7 +163,7 @@ std::shared_ptr<const ResourceClientRegistration> Registry::lookup_resource_clie
 }
 
 const google::protobuf::ServiceDescriptor* Registry::get_service_descriptor_(
-    const char* service_full_name) const {
+    const char* service_full_name) {
     const google::protobuf::DescriptorPool* p = google::protobuf::DescriptorPool::generated_pool();
     const google::protobuf::ServiceDescriptor* sd = p->FindServiceByName(service_full_name);
     if (!sd) {
diff --git a/src/viam/sdk/registry/registry.hpp b/src/viam/sdk/registry/registry.hpp
index 43fa5c38f..db476be97 100644
--- a/src/viam/sdk/registry/registry.hpp
+++ b/src/viam/sdk/registry/registry.hpp
@@ -204,8 +204,8 @@ class Registry {
     void register_resource_client_(
         API api, std::shared_ptr<ResourceClientRegistration> resource_registration);
 
-    const google::protobuf::ServiceDescriptor* get_service_descriptor_(
-        const char* service_full_name) const;
+    static const google::protobuf::ServiceDescriptor* get_service_descriptor_(
+        const char* service_full_name);
 
     std::shared_ptr<const ModelRegistration> lookup_model_inlock_(
         const std::string& name, const std::lock_guard<std::mutex>&) const;

From dbf1647b0a4074b0f260d59fb531a6ce57992a14 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 26 Feb 2025 11:33:05 -0500
Subject: [PATCH 12/86] update cml boost links

---
 CMakeLists.txt              | 2 +-
 src/viam/sdk/CMakeLists.txt | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 25fc45b3b..d05467cf9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -324,7 +324,7 @@ set(VIAMCPPSDK_XTL_VERSION_MINIMUM 0.7.2)
 set(VIAMCPPSDK_XTENSOR_VERSION_MINIMUM 0.24.3)
 
 # Time to find `BOOST`.
-find_package(Boost ${VIAMCPPSDK_BOOST_VERSION_MINIMUM} REQUIRED COMPONENTS headers log program_options)
+find_package(Boost ${VIAMCPPSDK_BOOST_VERSION_MINIMUM} REQUIRED COMPONENTS headers log log_setup program_options)
 
 # Time to find `protobuf` and `gRPC[++]`. Normally this would just be
 # something like `find_package(gRPC <version> CONFIG REQUIRED)`, and
diff --git a/src/viam/sdk/CMakeLists.txt b/src/viam/sdk/CMakeLists.txt
index b69b586f9..0ba964cc3 100644
--- a/src/viam/sdk/CMakeLists.txt
+++ b/src/viam/sdk/CMakeLists.txt
@@ -244,6 +244,7 @@ viamcppsdk_link_viam_api(viamsdk PRIVATE)
 target_link_libraries(viamsdk
   PUBLIC Boost::headers
   PUBLIC Boost::log
+  PUBLIC Boost::log_setup
   PUBLIC xtensor
   PRIVATE ${VIAMCPPSDK_GRPCXX_REFLECTION_LIBRARIES}
   PRIVATE ${VIAMCPPSDK_GRPCXX_LIBRARIES}

From d1dffb80fcf400b11f495a722102de392288640e Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 27 Feb 2025 12:49:51 -0500
Subject: [PATCH 13/86] add logger to resource

---
 src/viam/sdk/resource/resource.cpp | 5 ++++-
 src/viam/sdk/resource/resource.hpp | 5 +++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/viam/sdk/resource/resource.cpp b/src/viam/sdk/resource/resource.cpp
index caf4b94d1..48bbb0f37 100644
--- a/src/viam/sdk/resource/resource.cpp
+++ b/src/viam/sdk/resource/resource.cpp
@@ -1,5 +1,7 @@
 #include <viam/sdk/resource/resource.hpp>
 
+#include <boost/log/keywords/channel.hpp>
+
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/common/utils.hpp>
 #include <viam/sdk/registry/registry.hpp>
@@ -9,7 +11,8 @@ namespace viam {
 namespace sdk {
 
 Resource::~Resource() = default;
-Resource::Resource(std::string name) : name_(std::move(name)) {}
+Resource::Resource(std::string name)
+    : name_(std::move(name)), logger_(boost::log::keywords::channel = name_) {}
 
 std::string Resource::name() const {
     return name_;
diff --git a/src/viam/sdk/resource/resource.hpp b/src/viam/sdk/resource/resource.hpp
index dcf9f4363..d61e5eb04 100644
--- a/src/viam/sdk/resource/resource.hpp
+++ b/src/viam/sdk/resource/resource.hpp
@@ -2,6 +2,9 @@
 
 #include <unordered_map>
 
+#include <boost/log/sources/severity_channel_logger.hpp>
+#include <boost/log/trivial.hpp>
+
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/config/resource.hpp>
 #include <viam/sdk/resource/resource_api.hpp>
@@ -30,6 +33,8 @@ class Resource {
 
    protected:
     Name get_resource_name(const std::string& type) const;
+
+    boost::log::sources::severity_channel_logger_mt<boost::log::trivial::severity_level> logger_;
 };
 
 template <>

From 4771390555ed6fdad2220d138e4ab770bb8b7b43 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 27 Feb 2025 13:48:18 -0500
Subject: [PATCH 14/86] use static current to populate registry member

---
 src/viam/examples/camera/example_camera.cpp   |  8 +-----
 src/viam/examples/dial/example_dial.cpp       |  7 +----
 .../dial_api_key/example_dial_api_key.cpp     |  7 +----
 .../example_audio_classification_client.cpp   |  9 ++-----
 src/viam/examples/modules/complex/client.cpp  | 11 +++-----
 src/viam/examples/modules/complex/main.cpp    | 11 +++-----
 .../modules/complex/test_complex_module.cpp   |  5 ++--
 src/viam/examples/modules/simple/client.cpp   |  5 +---
 src/viam/examples/modules/simple/main.cpp     |  5 +---
 src/viam/examples/modules/tflite/main.cpp     |  9 ++-----
 src/viam/examples/motor/example_motor.cpp     |  8 +-----
 src/viam/sdk/module/service.cpp               | 27 +++++++++----------
 src/viam/sdk/module/service.hpp               |  5 ++--
 src/viam/sdk/registry/registry.cpp            |  5 ++++
 src/viam/sdk/registry/registry.hpp            |  3 +++
 src/viam/sdk/robot/client.cpp                 | 20 ++++++--------
 src/viam/sdk/robot/client.hpp                 | 24 ++++-------------
 src/viam/sdk/rpc/server.cpp                   |  4 +--
 src/viam/sdk/rpc/server.hpp                   |  4 +--
 src/viam/sdk/tests/test_robot.cpp             |  7 +++--
 src/viam/sdk/tests/test_utils.hpp             |  7 +++--
 21 files changed, 62 insertions(+), 129 deletions(-)

diff --git a/src/viam/examples/camera/example_camera.cpp b/src/viam/examples/camera/example_camera.cpp
index b0202a27e..bc1b0ab9a 100644
--- a/src/viam/examples/camera/example_camera.cpp
+++ b/src/viam/examples/camera/example_camera.cpp
@@ -3,7 +3,6 @@
 #include <unistd.h>
 #include <vector>
 
-#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/camera.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -14,11 +13,6 @@ int main() {
     using std::endl;
     namespace vs = ::viam::sdk;
     try {
-        // Every Viam C++ SDK program must have one and only one Instance object which is created
-        // before
-        // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
-        vs::Instance inst;
-
         // If you want to connect to a remote robot, this should be the url of the robot
         // Ex: xxx.xxx.viam.cloud
         std::string robot_address("localhost:8080");
@@ -41,7 +35,7 @@ int main() {
 
         std::shared_ptr<vs::RobotClient> robot;
         try {
-            robot = vs::RobotClient::at_address(robot_address, options, inst.registry());
+            robot = vs::RobotClient::at_address(robot_address, options);
             cout << "Successfully connected to the robot" << endl;
         } catch (const std::exception& e) {
             cerr << "Failed to connect to the robot. Exiting." << endl;
diff --git a/src/viam/examples/dial/example_dial.cpp b/src/viam/examples/dial/example_dial.cpp
index 28ccd7659..efa783dfb 100644
--- a/src/viam/examples/dial/example_dial.cpp
+++ b/src/viam/examples/dial/example_dial.cpp
@@ -9,7 +9,6 @@
 
 #include <boost/optional.hpp>
 
-#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/generic.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -17,10 +16,6 @@
 using namespace viam::sdk;
 
 int main() {
-    // Every Viam C++ SDK program must have one and only one Instance object which is created before
-    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
-    Instance inst;
-
     const char* uri = "<your robot URI here>";
     DialOptions dial_options;
     std::string type = "<your authentication type>";
@@ -32,7 +27,7 @@ int main() {
     Options options(1, opts);
 
     // connect to robot, ensure we can refresh it
-    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options, inst.registry());
+    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options);
 
     // ensure we can query resources
     std::vector<Name> resource_names = robot->resource_names();
diff --git a/src/viam/examples/dial_api_key/example_dial_api_key.cpp b/src/viam/examples/dial_api_key/example_dial_api_key.cpp
index 7c9e82fef..4c47beffe 100644
--- a/src/viam/examples/dial_api_key/example_dial_api_key.cpp
+++ b/src/viam/examples/dial_api_key/example_dial_api_key.cpp
@@ -10,7 +10,6 @@
 #include <boost/optional.hpp>
 #include <boost/program_options.hpp>
 
-#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
 
@@ -19,10 +18,6 @@ using namespace viam::sdk;
 namespace po = boost::program_options;
 
 int main(int argc, char* argv[]) {
-    // Every Viam C++ SDK program must have one and only one Instance object which is created before
-    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
-    Instance inst;
-
     po::options_description desc("Allowed options");
     desc.add_options()("help", "List options and exit")(
         "uri", po::value<std::string>(), "URI of robot")(
@@ -46,7 +41,7 @@ int main(int argc, char* argv[]) {
 
     // connect to robot, ensure we can refresh it
     std::shared_ptr<RobotClient> robot =
-        RobotClient::at_address(vm["uri"].as<std::string>(), options, inst.registry());
+        RobotClient::at_address(vm["uri"].as<std::string>(), options);
 
     // ensure we can query resources
     std::vector<Name> resource_names = robot->resource_names();
diff --git a/src/viam/examples/mlmodel/example_audio_classification_client.cpp b/src/viam/examples/mlmodel/example_audio_classification_client.cpp
index ab06a082e..95e26f57c 100644
--- a/src/viam/examples/mlmodel/example_audio_classification_client.cpp
+++ b/src/viam/examples/mlmodel/example_audio_classification_client.cpp
@@ -29,7 +29,6 @@
 #include <boost/program_options.hpp>
 #include <boost/variant/get.hpp>
 
-#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/services/mlmodel.hpp>
 
@@ -79,10 +78,6 @@ constexpr char kRobotConfigTemplate[] = R"(
 }  // namespace
 
 int main(int argc, char* argv[]) try {
-    // Every Viam C++ SDK program must have one and only one Instance object which is created before
-    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
-    viam::sdk::Instance inst;
-
     // Build up our command line options. The example operates in two
     // modes. In the "--generate" mode, it takes command line
     // parameters needed to satisfy the interpolation points in the
@@ -239,8 +234,8 @@ int main(int argc, char* argv[]) try {
         dial_options.set_entity(opt_api_key_id.get());
         dial_options.set_credentials(viam::sdk::Credentials("api-key", opt_api_key.get()));
 
-        auto robot = vsdk::RobotClient::at_address(
-            opt_robot_host.get(), {0, {std::move(dial_options)}}, inst.registry());
+        auto robot =
+            vsdk::RobotClient::at_address(opt_robot_host.get(), {0, {std::move(dial_options)}});
 
         // Obtain a handle to the MLModelService module on the robot. Note that the string
         // `yamnet_classification_tflite` is arbitrary. It just matches what was used to name the
diff --git a/src/viam/examples/modules/complex/client.cpp b/src/viam/examples/modules/complex/client.cpp
index ea3444844..ac85c9f00 100644
--- a/src/viam/examples/modules/complex/client.cpp
+++ b/src/viam/examples/modules/complex/client.cpp
@@ -11,7 +11,6 @@
 #include <grpcpp/grpcpp.h>
 #include <grpcpp/support/status.h>
 
-#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/motor.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -22,10 +21,6 @@
 using namespace viam::sdk;
 
 int main() {
-    // Every Viam C++ SDK program must have one and only one Instance object which is created before
-    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
-    Instance inst;
-
     const char* uri = "http://localhost:8080/";  // replace with your URI if connecting securely
     DialOptions dial_options;
     dial_options.set_allow_insecure_downgrade(true);  // set to false if connecting securely
@@ -42,11 +37,11 @@ int main() {
 
     // Register custom gizmo and summation clients so robot client can access resources
     // of that type from the server.
-    inst.registry()->register_resource_client<GizmoClient>();
-    inst.registry()->register_resource_client<SummationClient>();
+    Registry::get().register_resource_client<GizmoClient>();
+    Registry::get().register_resource_client<SummationClient>();
 
     // Connect to robot.
-    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options, inst.registry());
+    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options);
     // Print resources.
     std::cout << "Resources" << std::endl;
     std::vector<Name> resource_names = robot->resource_names();
diff --git a/src/viam/examples/modules/complex/main.cpp b/src/viam/examples/modules/complex/main.cpp
index 5d3f85f67..da80453e6 100644
--- a/src/viam/examples/modules/complex/main.cpp
+++ b/src/viam/examples/modules/complex/main.cpp
@@ -5,7 +5,6 @@
 #include <grpcpp/grpcpp.h>
 #include <grpcpp/server_context.h>
 
-#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/base.hpp>
 #include <viam/sdk/components/component.hpp>
 #include <viam/sdk/config/resource.hpp>
@@ -25,15 +24,11 @@
 using namespace viam::sdk;
 
 int main(int argc, char** argv) {
-    // Every Viam C++ SDK program must have one and only one Instance object which is created before
-    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
-    Instance inst;
-
     Model mybase_model("viam", "base", "mybase");
 
     // Make sure to explicity register resources with custom APIs.
-    inst.registry()->register_resource_server<GizmoServer>();
-    inst.registry()->register_resource_server<SummationServer>();
+    Registry::get().register_resource_server<GizmoServer>();
+    Registry::get().register_resource_server<SummationServer>();
 
     std::shared_ptr<ModelRegistration> mybase_mr = std::make_shared<ModelRegistration>(
         API::get<Base>(),
@@ -56,7 +51,7 @@ int main(int argc, char** argv) {
         });
 
     std::vector<std::shared_ptr<ModelRegistration>> mrs = {mybase_mr, mygizmo_mr, mysummation_mr};
-    auto my_mod = std::make_shared<ModuleService>(argc, argv, mrs, inst.registry());
+    auto my_mod = std::make_shared<ModuleService>(argc, argv, mrs);
     my_mod->serve();
 
     return EXIT_SUCCESS;
diff --git a/src/viam/examples/modules/complex/test_complex_module.cpp b/src/viam/examples/modules/complex/test_complex_module.cpp
index 3f4d4ee3d..14d68a8cf 100644
--- a/src/viam/examples/modules/complex/test_complex_module.cpp
+++ b/src/viam/examples/modules/complex/test_complex_module.cpp
@@ -24,9 +24,8 @@ using namespace viam::sdktests;
 
 struct RegisterGizmoAndSummationFixture {
     RegisterGizmoAndSummationFixture() {
-        auto* registry = Instance::current().registry();
-        registry->register_resource<GizmoClient, GizmoServer>();
-        registry->register_resource<SummationClient, SummationServer>();
+        Registry::get().register_resource<GizmoClient, GizmoServer>();
+        Registry::get().register_resource<SummationClient, SummationServer>();
     }
 
     // Test teardown is a noop;
diff --git a/src/viam/examples/modules/simple/client.cpp b/src/viam/examples/modules/simple/client.cpp
index ac014d1e7..fcaab5eff 100644
--- a/src/viam/examples/modules/simple/client.cpp
+++ b/src/viam/examples/modules/simple/client.cpp
@@ -2,7 +2,6 @@
 #include <memory>
 #include <string>
 
-#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/components/sensor.hpp>
 #include <viam/sdk/robot/client.hpp>
@@ -11,8 +10,6 @@
 using namespace viam::sdk;
 
 int main() {
-    Instance inst;
-
     const char* uri = "http://localhost:8080/";  // replace with your URI if connecting securely
     DialOptions dial_options;
     dial_options.set_allow_insecure_downgrade(true);  // set to false if connecting securely
@@ -27,7 +24,7 @@ int main() {
     std::string address(uri);
     Options options(1, opts);
 
-    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options, inst.registry());
+    std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options);
 
     // Print resources
     std::cout << "Resources\n";
diff --git a/src/viam/examples/modules/simple/main.cpp b/src/viam/examples/modules/simple/main.cpp
index b899d3b44..cfd783f66 100644
--- a/src/viam/examples/modules/simple/main.cpp
+++ b/src/viam/examples/modules/simple/main.cpp
@@ -3,7 +3,6 @@
 #include <sstream>
 
 #include <viam/sdk/common/exception.hpp>
-#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/components/sensor.hpp>
 #include <viam/sdk/config/resource.hpp>
@@ -76,8 +75,6 @@ ProtoStruct MySensor::get_readings(const ProtoStruct&) {
 }
 
 int main(int argc, char** argv) try {
-    Instance inst;
-
     Model mysensor_model("viam", "sensor", "mysensor");
 
     std::shared_ptr<ModelRegistration> mr = std::make_shared<ModelRegistration>(
@@ -87,7 +84,7 @@ int main(int argc, char** argv) try {
         &MySensor::validate);
 
     std::vector<std::shared_ptr<ModelRegistration>> mrs = {mr};
-    auto my_mod = std::make_shared<ModuleService>(argc, argv, mrs, inst.registry());
+    auto my_mod = std::make_shared<ModuleService>(argc, argv, mrs);
     my_mod->serve();
 
     return EXIT_SUCCESS;
diff --git a/src/viam/examples/modules/tflite/main.cpp b/src/viam/examples/modules/tflite/main.cpp
index e1c806354..44cd8e284 100644
--- a/src/viam/examples/modules/tflite/main.cpp
+++ b/src/viam/examples/modules/tflite/main.cpp
@@ -23,7 +23,6 @@
 
 #include <tensorflow/lite/c/c_api.h>
 
-#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/components/component.hpp>
 #include <viam/sdk/config/resource.hpp>
@@ -724,10 +723,6 @@ class MLModelServiceTFLite : public vsdk::MLModelService,
 };
 
 int serve(const std::string& socket_path) try {
-    // Every Viam C++ SDK program must have one and only one Instance object which is created before
-    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
-    vsdk::Instance inst;
-
     // Create a new model registration for the service.
     auto module_registration = std::make_shared<vsdk::ModelRegistration>(
         // Identify that this resource offers the MLModelService API
@@ -742,10 +737,10 @@ int serve(const std::string& socket_path) try {
         });
 
     // Register the newly created registration with the Registry.
-    inst.registry()->register_model(module_registration);
+    vsdk::Registry::register_model(module_registration);
 
     // Construct the module service and tell it where to place the socket path.
-    auto module_service = std::make_shared<vsdk::ModuleService>(socket_path, inst.registry());
+    auto module_service = std::make_shared<vsdk::ModuleService>(socket_path);
 
     // Add the server as providing the API and model declared in the
     // registration.
diff --git a/src/viam/examples/motor/example_motor.cpp b/src/viam/examples/motor/example_motor.cpp
index 1a5ff50b7..c4d8023d5 100644
--- a/src/viam/examples/motor/example_motor.cpp
+++ b/src/viam/examples/motor/example_motor.cpp
@@ -6,7 +6,6 @@
 #include <string>
 #include <vector>
 
-#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/motor.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -28,11 +27,6 @@ int main() {
     namespace vs = ::viam::sdk;
 
     try {
-        // Every Viam C++ SDK program must have one and only one Instance object which is created
-        // before any other C++ SDK objects and stays alive until all Viam C++ SDK objects are
-        // destroyed.
-        vs::Instance inst;
-
         // If you want to connect to a remote robot, this should be the url of the robot
         // Ex: xxx.xxx.viam.cloud
         std::string robot_address("localhost:8080");
@@ -55,7 +49,7 @@ int main() {
 
         std::shared_ptr<vs::RobotClient> robot;
         try {
-            robot = vs::RobotClient::at_address(robot_address, options, inst.registry());
+            robot = vs::RobotClient::at_address(robot_address, options);
             cout << "Successfully connected to the robot" << endl;
         } catch (const std::exception& e) {
             cerr << "Failed to connect to the robot. Exiting." << endl;
diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp
index 041fb90b3..d644b5420 100644
--- a/src/viam/sdk/module/service.cpp
+++ b/src/viam/sdk/module/service.cpp
@@ -208,32 +208,29 @@ Dependencies ModuleService::get_dependencies_(
 
 std::shared_ptr<Resource> ModuleService::get_parent_resource_(const Name& name) {
     if (!parent_) {
-        parent_ = RobotClient::at_local_socket(parent_addr_, {0, boost::none}, registry_);
+        parent_ = RobotClient::at_local_socket(parent_addr_, {0, boost::none});
     }
 
     return parent_->resource_by_name(name);
 }
 
-ModuleService::ModuleService(std::string addr, Registry* registry)
-    : registry_(registry),
+ModuleService::ModuleService(std::string addr)
+    : registry_(&Registry::get()),
       module_(std::make_unique<Module>(std::move(addr))),
-      server_(std::make_unique<Server>(registry_)) {
+      server_(std::make_unique<Server>()) {
     impl_ = std::make_unique<ServiceImpl>(*this);
 }
 
 ModuleService::ModuleService(int argc,
                              char** argv,
-                             const std::vector<std::shared_ptr<ModelRegistration>>& registrations,
-                             Registry* registry)
-    : ModuleService(
-          [argc, argv] {
-              if (argc < 2) {
-                  throw Exception(ErrorCondition::k_connection,
-                                  "Need socket path as command line argument");
-              }
-              return argv[1];
-          }(),
-          registry) {
+                             const std::vector<std::shared_ptr<ModelRegistration>>& registrations)
+    : ModuleService([argc, argv] {
+          if (argc < 2) {
+              throw Exception(ErrorCondition::k_connection,
+                              "Need socket path as command line argument");
+          }
+          return argv[1];
+      }()) {
     set_logger_severity_from_args(argc, argv);
 
     for (auto&& mr : registrations) {
diff --git a/src/viam/sdk/module/service.hpp b/src/viam/sdk/module/service.hpp
index 0927d26a8..95a7ad4f3 100644
--- a/src/viam/sdk/module/service.hpp
+++ b/src/viam/sdk/module/service.hpp
@@ -31,7 +31,7 @@ class ModuleService {
    public:
     /// @brief Creates a new ModuleService that can serve on the provided socket.
     /// @param addr Address of socket to serve on.
-    explicit ModuleService(std::string addr, Registry* registry);
+    explicit ModuleService(std::string addr);
 
     /// @brief Creates a new ModuleService. Socket path and log level will be
     /// inferred from passed in command line arguments, and passed in model
@@ -41,8 +41,7 @@ class ModuleService {
     /// @param registrations Models to register and add to the module.
     explicit ModuleService(int argc,
                            char** argv,
-                           const std::vector<std::shared_ptr<ModelRegistration>>& registrations,
-                           Registry* registry);
+                           const std::vector<std::shared_ptr<ModelRegistration>>& registrations);
     ~ModuleService();
 
     /// @brief Starts module. serve will return when SIGINT or SIGTERM is received
diff --git a/src/viam/sdk/registry/registry.cpp b/src/viam/sdk/registry/registry.cpp
index 8f7209adf..1ee9b5533 100644
--- a/src/viam/sdk/registry/registry.cpp
+++ b/src/viam/sdk/registry/registry.cpp
@@ -12,6 +12,7 @@
 #include <grpcpp/ext/proto_server_reflection_plugin.h>
 
 #include <viam/sdk/common/exception.hpp>
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/private/arm_client.hpp>
 #include <viam/sdk/components/private/arm_server.hpp>
 #include <viam/sdk/components/private/base_client.hpp>
@@ -90,6 +91,10 @@ const Model& ModelRegistration::model() const {
     return model_;
 };
 
+Registry& Registry::get() {
+    return *Instance::current().registry();
+}
+
 void Registry::register_model(std::shared_ptr<const ModelRegistration> resource) {
     std::string reg_key = resource->api().to_string() + "/" + resource->model().to_string();
     if (resources_.find(reg_key) != resources_.end()) {
diff --git a/src/viam/sdk/registry/registry.hpp b/src/viam/sdk/registry/registry.hpp
index db476be97..f09ee2d62 100644
--- a/src/viam/sdk/registry/registry.hpp
+++ b/src/viam/sdk/registry/registry.hpp
@@ -103,6 +103,9 @@ class ModelRegistration {
 /// @brief A registry of known resources.
 class Registry {
    public:
+    /// @brief Get the application-wide instance of Registry.
+    static Registry& get();
+
     /// @brief Registers a resource with the Registry.
     /// @param resource An object containing resource registration information.
     /// @throws `Exception` if the resource has already been registered.
diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp
index cb72451c7..a8cb16c75 100644
--- a/src/viam/sdk/robot/client.cpp
+++ b/src/viam/sdk/robot/client.cpp
@@ -221,8 +221,8 @@ void RobotClient::refresh_every() {
     }
 };
 
-RobotClient::RobotClient(std::shared_ptr<ViamChannel> channel, Registry* registry)
-    : registry_(registry),
+RobotClient::RobotClient(std::shared_ptr<ViamChannel> channel)
+    : registry_(&Registry::get()),
       channel_(channel->channel()),
       viam_channel_(std::move(channel)),
       should_close_channel_(false),
@@ -234,10 +234,8 @@ std::vector<Name> RobotClient::resource_names() const {
 }
 
 std::shared_ptr<RobotClient> RobotClient::with_channel(std::shared_ptr<ViamChannel> channel,
-                                                       const Options& options,
-                                                       Registry* registry) {
-    std::shared_ptr<RobotClient> robot =
-        std::make_shared<RobotClient>(std::move(channel), registry);
+                                                       const Options& options) {
+    std::shared_ptr<RobotClient> robot = std::make_shared<RobotClient>(std::move(channel));
     robot->refresh_interval_ = options.refresh_interval();
     robot->should_refresh_ = (robot->refresh_interval_ > 0);
     if (robot->should_refresh_) {
@@ -255,25 +253,23 @@ std::shared_ptr<RobotClient> RobotClient::with_channel(std::shared_ptr<ViamChann
 };
 
 std::shared_ptr<RobotClient> RobotClient::at_address(const std::string& address,
-                                                     const Options& options,
-                                                     Registry* registry) {
+                                                     const Options& options) {
     const char* uri = address.c_str();
     auto channel = ViamChannel::dial(uri, options.dial_options());
-    std::shared_ptr<RobotClient> robot = RobotClient::with_channel(channel, options, registry);
+    std::shared_ptr<RobotClient> robot = RobotClient::with_channel(channel, options);
     robot->should_close_channel_ = true;
 
     return robot;
 };
 
 std::shared_ptr<RobotClient> RobotClient::at_local_socket(const std::string& address,
-                                                          const Options& options,
-                                                          Registry* registry) {
+                                                          const Options& options) {
     const std::string addr = "unix://" + address;
     const char* uri = addr.c_str();
     const std::shared_ptr<grpc::Channel> channel =
         sdk::impl::create_viam_channel(uri, grpc::InsecureChannelCredentials());
     auto viam_channel = std::make_shared<ViamChannel>(channel, address.c_str(), nullptr);
-    std::shared_ptr<RobotClient> robot = RobotClient::with_channel(viam_channel, options, registry);
+    std::shared_ptr<RobotClient> robot = RobotClient::with_channel(viam_channel, options);
     robot->should_close_channel_ = true;
 
     return robot;
diff --git a/src/viam/sdk/robot/client.hpp b/src/viam/sdk/robot/client.hpp
index 0a9610ac2..410abca28 100644
--- a/src/viam/sdk/robot/client.hpp
+++ b/src/viam/sdk/robot/client.hpp
@@ -12,16 +12,14 @@
 #include <viam/sdk/common/utils.hpp>
 #include <viam/sdk/common/world_state.hpp>
 #include <viam/sdk/components/component.hpp>
+#include <viam/sdk/registry/registry.hpp>
 #include <viam/sdk/resource/resource.hpp>
-#include <viam/sdk/resource/resource_manager.hpp>
 #include <viam/sdk/rpc/dial.hpp>
 #include <viam/sdk/services/service.hpp>
 
 namespace viam {
 namespace sdk {
 
-class Registry;
-
 /// @defgroup Robot Classes related to a Robot representation.
 
 /// @class RobotClient client.hpp "robot/client.hpp"
@@ -70,8 +68,7 @@ class RobotClient {
     /// @param address The address of the robot (IP address, URI, URL, etc.)
     /// @param options Options for connecting and refreshing.
     static std::shared_ptr<RobotClient> at_address(const std::string& address,
-                                                   const Options& options,
-                                                   Registry* registry);
+                                                   const Options& options);
 
     /// @brief Creates a robot client connected to the robot at the provided local socket.
     /// @param address The local socket of the robot (a .sock file, etc.).
@@ -79,8 +76,7 @@ class RobotClient {
     /// Creates a direct connection to the robot using the `unix://` scheme.
     /// Only useful for connecting to robots across Unix sockets.
     static std::shared_ptr<RobotClient> at_local_socket(const std::string& address,
-                                                        const Options& options,
-                                                        Registry* registry);
+                                                        const Options& options);
 
     /// @brief Creates a robot client connected to the provided channel.
     /// @param channel The channel to connect with.
@@ -88,11 +84,8 @@ class RobotClient {
     /// Connects directly to a pre-existing channel. A robot created this way must be
     /// `close()`d manually.
     static std::shared_ptr<RobotClient> with_channel(std::shared_ptr<ViamChannel> channel,
-                                                     const Options& options,
-                                                     Registry* registry);
-
-    RobotClient(std::shared_ptr<ViamChannel> channel, Registry* registry);
-
+                                                     const Options& options);
+    RobotClient(std::shared_ptr<ViamChannel> channel);
     std::vector<Name> resource_names() const;
 
     /// @brief Lookup and return a `shared_ptr` to a resource.
@@ -155,24 +148,17 @@ class RobotClient {
 
    private:
     Registry* registry_;
-
     std::vector<std::shared_ptr<std::thread>> threads_;
-
     std::atomic<bool> should_refresh_;
     unsigned int refresh_interval_;
-
     std::shared_ptr<GrpcChannel> channel_;
     std::shared_ptr<ViamChannel> viam_channel_;
     bool should_close_channel_;
-
     struct impl;
     std::unique_ptr<impl> impl_;
-
     mutable std::mutex lock_;
-
     std::vector<Name> resource_names_;
     ResourceManager resource_manager_;
-
     void refresh_every();
 };
 
diff --git a/src/viam/sdk/rpc/server.cpp b/src/viam/sdk/rpc/server.cpp
index b2c46c695..9964abbac 100644
--- a/src/viam/sdk/rpc/server.cpp
+++ b/src/viam/sdk/rpc/server.cpp
@@ -15,11 +15,11 @@
 namespace viam {
 namespace sdk {
 
-Server::Server(const Registry* registry) : builder_(std::make_unique<grpc::ServerBuilder>()) {
+Server::Server() : builder_(std::make_unique<grpc::ServerBuilder>()) {
     builder_->SetMaxReceiveMessageSize(kMaxMessageSize);
     builder_->SetMaxSendMessageSize(kMaxMessageSize);
     builder_->SetMaxMessageSize(kMaxMessageSize);
-    for (const auto& rr : registry->registered_resource_servers()) {
+    for (const auto& rr : Registry::get().registered_resource_servers()) {
         auto new_manager = std::make_shared<ResourceManager>();
         auto server = rr.second->create_resource_server(new_manager, *this);
         managed_servers_.emplace(rr.first, std::move(server));
diff --git a/src/viam/sdk/rpc/server.hpp b/src/viam/sdk/rpc/server.hpp
index 5f868b779..e735a6351 100644
--- a/src/viam/sdk/rpc/server.hpp
+++ b/src/viam/sdk/rpc/server.hpp
@@ -23,13 +23,11 @@ class TestServer;
 
 namespace sdk {
 
-class Registry;
-
 /// @class Server server.hpp "rpc/server.hpp"
 /// @brief Defines gRPC `Server` functionality.
 class Server {
    public:
-    Server(const Registry* registry);
+    Server();
     ~Server();
 
     /// @brief Starts the grpc server. Can only be called once.
diff --git a/src/viam/sdk/tests/test_robot.cpp b/src/viam/sdk/tests/test_robot.cpp
index e3bead0c9..2018ed36b 100644
--- a/src/viam/sdk/tests/test_robot.cpp
+++ b/src/viam/sdk/tests/test_robot.cpp
@@ -42,7 +42,7 @@ void robot_client_to_mocks_pipeline(F&& test_case) {
     rm->add(std::string("mock_generic"), generic::MockGenericComponent::get_mock_generic());
     rm->add(std::string("mock_motor"), motor::MockMotor::get_mock_motor());
     rm->add(std::string("mock_camera"), camera::MockCamera::get_mock_camera());
-    auto server = std::make_shared<sdk::Server>(Instance::current().registry());
+    auto server = std::make_shared<sdk::Server>();
     MockRobotService service(rm, *server);
     server->start();
 
@@ -51,8 +51,7 @@ void robot_client_to_mocks_pipeline(F&& test_case) {
     auto test_server = TestServer(server);
     auto grpc_channel = test_server.grpc_in_process_channel();
     auto viam_channel = std::make_shared<ViamChannel>(grpc_channel, "", nullptr);
-    auto client = RobotClient::with_channel(
-        viam_channel, Options(0, boost::none), Instance::current().registry());
+    auto client = RobotClient::with_channel(viam_channel, Options(0, boost::none));
 
     // Run the passed-in test case on the created stack and give access to the
     // created RobotClient and MockRobotService.
@@ -63,7 +62,7 @@ void robot_client_to_mocks_pipeline(F&& test_case) {
 }
 
 BOOST_AUTO_TEST_CASE(test_registering_resources) {
-    auto& registry = *Instance::current().registry();
+    auto& registry = Registry::get();
 
     // To test with mock resources we need to be able to create them, which means registering
     // constructors. This tests that we register correctly.
diff --git a/src/viam/sdk/tests/test_utils.hpp b/src/viam/sdk/tests/test_utils.hpp
index 3de43aed8..26a51444e 100644
--- a/src/viam/sdk/tests/test_utils.hpp
+++ b/src/viam/sdk/tests/test_utils.hpp
@@ -51,7 +51,7 @@ class TestServer {
 // The passed in test_case function will have access to the created ResourceClient.
 template <typename ResourceType, typename F>
 void client_to_mock_pipeline(std::shared_ptr<Resource> mock, F&& test_case) {
-    auto server = std::make_shared<sdk::Server>(sdk::Instance::current().registry());
+    auto server = std::make_shared<sdk::Server>();
 
     // normally the high level server service (either robot or module) handles adding managed
     // resources, but in this case we must do it ourselves.
@@ -63,9 +63,8 @@ void client_to_mock_pipeline(std::shared_ptr<Resource> mock, F&& test_case) {
     auto test_server = TestServer(server);
     auto grpc_channel = test_server.grpc_in_process_channel();
 
-    auto resource_client = sdk::Instance::current()
-                               .registry()
-                               ->lookup_resource_client(API::get<ResourceType>())
+    auto resource_client = sdk::Registry::get()
+                               .lookup_resource_client(API::get<ResourceType>())
                                ->create_rpc_client(mock->name(), std::move(grpc_channel));
 
     // Run the passed-in test case on the created stack and give access to the

From 18d26c92c4a0b39dc785c9306a3fcd803c736f0b Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 27 Feb 2025 13:51:47 -0500
Subject: [PATCH 15/86] revert and adapt examples

---
 src/viam/examples/camera/example_camera.cpp              | 6 ++++++
 src/viam/examples/dial/example_dial.cpp                  | 5 +++++
 src/viam/examples/dial_api_key/example_dial_api_key.cpp  | 5 +++++
 .../mlmodel/example_audio_classification_client.cpp      | 5 +++++
 src/viam/examples/modules/complex/client.cpp             | 5 +++++
 src/viam/examples/modules/complex/main.cpp               | 9 +++++++--
 .../examples/modules/complex/test_complex_module.cpp     | 5 +++--
 src/viam/examples/modules/simple/client.cpp              | 3 +++
 src/viam/examples/modules/simple/main.cpp                | 3 +++
 src/viam/examples/modules/tflite/main.cpp                | 9 +++++++--
 src/viam/examples/motor/example_motor.cpp                | 6 ++++++
 11 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/src/viam/examples/camera/example_camera.cpp b/src/viam/examples/camera/example_camera.cpp
index bc1b0ab9a..d1eb9b80c 100644
--- a/src/viam/examples/camera/example_camera.cpp
+++ b/src/viam/examples/camera/example_camera.cpp
@@ -3,6 +3,7 @@
 #include <unistd.h>
 #include <vector>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/camera.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -13,6 +14,11 @@ int main() {
     using std::endl;
     namespace vs = ::viam::sdk;
     try {
+        // Every Viam C++ SDK program must have one and only one Instance object which is created
+        // before
+        // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+        vs::Instance inst;
+
         // If you want to connect to a remote robot, this should be the url of the robot
         // Ex: xxx.xxx.viam.cloud
         std::string robot_address("localhost:8080");
diff --git a/src/viam/examples/dial/example_dial.cpp b/src/viam/examples/dial/example_dial.cpp
index efa783dfb..f39522c26 100644
--- a/src/viam/examples/dial/example_dial.cpp
+++ b/src/viam/examples/dial/example_dial.cpp
@@ -9,6 +9,7 @@
 
 #include <boost/optional.hpp>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/generic.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -16,6 +17,10 @@
 using namespace viam::sdk;
 
 int main() {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    Instance inst;
+
     const char* uri = "<your robot URI here>";
     DialOptions dial_options;
     std::string type = "<your authentication type>";
diff --git a/src/viam/examples/dial_api_key/example_dial_api_key.cpp b/src/viam/examples/dial_api_key/example_dial_api_key.cpp
index 4c47beffe..b723e4227 100644
--- a/src/viam/examples/dial_api_key/example_dial_api_key.cpp
+++ b/src/viam/examples/dial_api_key/example_dial_api_key.cpp
@@ -10,6 +10,7 @@
 #include <boost/optional.hpp>
 #include <boost/program_options.hpp>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
 
@@ -18,6 +19,10 @@ using namespace viam::sdk;
 namespace po = boost::program_options;
 
 int main(int argc, char* argv[]) {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    Instance inst;
+
     po::options_description desc("Allowed options");
     desc.add_options()("help", "List options and exit")(
         "uri", po::value<std::string>(), "URI of robot")(
diff --git a/src/viam/examples/mlmodel/example_audio_classification_client.cpp b/src/viam/examples/mlmodel/example_audio_classification_client.cpp
index 95e26f57c..6be570449 100644
--- a/src/viam/examples/mlmodel/example_audio_classification_client.cpp
+++ b/src/viam/examples/mlmodel/example_audio_classification_client.cpp
@@ -29,6 +29,7 @@
 #include <boost/program_options.hpp>
 #include <boost/variant/get.hpp>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/services/mlmodel.hpp>
 
@@ -78,6 +79,10 @@ constexpr char kRobotConfigTemplate[] = R"(
 }  // namespace
 
 int main(int argc, char* argv[]) try {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    viam::sdk::Instance inst;
+
     // Build up our command line options. The example operates in two
     // modes. In the "--generate" mode, it takes command line
     // parameters needed to satisfy the interpolation points in the
diff --git a/src/viam/examples/modules/complex/client.cpp b/src/viam/examples/modules/complex/client.cpp
index ac85c9f00..4c26f055f 100644
--- a/src/viam/examples/modules/complex/client.cpp
+++ b/src/viam/examples/modules/complex/client.cpp
@@ -11,6 +11,7 @@
 #include <grpcpp/grpcpp.h>
 #include <grpcpp/support/status.h>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/motor.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -21,6 +22,10 @@
 using namespace viam::sdk;
 
 int main() {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    Instance inst;
+
     const char* uri = "http://localhost:8080/";  // replace with your URI if connecting securely
     DialOptions dial_options;
     dial_options.set_allow_insecure_downgrade(true);  // set to false if connecting securely
diff --git a/src/viam/examples/modules/complex/main.cpp b/src/viam/examples/modules/complex/main.cpp
index da80453e6..c6a1e7532 100644
--- a/src/viam/examples/modules/complex/main.cpp
+++ b/src/viam/examples/modules/complex/main.cpp
@@ -5,6 +5,7 @@
 #include <grpcpp/grpcpp.h>
 #include <grpcpp/server_context.h>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/base.hpp>
 #include <viam/sdk/components/component.hpp>
 #include <viam/sdk/config/resource.hpp>
@@ -24,11 +25,15 @@
 using namespace viam::sdk;
 
 int main(int argc, char** argv) {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    Instance inst;
+
     Model mybase_model("viam", "base", "mybase");
 
     // Make sure to explicity register resources with custom APIs.
-    Registry::get().register_resource_server<GizmoServer>();
-    Registry::get().register_resource_server<SummationServer>();
+    inst.registry()->register_resource_server<GizmoServer>();
+    inst.registry()->register_resource_server<SummationServer>();
 
     std::shared_ptr<ModelRegistration> mybase_mr = std::make_shared<ModelRegistration>(
         API::get<Base>(),
diff --git a/src/viam/examples/modules/complex/test_complex_module.cpp b/src/viam/examples/modules/complex/test_complex_module.cpp
index 14d68a8cf..3f4d4ee3d 100644
--- a/src/viam/examples/modules/complex/test_complex_module.cpp
+++ b/src/viam/examples/modules/complex/test_complex_module.cpp
@@ -24,8 +24,9 @@ using namespace viam::sdktests;
 
 struct RegisterGizmoAndSummationFixture {
     RegisterGizmoAndSummationFixture() {
-        Registry::get().register_resource<GizmoClient, GizmoServer>();
-        Registry::get().register_resource<SummationClient, SummationServer>();
+        auto* registry = Instance::current().registry();
+        registry->register_resource<GizmoClient, GizmoServer>();
+        registry->register_resource<SummationClient, SummationServer>();
     }
 
     // Test teardown is a noop;
diff --git a/src/viam/examples/modules/simple/client.cpp b/src/viam/examples/modules/simple/client.cpp
index fcaab5eff..09e510636 100644
--- a/src/viam/examples/modules/simple/client.cpp
+++ b/src/viam/examples/modules/simple/client.cpp
@@ -2,6 +2,7 @@
 #include <memory>
 #include <string>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/components/sensor.hpp>
 #include <viam/sdk/robot/client.hpp>
@@ -10,6 +11,8 @@
 using namespace viam::sdk;
 
 int main() {
+    Instance inst;
+
     const char* uri = "http://localhost:8080/";  // replace with your URI if connecting securely
     DialOptions dial_options;
     dial_options.set_allow_insecure_downgrade(true);  // set to false if connecting securely
diff --git a/src/viam/examples/modules/simple/main.cpp b/src/viam/examples/modules/simple/main.cpp
index cfd783f66..0df75464b 100644
--- a/src/viam/examples/modules/simple/main.cpp
+++ b/src/viam/examples/modules/simple/main.cpp
@@ -3,6 +3,7 @@
 #include <sstream>
 
 #include <viam/sdk/common/exception.hpp>
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/components/sensor.hpp>
 #include <viam/sdk/config/resource.hpp>
@@ -75,6 +76,8 @@ ProtoStruct MySensor::get_readings(const ProtoStruct&) {
 }
 
 int main(int argc, char** argv) try {
+    Instance inst;
+
     Model mysensor_model("viam", "sensor", "mysensor");
 
     std::shared_ptr<ModelRegistration> mr = std::make_shared<ModelRegistration>(
diff --git a/src/viam/examples/modules/tflite/main.cpp b/src/viam/examples/modules/tflite/main.cpp
index 44cd8e284..e1c806354 100644
--- a/src/viam/examples/modules/tflite/main.cpp
+++ b/src/viam/examples/modules/tflite/main.cpp
@@ -23,6 +23,7 @@
 
 #include <tensorflow/lite/c/c_api.h>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/components/component.hpp>
 #include <viam/sdk/config/resource.hpp>
@@ -723,6 +724,10 @@ class MLModelServiceTFLite : public vsdk::MLModelService,
 };
 
 int serve(const std::string& socket_path) try {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    vsdk::Instance inst;
+
     // Create a new model registration for the service.
     auto module_registration = std::make_shared<vsdk::ModelRegistration>(
         // Identify that this resource offers the MLModelService API
@@ -737,10 +742,10 @@ int serve(const std::string& socket_path) try {
         });
 
     // Register the newly created registration with the Registry.
-    vsdk::Registry::register_model(module_registration);
+    inst.registry()->register_model(module_registration);
 
     // Construct the module service and tell it where to place the socket path.
-    auto module_service = std::make_shared<vsdk::ModuleService>(socket_path);
+    auto module_service = std::make_shared<vsdk::ModuleService>(socket_path, inst.registry());
 
     // Add the server as providing the API and model declared in the
     // registration.
diff --git a/src/viam/examples/motor/example_motor.cpp b/src/viam/examples/motor/example_motor.cpp
index c4d8023d5..3d5a640ef 100644
--- a/src/viam/examples/motor/example_motor.cpp
+++ b/src/viam/examples/motor/example_motor.cpp
@@ -6,6 +6,7 @@
 #include <string>
 #include <vector>
 
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/motor.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -27,6 +28,11 @@ int main() {
     namespace vs = ::viam::sdk;
 
     try {
+        // Every Viam C++ SDK program must have one and only one Instance object which is created
+        // before any other C++ SDK objects and stays alive until all Viam C++ SDK objects are
+        // destroyed.
+        vs::Instance inst;
+
         // If you want to connect to a remote robot, this should be the url of the robot
         // Ex: xxx.xxx.viam.cloud
         std::string robot_address("localhost:8080");

From 60183237def4a3aff5c2163efd31ce86630b45c6 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 27 Feb 2025 14:26:16 -0500
Subject: [PATCH 16/86] make instance more of a black box

---
 src/viam/examples/modules/complex/main.cpp               | 4 ++--
 .../examples/modules/complex/test_complex_module.cpp     | 6 +++---
 src/viam/examples/modules/tflite/main.cpp                | 2 +-
 src/viam/sdk/common/instance.cpp                         | 9 +--------
 src/viam/sdk/common/instance.hpp                         | 6 ++----
 src/viam/sdk/registry/registry.cpp                       | 4 ++--
 src/viam/sdk/tests/mocks/mock_robot.cpp                  | 2 +-
 7 files changed, 12 insertions(+), 21 deletions(-)

diff --git a/src/viam/examples/modules/complex/main.cpp b/src/viam/examples/modules/complex/main.cpp
index c6a1e7532..253e69d78 100644
--- a/src/viam/examples/modules/complex/main.cpp
+++ b/src/viam/examples/modules/complex/main.cpp
@@ -32,8 +32,8 @@ int main(int argc, char** argv) {
     Model mybase_model("viam", "base", "mybase");
 
     // Make sure to explicity register resources with custom APIs.
-    inst.registry()->register_resource_server<GizmoServer>();
-    inst.registry()->register_resource_server<SummationServer>();
+    Registry::get().register_resource_server<GizmoServer>();
+    Registry::get().register_resource_server<SummationServer>();
 
     std::shared_ptr<ModelRegistration> mybase_mr = std::make_shared<ModelRegistration>(
         API::get<Base>(),
diff --git a/src/viam/examples/modules/complex/test_complex_module.cpp b/src/viam/examples/modules/complex/test_complex_module.cpp
index 3f4d4ee3d..4a178271f 100644
--- a/src/viam/examples/modules/complex/test_complex_module.cpp
+++ b/src/viam/examples/modules/complex/test_complex_module.cpp
@@ -24,9 +24,9 @@ using namespace viam::sdktests;
 
 struct RegisterGizmoAndSummationFixture {
     RegisterGizmoAndSummationFixture() {
-        auto* registry = Instance::current().registry();
-        registry->register_resource<GizmoClient, GizmoServer>();
-        registry->register_resource<SummationClient, SummationServer>();
+        auto& registry = Registry::get();
+        registry.register_resource<GizmoClient, GizmoServer>();
+        registry.register_resource<SummationClient, SummationServer>();
     }
 
     // Test teardown is a noop;
diff --git a/src/viam/examples/modules/tflite/main.cpp b/src/viam/examples/modules/tflite/main.cpp
index e1c806354..c1224266f 100644
--- a/src/viam/examples/modules/tflite/main.cpp
+++ b/src/viam/examples/modules/tflite/main.cpp
@@ -742,7 +742,7 @@ int serve(const std::string& socket_path) try {
         });
 
     // Register the newly created registration with the Registry.
-    inst.registry()->register_model(module_registration);
+    Registry::get().register_model(module_registration);
 
     // Construct the module service and tell it where to place the socket path.
     auto module_service = std::make_shared<vsdk::ModuleService>(socket_path, inst.registry());
diff --git a/src/viam/sdk/common/instance.cpp b/src/viam/sdk/common/instance.cpp
index c366ad6c6..bc52367f6 100644
--- a/src/viam/sdk/common/instance.cpp
+++ b/src/viam/sdk/common/instance.cpp
@@ -1,6 +1,7 @@
 #include <viam/sdk/common/instance.hpp>
 
 #include <viam/sdk/common/exception.hpp>
+#include <viam/sdk/common/private/instance.hpp>
 #include <viam/sdk/registry/registry.hpp>
 
 #include <atomic>
@@ -17,10 +18,6 @@ std::atomic<Instance*> current_instance{nullptr};
 
 }  // namespace
 
-struct Instance::Impl {
-    Registry registry;
-};
-
 Instance::Instance() {
     Instance* expected = nullptr;
 
@@ -52,9 +49,5 @@ Instance& Instance::current() {
     return *current;
 }
 
-Registry* Instance::registry() {
-    return &(impl_->registry);
-}
-
 }  // namespace sdk
 }  // namespace viam
diff --git a/src/viam/sdk/common/instance.hpp b/src/viam/sdk/common/instance.hpp
index c1ab0961f..1c97a2479 100644
--- a/src/viam/sdk/common/instance.hpp
+++ b/src/viam/sdk/common/instance.hpp
@@ -5,8 +5,6 @@
 namespace viam {
 namespace sdk {
 
-class Registry;
-
 /// @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
@@ -19,9 +17,9 @@ class Instance {
 
     static Instance& current();
 
-    Registry* registry();
-
    private:
+    friend class Registry;
+
     struct Impl;
     std::unique_ptr<Impl> impl_;
 };
diff --git a/src/viam/sdk/registry/registry.cpp b/src/viam/sdk/registry/registry.cpp
index 1ee9b5533..b1e8fb604 100644
--- a/src/viam/sdk/registry/registry.cpp
+++ b/src/viam/sdk/registry/registry.cpp
@@ -12,7 +12,7 @@
 #include <grpcpp/ext/proto_server_reflection_plugin.h>
 
 #include <viam/sdk/common/exception.hpp>
-#include <viam/sdk/common/instance.hpp>
+#include <viam/sdk/common/private/instance.hpp>
 #include <viam/sdk/components/private/arm_client.hpp>
 #include <viam/sdk/components/private/arm_server.hpp>
 #include <viam/sdk/components/private/base_client.hpp>
@@ -92,7 +92,7 @@ const Model& ModelRegistration::model() const {
 };
 
 Registry& Registry::get() {
-    return *Instance::current().registry();
+    return Instance::current().impl_->registry;
 }
 
 void Registry::register_model(std::shared_ptr<const ModelRegistration> resource) {
diff --git a/src/viam/sdk/tests/mocks/mock_robot.cpp b/src/viam/sdk/tests/mocks/mock_robot.cpp
index 5dc03fcc2..ce9a62884 100644
--- a/src/viam/sdk/tests/mocks/mock_robot.cpp
+++ b/src/viam/sdk/tests/mocks/mock_robot.cpp
@@ -26,7 +26,7 @@ std::vector<Name> registered_models_for_resource(const std::shared_ptr<Resource>
     std::string resource_type;
     std::string resource_subtype;
     std::vector<Name> resource_names;
-    for (const auto& kv : Instance::current().registry()->registered_models()) {
+    for (const auto& kv : Registry::get().registered_models()) {
         const std::shared_ptr<const ModelRegistration> reg = kv.second;
         if (reg->api() == resource->api()) {
             resource_type = reg->api().resource_type();

From 5535616d43a2fabe14e9c5a5a7d5b5718252a924 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 27 Feb 2025 14:40:39 -0500
Subject: [PATCH 17/86] add impl instance to git

---
 src/viam/sdk/common/private/instance.hpp | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 src/viam/sdk/common/private/instance.hpp

diff --git a/src/viam/sdk/common/private/instance.hpp b/src/viam/sdk/common/private/instance.hpp
new file mode 100644
index 000000000..b1728de8c
--- /dev/null
+++ b/src/viam/sdk/common/private/instance.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <viam/sdk/common/instance.hpp>
+#include <viam/sdk/registry/registry.hpp>
+
+namespace viam {
+namespace sdk {
+
+struct Instance::Impl {
+    Registry registry;
+};
+
+}  // namespace sdk
+}  // namespace viam

From eeac195427f24641a37fceccaf4aa87fd4df6385 Mon Sep 17 00:00:00 2001
From: lia <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 3 Mar 2025 10:05:20 -0500
Subject: [PATCH 18/86] Add instance comment boilerplate

Co-authored-by: Ethan <ethanrodkin@proton.me>
---
 src/viam/examples/modules/simple/client.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/viam/examples/modules/simple/client.cpp b/src/viam/examples/modules/simple/client.cpp
index 09e510636..f08410336 100644
--- a/src/viam/examples/modules/simple/client.cpp
+++ b/src/viam/examples/modules/simple/client.cpp
@@ -11,6 +11,8 @@
 using namespace viam::sdk;
 
 int main() {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
     Instance inst;
 
     const char* uri = "http://localhost:8080/";  // replace with your URI if connecting securely

From 7560f6d0c965939d5ae388bd49766aa268c19e67 Mon Sep 17 00:00:00 2001
From: lia <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 3 Mar 2025 10:05:49 -0500
Subject: [PATCH 19/86] Add instance comment boilerplate

Co-authored-by: Ethan <ethanrodkin@proton.me>
---
 src/viam/examples/modules/simple/main.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/viam/examples/modules/simple/main.cpp b/src/viam/examples/modules/simple/main.cpp
index 0df75464b..e85c2fe3a 100644
--- a/src/viam/examples/modules/simple/main.cpp
+++ b/src/viam/examples/modules/simple/main.cpp
@@ -76,6 +76,8 @@ ProtoStruct MySensor::get_readings(const ProtoStruct&) {
 }
 
 int main(int argc, char** argv) try {
+    // Every Viam C++ SDK program must have one and only one Instance object which is created before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
     Instance inst;
 
     Model mysensor_model("viam", "sensor", "mysensor");

From af084037688853000bdf5284f4814c63cd645c3a Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 3 Mar 2025 13:18:52 -0500
Subject: [PATCH 20/86] connect robot client logging when parent of module
 service

---
 src/viam/sdk/CMakeLists.txt              |  1 +
 src/viam/sdk/log/private/log_backend.cpp |  7 ++++
 src/viam/sdk/log/private/log_backend.hpp | 31 ++++++++++++++++++
 src/viam/sdk/module/service.cpp          |  1 +
 src/viam/sdk/robot/client.cpp            | 41 +++++++++++++++++++++++-
 src/viam/sdk/robot/client.hpp            | 17 +++++++++-
 6 files changed, 96 insertions(+), 2 deletions(-)
 create mode 100644 src/viam/sdk/log/private/log_backend.cpp
 create mode 100644 src/viam/sdk/log/private/log_backend.hpp

diff --git a/src/viam/sdk/CMakeLists.txt b/src/viam/sdk/CMakeLists.txt
index 0ba964cc3..8970239dd 100644
--- a/src/viam/sdk/CMakeLists.txt
+++ b/src/viam/sdk/CMakeLists.txt
@@ -103,6 +103,7 @@ target_sources(viamsdk
     components/sensor.cpp
     components/servo.cpp
     config/resource.cpp
+    log/private/log_backend.cpp
     module/handler_map.cpp
     module/module.cpp
     module/service.cpp
diff --git a/src/viam/sdk/log/private/log_backend.cpp b/src/viam/sdk/log/private/log_backend.cpp
new file mode 100644
index 000000000..c7f3acf78
--- /dev/null
+++ b/src/viam/sdk/log/private/log_backend.cpp
@@ -0,0 +1,7 @@
+#include <viam/sdk/log/private/log_backend.hpp>
+
+namespace viam {
+namespace sdk {
+namespace impl {}
+}  // namespace sdk
+}  // namespace viam
diff --git a/src/viam/sdk/log/private/log_backend.hpp b/src/viam/sdk/log/private/log_backend.hpp
new file mode 100644
index 000000000..de1ed7038
--- /dev/null
+++ b/src/viam/sdk/log/private/log_backend.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <boost/log/core/record_view.hpp>
+#include <boost/log/sinks/basic_sink_backend.hpp>
+#include <boost/log/sinks/sync_frontend.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <viam/sdk/robot/client.hpp>
+
+namespace viam {
+namespace sdk {
+namespace impl {
+
+struct LogBackend : boost::log::sinks::basic_sink_backend<boost::log::sinks::synchronized_feeding> {
+    LogBackend(RobotClient* p) : parent(p) {}
+
+    void consume(const boost::log::record_view&);
+
+    static auto create(RobotClient* p) {
+        auto backend = boost::make_shared<LogBackend>(p);
+        return boost::make_shared<boost::log::sinks::synchronous_sink<LogBackend>>(backend);
+    }
+
+    RobotClient* parent;
+};
+
+using SinkType = boost::log::sinks::synchronous_sink<LogBackend>;
+
+}  // namespace impl
+}  // namespace sdk
+}  // namespace viam
diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp
index d644b5420..ea3f5fccb 100644
--- a/src/viam/sdk/module/service.cpp
+++ b/src/viam/sdk/module/service.cpp
@@ -209,6 +209,7 @@ Dependencies ModuleService::get_dependencies_(
 std::shared_ptr<Resource> ModuleService::get_parent_resource_(const Name& name) {
     if (!parent_) {
         parent_ = RobotClient::at_local_socket(parent_addr_, {0, boost::none});
+        parent_->connect_logging();
     }
 
     return parent_->resource_by_name(name);
diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp
index a8cb16c75..a20716a76 100644
--- a/src/viam/sdk/robot/client.cpp
+++ b/src/viam/sdk/robot/client.cpp
@@ -9,6 +9,7 @@
 #include <unistd.h>
 #include <vector>
 
+#include <boost/log/core/core.hpp>
 #include <boost/log/trivial.hpp>
 #include <grpcpp/channel.h>
 #include <grpcpp/client_context.h>
@@ -24,6 +25,7 @@
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/common/utils.hpp>
 #include <viam/sdk/components/component.hpp>
+#include <viam/sdk/log/private/log_backend.hpp>
 #include <viam/sdk/registry/registry.hpp>
 #include <viam/sdk/resource/resource.hpp>
 #include <viam/sdk/rpc/dial.hpp>
@@ -84,9 +86,26 @@ bool operator==(const RobotClient::operation& lhs, const RobotClient::operation&
 
 struct RobotClient::impl {
     impl(std::unique_ptr<RobotService::Stub> stub) : stub_(std::move(stub)) {}
+
+    ~impl() {
+        if (log_sink) {
+            boost::log::core::get()->remove_sink(log_sink);
+        }
+    }
+
     std::unique_ptr<RobotService::Stub> stub_;
+
+    boost::shared_ptr<viam::sdk::impl::SinkType> log_sink;
 };
 
+void RobotClient::connect_logging() {
+    auto& sink = impl_->log_sink;
+    if (!sink) {
+        sink = sdk::impl::LogBackend::create(this);
+        boost::log::core::get()->add_sink(sink);
+    }
+}
+
 RobotClient::~RobotClient() {
     if (should_close_channel_) {
         try {
@@ -226,13 +245,33 @@ RobotClient::RobotClient(std::shared_ptr<ViamChannel> channel)
       channel_(channel->channel()),
       viam_channel_(std::move(channel)),
       should_close_channel_(false),
-      impl_(std::make_unique<impl>(RobotService::NewStub(channel_))) {}
+      impl_(std::make_unique<impl>(RobotService::NewStub(channel_), this)) {}
 
 std::vector<Name> RobotClient::resource_names() const {
     const std::lock_guard<std::mutex> lock(lock_);
     return resource_names_;
 }
 
+void RobotClient::log(
+    const std::string& name,
+    const std::string& level,
+    const std::string& message,
+    std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> time) {
+    robot::v1::LogRequest req;
+    common::v1::LogEntry log;
+
+    *log.mutable_logger_name() = name;
+    log.set_level(level);
+    *log.mutable_message() = message;
+    (void)time;
+    req.mutable_logs()->Add(std::move(log));
+
+    robot::v1::LogResponse resp;
+    ClientContext ctx;
+    const auto response = impl_->stub_->Log(ctx, req, &resp);
+    (void)response;
+}
+
 std::shared_ptr<RobotClient> RobotClient::with_channel(std::shared_ptr<ViamChannel> channel,
                                                        const Options& options) {
     std::shared_ptr<RobotClient> robot = std::make_shared<RobotClient>(std::move(channel));
diff --git a/src/viam/sdk/robot/client.hpp b/src/viam/sdk/robot/client.hpp
index 410abca28..473ecf5a9 100644
--- a/src/viam/sdk/robot/client.hpp
+++ b/src/viam/sdk/robot/client.hpp
@@ -85,7 +85,16 @@ class RobotClient {
     /// `close()`d manually.
     static std::shared_ptr<RobotClient> with_channel(std::shared_ptr<ViamChannel> channel,
                                                      const Options& options);
+
     RobotClient(std::shared_ptr<ViamChannel> channel);
+
+    void log(const std::string& name,
+             const std::string& level,
+             const std::string& message,
+             std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> time);
+
+    void connect_logging();
+
     std::vector<Name> resource_names() const;
 
     /// @brief Lookup and return a `shared_ptr` to a resource.
@@ -147,19 +156,25 @@ class RobotClient {
     status get_machine_status() const;
 
    private:
+    void refresh_every();
+
     Registry* registry_;
+
     std::vector<std::shared_ptr<std::thread>> threads_;
     std::atomic<bool> should_refresh_;
     unsigned int refresh_interval_;
+
     std::shared_ptr<GrpcChannel> channel_;
     std::shared_ptr<ViamChannel> viam_channel_;
     bool should_close_channel_;
+
     struct impl;
     std::unique_ptr<impl> impl_;
+
     mutable std::mutex lock_;
+
     std::vector<Name> resource_names_;
     ResourceManager resource_manager_;
-    void refresh_every();
 };
 
 }  // namespace sdk

From 4e25d35b30bfaa3fae2452576e23c32da9c57690 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 3 Mar 2025 14:52:30 -0500
Subject: [PATCH 21/86] remove old ctor arg

---
 src/viam/examples/modules/tflite/main.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/viam/examples/modules/tflite/main.cpp b/src/viam/examples/modules/tflite/main.cpp
index c1224266f..65ca8c191 100644
--- a/src/viam/examples/modules/tflite/main.cpp
+++ b/src/viam/examples/modules/tflite/main.cpp
@@ -745,7 +745,7 @@ int serve(const std::string& socket_path) try {
     Registry::get().register_model(module_registration);
 
     // Construct the module service and tell it where to place the socket path.
-    auto module_service = std::make_shared<vsdk::ModuleService>(socket_path, inst.registry());
+    auto module_service = std::make_shared<vsdk::ModuleService>(socket_path);
 
     // Add the server as providing the API and model declared in the
     // registration.

From 55baa6c00d4d3de8eeaaeb2befb13423db23c143 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 3 Mar 2025 14:53:26 -0500
Subject: [PATCH 22/86] prohibit multi instance

---
 src/viam/sdk/common/instance.hpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/viam/sdk/common/instance.hpp b/src/viam/sdk/common/instance.hpp
index 1c97a2479..64bb430c2 100644
--- a/src/viam/sdk/common/instance.hpp
+++ b/src/viam/sdk/common/instance.hpp
@@ -8,8 +8,8 @@ namespace sdk {
 /// @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
-/// remain alive in a valid state for the duration of the program. Creating multiple overlapping
-/// Instance objects is an error.
+/// remain alive in a valid state for the duration of the program. Creating multiple Instance
+/// objects in the same program is an error.
 class Instance {
    public:
     Instance();

From 10cd711441b03ea12bea259ba27013d622a0a8a3 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 3 Mar 2025 14:54:54 -0500
Subject: [PATCH 23/86] remove initialized flag

---
 src/viam/sdk/registry/registry.cpp | 7 -------
 src/viam/sdk/registry/registry.hpp | 3 ++-
 2 files changed, 2 insertions(+), 8 deletions(-)

diff --git a/src/viam/sdk/registry/registry.cpp b/src/viam/sdk/registry/registry.cpp
index b1e8fb604..15b1f118b 100644
--- a/src/viam/sdk/registry/registry.cpp
+++ b/src/viam/sdk/registry/registry.cpp
@@ -219,13 +219,6 @@ void Registry::register_resources() {
 }
 
 void Registry::initialize() {
-    const std::lock_guard<std::mutex> lock(lock_);
-    if (initialized_) {
-        BOOST_LOG_TRIVIAL(warning)
-            << "Attempted to initialize the Registry but it was already initialized.";
-        return;
-    }
-    initialized_ = true;
     register_resources();
     grpc::reflection::InitProtoReflectionServerBuilderPlugin();
 }
diff --git a/src/viam/sdk/registry/registry.hpp b/src/viam/sdk/registry/registry.hpp
index f09ee2d62..c1a5055a3 100644
--- a/src/viam/sdk/registry/registry.hpp
+++ b/src/viam/sdk/registry/registry.hpp
@@ -194,8 +194,9 @@ class Registry {
     void initialize();
 
     mutable std::mutex lock_;
-    bool initialized_{false};
+
     std::unordered_map<std::string, std::shared_ptr<const ModelRegistration>> resources_;
+
     std::unordered_map<API, std::shared_ptr<const ResourceClientRegistration>> client_apis_;
     std::unordered_map<API, std::shared_ptr<const ResourceServerRegistration>> server_apis_;
 

From c74542296e2513e09281450fdb6e1452b72129a5 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 3 Mar 2025 14:55:54 -0500
Subject: [PATCH 24/86] meyers singleton registry

---
 src/viam/sdk/registry/registry.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/viam/sdk/registry/registry.cpp b/src/viam/sdk/registry/registry.cpp
index 15b1f118b..e538d2ec2 100644
--- a/src/viam/sdk/registry/registry.cpp
+++ b/src/viam/sdk/registry/registry.cpp
@@ -92,7 +92,9 @@ const Model& ModelRegistration::model() const {
 };
 
 Registry& Registry::get() {
-    return Instance::current().impl_->registry;
+    static Registry& result = Instance::current().impl_->registry;
+
+    return result;
 }
 
 void Registry::register_model(std::shared_ptr<const ModelRegistration> resource) {

From 75ab450a7fa122ff93e50ff9226ab9c49f003177 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 3 Mar 2025 15:13:33 -0500
Subject: [PATCH 25/86] remove registry member and fix member spacing/ordering

---
 src/viam/sdk/module/service.cpp | 14 ++++++--------
 src/viam/sdk/module/service.hpp |  2 --
 src/viam/sdk/robot/client.cpp   |  6 +++---
 src/viam/sdk/robot/client.hpp   |  9 +++++++--
 4 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp
index d644b5420..911be9bb3 100644
--- a/src/viam/sdk/module/service.cpp
+++ b/src/viam/sdk/module/service.cpp
@@ -57,7 +57,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         std::shared_ptr<Resource> res;
         const Dependencies deps = parent.get_dependencies_(&request->dependencies(), cfg.name());
         const std::shared_ptr<const ModelRegistration> reg =
-            parent.registry_->lookup_model(cfg.api(), cfg.model());
+            Registry::get().lookup_model(cfg.api(), cfg.model());
         if (reg) {
             try {
                 res = reg->construct_resource(deps, cfg);
@@ -112,7 +112,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         }
 
         const std::shared_ptr<const ModelRegistration> reg =
-            parent.registry_->lookup_model(cfg.name());
+            Registry::get().lookup_model(cfg.name());
         if (reg) {
             try {
                 const std::shared_ptr<Resource> res = reg->construct_resource(deps, cfg);
@@ -132,7 +132,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         ResourceConfig cfg = from_proto(proto);
 
         const std::shared_ptr<const ModelRegistration> reg =
-            parent.registry_->lookup_model(cfg.api(), cfg.model());
+            Registry::get().lookup_model(cfg.api(), cfg.model());
         if (!reg) {
             return grpc::Status(grpc::UNKNOWN,
                                 "unable to validate resource " + cfg.resource_name().name() +
@@ -215,9 +215,7 @@ std::shared_ptr<Resource> ModuleService::get_parent_resource_(const Name& name)
 }
 
 ModuleService::ModuleService(std::string addr)
-    : registry_(&Registry::get()),
-      module_(std::make_unique<Module>(std::move(addr))),
-      server_(std::make_unique<Server>()) {
+    : module_(std::make_unique<Module>(std::move(addr))), server_(std::make_unique<Server>()) {
     impl_ = std::make_unique<ServiceImpl>(*this);
 }
 
@@ -234,7 +232,7 @@ ModuleService::ModuleService(int argc,
     set_logger_severity_from_args(argc, argv);
 
     for (auto&& mr : registrations) {
-        registry_->register_model(mr);
+        Registry::get().register_model(mr);
         add_model_from_registry(mr->api(), mr->model());
     }
 }
@@ -277,7 +275,7 @@ void ModuleService::add_model_from_registry_inlock_(API api,
                                                     Model model,
                                                     const std::lock_guard<std::mutex>&) {
     const std::shared_ptr<const ResourceServerRegistration> creator =
-        registry_->lookup_resource_server(api);
+        Registry::get().lookup_resource_server(api);
     std::string name;
     if (creator && creator->service_descriptor()) {
         name = creator->service_descriptor()->full_name();
diff --git a/src/viam/sdk/module/service.hpp b/src/viam/sdk/module/service.hpp
index 95a7ad4f3..799ba1ab5 100644
--- a/src/viam/sdk/module/service.hpp
+++ b/src/viam/sdk/module/service.hpp
@@ -64,8 +64,6 @@ class ModuleService {
                                    std::string const& resource_name);
     std::shared_ptr<Resource> get_parent_resource_(const Name& name);
 
-    Registry* registry_;
-
     std::mutex lock_;
     std::unique_ptr<Module> module_;
     std::shared_ptr<RobotClient> parent_;
diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp
index a8cb16c75..6c2f0f0fe 100644
--- a/src/viam/sdk/robot/client.cpp
+++ b/src/viam/sdk/robot/client.cpp
@@ -178,7 +178,8 @@ void RobotClient::refresh() {
         // are being properly registered from name.subtype(), or update what we're
         // using for lookup
         const std::shared_ptr<const ResourceClientRegistration> rs =
-            registry_->lookup_resource_client({name.namespace_(), name.type(), name.subtype()});
+            Registry::get().lookup_resource_client(
+                {name.namespace_(), name.type(), name.subtype()});
         if (rs) {
             try {
                 const std::shared_ptr<Resource> rpc_client =
@@ -222,8 +223,7 @@ void RobotClient::refresh_every() {
 };
 
 RobotClient::RobotClient(std::shared_ptr<ViamChannel> channel)
-    : registry_(&Registry::get()),
-      channel_(channel->channel()),
+    : channel_(channel->channel()),
       viam_channel_(std::move(channel)),
       should_close_channel_(false),
       impl_(std::make_unique<impl>(RobotService::NewStub(channel_))) {}
diff --git a/src/viam/sdk/robot/client.hpp b/src/viam/sdk/robot/client.hpp
index 410abca28..50fda612c 100644
--- a/src/viam/sdk/robot/client.hpp
+++ b/src/viam/sdk/robot/client.hpp
@@ -147,19 +147,24 @@ class RobotClient {
     status get_machine_status() const;
 
    private:
-    Registry* registry_;
+    void refresh_every();
+
     std::vector<std::shared_ptr<std::thread>> threads_;
+
     std::atomic<bool> should_refresh_;
     unsigned int refresh_interval_;
+
     std::shared_ptr<GrpcChannel> channel_;
     std::shared_ptr<ViamChannel> viam_channel_;
     bool should_close_channel_;
+
     struct impl;
     std::unique_ptr<impl> impl_;
+
     mutable std::mutex lock_;
+
     std::vector<Name> resource_names_;
     ResourceManager resource_manager_;
-    void refresh_every();
 };
 
 }  // namespace sdk

From fc21c356bf455947c5cd8d3dd68b174b354a5766 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 3 Mar 2025 15:19:14 -0500
Subject: [PATCH 26/86] remove unused include

---
 src/viam/sdk/tests/test_utils.hpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/viam/sdk/tests/test_utils.hpp b/src/viam/sdk/tests/test_utils.hpp
index 26a51444e..11519a551 100644
--- a/src/viam/sdk/tests/test_utils.hpp
+++ b/src/viam/sdk/tests/test_utils.hpp
@@ -2,7 +2,6 @@
 
 #include <grpcpp/grpcpp.h>
 
-#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/config/resource.hpp>
 #include <viam/sdk/registry/registry.hpp>
 #include <viam/sdk/resource/resource.hpp>

From 795578ce39af04ef0fdfc1d5e7a94ef179b94a64 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 4 Mar 2025 15:46:00 -0500
Subject: [PATCH 27/86] first attempt full logger machinery

---
 src/viam/sdk/CMakeLists.txt              |  2 +
 src/viam/sdk/common/instance.cpp         |  1 +
 src/viam/sdk/common/instance.hpp         |  1 +
 src/viam/sdk/common/private/instance.hpp |  2 +
 src/viam/sdk/log/logger.cpp              | 88 ++++++++++++++++++++++++
 src/viam/sdk/log/logger.hpp              | 58 ++++++++++++++++
 src/viam/sdk/log/private/keywords.hpp    | 18 +++++
 src/viam/sdk/log/private/log_backend.cpp | 11 ++-
 src/viam/sdk/log/private/log_backend.hpp |  1 -
 src/viam/sdk/resource/resource.hpp       |  6 +-
 src/viam/sdk/robot/client.cpp            |  2 +
 11 files changed, 186 insertions(+), 4 deletions(-)
 create mode 100644 src/viam/sdk/log/logger.cpp
 create mode 100644 src/viam/sdk/log/logger.hpp
 create mode 100644 src/viam/sdk/log/private/keywords.hpp

diff --git a/src/viam/sdk/CMakeLists.txt b/src/viam/sdk/CMakeLists.txt
index 8970239dd..efbd6d2fd 100644
--- a/src/viam/sdk/CMakeLists.txt
+++ b/src/viam/sdk/CMakeLists.txt
@@ -103,6 +103,7 @@ target_sources(viamsdk
     components/sensor.cpp
     components/servo.cpp
     config/resource.cpp
+    log/logger.cpp
     log/private/log_backend.cpp
     module/handler_map.cpp
     module/module.cpp
@@ -171,6 +172,7 @@ target_sources(viamsdk
       ../../viam/sdk/components/sensor.hpp
       ../../viam/sdk/components/servo.hpp
       ../../viam/sdk/config/resource.hpp
+      ../../viam/sdk/log/logger.hpp
       ../../viam/sdk/module/handler_map.hpp
       ../../viam/sdk/module/module.hpp
       ../../viam/sdk/module/service.hpp
diff --git a/src/viam/sdk/common/instance.cpp b/src/viam/sdk/common/instance.cpp
index bc52367f6..54c20ae18 100644
--- a/src/viam/sdk/common/instance.cpp
+++ b/src/viam/sdk/common/instance.cpp
@@ -27,6 +27,7 @@ Instance::Instance() {
 
     impl_ = std::make_unique<Instance::Impl>();
     impl_->registry.initialize();
+    impl_->logger.init_logging();
 }
 
 Instance::~Instance() {
diff --git a/src/viam/sdk/common/instance.hpp b/src/viam/sdk/common/instance.hpp
index 64bb430c2..4bec87110 100644
--- a/src/viam/sdk/common/instance.hpp
+++ b/src/viam/sdk/common/instance.hpp
@@ -19,6 +19,7 @@ class Instance {
 
    private:
     friend class Registry;
+    friend class Logger;
 
     struct Impl;
     std::unique_ptr<Impl> impl_;
diff --git a/src/viam/sdk/common/private/instance.hpp b/src/viam/sdk/common/private/instance.hpp
index b1728de8c..622cfe354 100644
--- a/src/viam/sdk/common/private/instance.hpp
+++ b/src/viam/sdk/common/private/instance.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <viam/sdk/common/instance.hpp>
+#include <viam/sdk/log/logger.hpp>
 #include <viam/sdk/registry/registry.hpp>
 
 namespace viam {
@@ -8,6 +9,7 @@ namespace sdk {
 
 struct Instance::Impl {
     Registry registry;
+    Logger logger;
 };
 
 }  // namespace sdk
diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
new file mode 100644
index 000000000..046b1b9b0
--- /dev/null
+++ b/src/viam/sdk/log/logger.cpp
@@ -0,0 +1,88 @@
+#include <viam/sdk/log/logger.hpp>
+
+#include <iostream>
+
+#include <boost/log/attributes.hpp>
+#include <boost/log/expressions.hpp>
+#include <boost/log/expressions/formatters/date_time.hpp>
+#include <boost/log/expressions/keyword.hpp>
+#include <boost/log/support/date_time.hpp>
+#include <boost/log/utility/setup/console.hpp>
+
+#include <viam/sdk/common/instance.hpp>
+#include <viam/sdk/common/private/instance.hpp>
+#include <viam/sdk/log/private/keywords.hpp>
+
+namespace viam {
+namespace sdk {
+
+std::string to_string(log_level lvl) {
+    switch (lvl) {
+        case log_level::trace:
+            return "trace";
+        case log_level::debug:
+            return "debug";
+        case log_level::info:
+            return "info";
+        case log_level::warning:
+            return "warning";
+        case log_level::error:  // fallthrough
+        case log_level::fatal:
+            return "error";
+        default:
+            return std::to_string(static_cast<std::underlying_type_t<log_level>>(lvl));
+    }
+}
+
+std::ostream& operator<<(std::ostream& os, log_level lvl) {
+    os << to_string(lvl);
+    return os;
+}
+
+std::string global_resource_name() {
+    return "Viam C++ SDK";
+}
+
+bool Logger::Filter::operator()(const boost::log::attribute_value_set& attrs) {
+    auto sev = attrs[attr_sev];
+    if (!sev) {
+        return false;
+    }
+
+    auto resource = attrs[attr_channel];
+    if (resource) {
+        auto it = parent->resource_levels_.find(*resource);
+        if (it != parent->resource_levels_.end()) {
+            return *sev >= it->second;
+        }
+    }
+
+    return *sev >= parent->global_level_;
+}
+
+Logger& Logger::get() {
+    static Logger& result = Instance::current().impl_->logger;
+
+    return result;
+}
+
+void Logger::init_logging() {
+    sdk_logger_.channel(global_resource_name());
+
+    boost::log::formatter fmt =
+        boost::log::expressions::stream
+        << boost::log::expressions::format_date_time<boost::posix_time::ptime>(
+               "TimeStamp", "%Y--%m--%d %H:%M:%S")
+        << ": [" << attr_channel << "] <" << attr_sev << "> [" << attr_file << ":" << attr_line
+        << "]" << boost::log::expressions::smessage;
+
+    boost::log::add_console_log(
+        std::cout, boost::log::keywords::filter = Filter{this}, boost::log::keywords::format = fmt);
+
+    boost::shared_ptr<boost::log::core> core = boost::log::core::get();
+
+    core->add_global_attribute("TimeStamp", boost::log::attributes::local_clock());
+}
+
+}  // namespace sdk
+}  // namespace viam
diff --git a/src/viam/sdk/log/logger.hpp b/src/viam/sdk/log/logger.hpp
new file mode 100644
index 000000000..0379eebfc
--- /dev/null
+++ b/src/viam/sdk/log/logger.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <cstdint>
+
+#include <map>
+#include <memory>
+#include <ostream>
+
+#include <boost/log/sources/severity_channel_logger.hpp>
+
+namespace viam {
+namespace sdk {
+
+enum class log_level : std::int8_t {
+    trace = -2,
+    debug = -1,
+    info = 0,  // default value is info
+    warning = 1,
+    error = 2,
+    fatal = 3,
+};
+
+std::string to_string(log_level);
+
+std::ostream& operator<<(std::ostream&, log_level);
+
+using LogSource = boost::log::sources::severity_channel_logger_mt<log_level>;
+
+std::string global_resource_name();
+
+class Logger {
+   public:
+    struct Filter {
+        Logger* parent;
+
+        bool operator()(const boost::log::attribute_value_set&);
+    };
+
+    void set_global_log_level(log_level);
+    void set_resource_log_level(std::string resource, log_level);
+
+    static Logger& get();
+
+   private:
+    friend class Instance;
+    Logger() = default;
+
+    void init_logging();
+
+    LogSource sdk_logger_;
+
+    log_level global_level_{log_level::info};
+
+    std::map<std::string, log_level> resource_levels_;
+};
+
+}  // namespace sdk
+}  // namespace viam
diff --git a/src/viam/sdk/log/private/keywords.hpp b/src/viam/sdk/log/private/keywords.hpp
new file mode 100644
index 000000000..4a5c53d87
--- /dev/null
+++ b/src/viam/sdk/log/private/keywords.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <boost/log/attributes/clock.hpp>
+#include <boost/log/expressions/keyword.hpp>
+
+namespace viam {
+namespace sdk {
+
+BOOST_LOG_ATTRIBUTE_KEYWORD(attr_channel, "Channel", std::string);
+BOOST_LOG_ATTRIBUTE_KEYWORD(attr_sev, "Severity", viam::sdk::log_level);
+BOOST_LOG_ATTRIBUTE_KEYWORD(attr_file, "file", const char*);
+BOOST_LOG_ATTRIBUTE_KEYWORD(attr_line, "line", unsigned int);
+BOOST_LOG_ATTRIBUTE_KEYWORD(attr_time,
+                            "TimeStamp",
+                            boost::log::attributes::local_clock::value_type);
+
+}  // namespace sdk
+}  // namespace viam
diff --git a/src/viam/sdk/log/private/log_backend.cpp b/src/viam/sdk/log/private/log_backend.cpp
index d7eda691d..ca104cec7 100644
--- a/src/viam/sdk/log/private/log_backend.cpp
+++ b/src/viam/sdk/log/private/log_backend.cpp
@@ -1,10 +1,19 @@
 #include <viam/sdk/log/private/log_backend.hpp>
 
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <viam/sdk/log/private/keywords.hpp>
+
 namespace viam {
 namespace sdk {
 namespace impl {
 
-void LogBackend::consume(const boost::log::record_view& rec) {}
+void LogBackend::consume(const boost::log::record_view& rec) {
+    // auto time = *rec[attr_time];
+
+    parent->log(
+        *rec[attr_channel], to_string(*rec[attr_sev]), *rec[boost::log::expressions::smessage], {});
+}
 
 boost::shared_ptr<SinkType> LogBackend::create(RobotClient* p) {
     auto backend = boost::make_shared<LogBackend>(p);
diff --git a/src/viam/sdk/log/private/log_backend.hpp b/src/viam/sdk/log/private/log_backend.hpp
index 62ade1c14..8bb2dc4cb 100644
--- a/src/viam/sdk/log/private/log_backend.hpp
+++ b/src/viam/sdk/log/private/log_backend.hpp
@@ -3,7 +3,6 @@
 #include <boost/log/core/record_view.hpp>
 #include <boost/log/sinks/basic_sink_backend.hpp>
 #include <boost/log/sinks/sync_frontend.hpp>
-#include <boost/smart_ptr/make_shared.hpp>
 
 #include <viam/sdk/robot/client.hpp>
 
diff --git a/src/viam/sdk/resource/resource.hpp b/src/viam/sdk/resource/resource.hpp
index d61e5eb04..918a988f0 100644
--- a/src/viam/sdk/resource/resource.hpp
+++ b/src/viam/sdk/resource/resource.hpp
@@ -2,18 +2,20 @@
 
 #include <unordered_map>
 
-#include <boost/log/sources/severity_channel_logger.hpp>
 #include <boost/log/trivial.hpp>
 
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/config/resource.hpp>
+#include <viam/sdk/log/logger.hpp>
 #include <viam/sdk/resource/resource_api.hpp>
 
 namespace viam {
 namespace sdk {
 
 class Resource;
+
 using Dependencies = std::unordered_map<Name, std::shared_ptr<Resource>>;
+
 class Resource {
    public:
     virtual ~Resource();
@@ -34,7 +36,7 @@ class Resource {
    protected:
     Name get_resource_name(const std::string& type) const;
 
-    boost::log::sources::severity_channel_logger_mt<boost::log::trivial::severity_level> logger_;
+    LogSource logger_;
 };
 
 template <>
diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp
index 32e7a665a..2ec71752f 100644
--- a/src/viam/sdk/robot/client.cpp
+++ b/src/viam/sdk/robot/client.cpp
@@ -102,6 +102,8 @@ void RobotClient::connect_logging() {
     auto& sink = impl_->log_sink;
     if (!sink) {
         sink = sdk::impl::LogBackend::create(this);
+        sink->set_filter(Logger::Filter{&Logger::get()});
+
         boost::log::core::get()->add_sink(sink);
     }
 }

From 1b88d1c084f838d133c9efa7045a550ee61f5656 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 7 Mar 2025 13:42:50 -0500
Subject: [PATCH 28/86] update log to have keywords in header

---
 src/viam/sdk/log/logger.cpp              | 41 ++++++++++++++++------
 src/viam/sdk/log/logger.hpp              | 44 ++++++++++++++++++++++--
 src/viam/sdk/log/private/keywords.hpp    | 18 ----------
 src/viam/sdk/log/private/log_backend.cpp | 18 +++++++---
 4 files changed, 87 insertions(+), 34 deletions(-)
 delete mode 100644 src/viam/sdk/log/private/keywords.hpp

diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index 046b1b9b0..d3046e82a 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -2,6 +2,7 @@
 
 #include <iostream>
 
+#include <boost/core/null_deleter.hpp>
 #include <boost/log/attributes.hpp>
 #include <boost/log/expressions.hpp>
 #include <boost/log/expressions/formatters/date_time.hpp>
@@ -11,7 +12,6 @@
 
 #include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/private/instance.hpp>
-#include <viam/sdk/log/private/keywords.hpp>
 
 namespace viam {
 namespace sdk {
@@ -24,7 +24,7 @@ std::string to_string(log_level lvl) {
             return "debug";
         case log_level::info:
             return "info";
-        case log_level::warning:
+        case log_level::warn:
             return "warning";
         case log_level::error:  // fallthrough
         case log_level::fatal:
@@ -44,12 +44,12 @@ std::string global_resource_name() {
 }
 
 bool Logger::Filter::operator()(const boost::log::attribute_value_set& attrs) {
-    auto sev = attrs[attr_sev];
+    auto sev = attrs[attr_sev_type{}];
     if (!sev) {
         return false;
     }
 
-    auto resource = attrs[attr_channel];
+    auto resource = attrs[attr_channel_type{}];
     if (resource) {
         auto it = parent->resource_levels_.find(*resource);
         if (it != parent->resource_levels_.end()) {
@@ -66,22 +66,43 @@ Logger& Logger::get() {
     return result;
 }
 
+void Logger::set_global_log_level(log_level lvl) {
+    global_level_ = lvl;
+}
+
+void Logger::set_global_log_level(int argc, char** argv) {
+    if (argc >= 3 && strcmp(argv[2], "--log-level=debug") == 0) {
+        set_global_log_level(log_level::debug);
+    }
+}
+
 void Logger::init_logging() {
     sdk_logger_.channel(global_resource_name());
+    boost::log::core::get()->add_global_attribute("TimeStamp",
+                                                  boost::log::attributes::local_clock());
 
     boost::log::formatter fmt =
         boost::log::expressions::stream
         << boost::log::expressions::format_date_time<boost::posix_time::ptime>(
                "TimeStamp", "%Y--%m--%d %H:%M:%S")
-        << ": [" << attr_channel << "] <" << attr_sev << "> [" << attr_file << ":" << attr_line
-        << "]" << boost::log::expressions::smessage;
+        << ": [" << attr_channel_type{} << "] <" << attr_sev_type{} << "> [" << attr_file_type{}
+        << ":" << attr_line_type{} << "] " << boost::log::expressions::smessage;
+
+    auto backend = boost::make_shared<boost::log::sinks::text_ostream_backend>();
+    backend->add_stream(boost::shared_ptr<std::ostream>(&std::cout, boost::null_deleter()));
+    backend->auto_flush(true);
 
-    boost::log::add_console_log(
-        std::cout, boost::log::keywords::filter = Filter{this}, boost::log::keywords::format = fmt);
+    console_sink_ = boost::make_shared<
+        boost::log::sinks::synchronous_sink<boost::log::sinks::text_ostream_backend>>(backend);
 
-    boost::shared_ptr<boost::log::core> core = boost::log::core::get();
+    console_sink_->set_filter(Filter{this});
+    console_sink_->set_formatter(fmt);
+
+    boost::log::core::get()->add_sink(console_sink_);
+}
 
-    core->add_global_attribute("TimeStamp", boost::log::attributes::local_clock());
+void Logger::disable_console_logging() {
+    boost::log::core::get()->remove_sink(console_sink_);
 }
 
 }  // namespace sdk
diff --git a/src/viam/sdk/log/logger.hpp b/src/viam/sdk/log/logger.hpp
index 0379eebfc..b860631f8 100644
--- a/src/viam/sdk/log/logger.hpp
+++ b/src/viam/sdk/log/logger.hpp
@@ -6,7 +6,12 @@
 #include <memory>
 #include <ostream>
 
+#include <boost/log/attributes/clock.hpp>
+#include <boost/log/expressions/keyword.hpp>
+#include <boost/log/sinks/sync_frontend.hpp>
+#include <boost/log/sinks/text_ostream_backend.hpp>
 #include <boost/log/sources/severity_channel_logger.hpp>
+#include <boost/log/utility/manipulators/add_value.hpp>
 
 namespace viam {
 namespace sdk {
@@ -15,7 +20,7 @@ enum class log_level : std::int8_t {
     trace = -2,
     debug = -1,
     info = 0,  // default value is info
-    warning = 1,
+    warn = 1,
     error = 2,
     fatal = 3,
 };
@@ -36,23 +41,58 @@ class Logger {
         bool operator()(const boost::log::attribute_value_set&);
     };
 
+    static Logger& get();
+
     void set_global_log_level(log_level);
+
+    /// @brief Set the SDK logger severity from a command line argument vector.
+    ///
+    /// Sets the boost trivial logger's severity to debug if "--log-level=debug" is the
+    /// the third argument. Sets severity to info otherwise. Useful for module
+    /// implementations. See LogLevel documentation in the RDK for more information on
+    /// how to start modules with a "log-level" commandline argument.
+    void set_global_log_level(int argc, char** argv);
+
     void set_resource_log_level(std::string resource, log_level);
 
-    static Logger& get();
+    LogSource& logger() {
+        return sdk_logger_;
+    }
 
    private:
+    friend class RobotClient;
     friend class Instance;
     Logger() = default;
 
     void init_logging();
+    void disable_console_logging();
 
     LogSource sdk_logger_;
+    boost::shared_ptr<boost::log::sinks::synchronous_sink<boost::log::sinks::text_ostream_backend>>
+        console_sink_;
 
     log_level global_level_{log_level::info};
 
     std::map<std::string, log_level> resource_levels_;
 };
 
+BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_channel, "Channel", std::string);
+BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_sev, "Severity", viam::sdk::log_level);
+BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_file, "file", const char*);
+BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_line, "line", unsigned int);
+BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_time,
+                                 "TimeStamp",
+                                 boost::log::attributes::local_clock::value_type);
+
+// Logging macro for general SDK logs
+#define VIAM_LOG_IMPL(lg, level)                                            \
+    BOOST_LOG_SEV((lg), ::viam::sdk::log_level::level)                      \
+        << ::boost::log::add_value(::viam::sdk::attr_file_type{}, __FILE__) \
+        << ::boost::log::add_value(::viam::sdk::attr_line_type{}, __LINE__)
+
+#define VIAM_LOG(level) VIAM_LOG_IMPL(::viam::sdk::Logger::get().logger(), level)
+
+#define VIAM_RESOURCE_LOG(level) VIAM_LOG_IMPL(this->logger_, level)
+
 }  // namespace sdk
 }  // namespace viam
diff --git a/src/viam/sdk/log/private/keywords.hpp b/src/viam/sdk/log/private/keywords.hpp
deleted file mode 100644
index 4a5c53d87..000000000
--- a/src/viam/sdk/log/private/keywords.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include <boost/log/attributes/clock.hpp>
-#include <boost/log/expressions/keyword.hpp>
-
-namespace viam {
-namespace sdk {
-
-BOOST_LOG_ATTRIBUTE_KEYWORD(attr_channel, "Channel", std::string);
-BOOST_LOG_ATTRIBUTE_KEYWORD(attr_sev, "Severity", viam::sdk::log_level);
-BOOST_LOG_ATTRIBUTE_KEYWORD(attr_file, "file", const char*);
-BOOST_LOG_ATTRIBUTE_KEYWORD(attr_line, "line", unsigned int);
-BOOST_LOG_ATTRIBUTE_KEYWORD(attr_time,
-                            "TimeStamp",
-                            boost::log::attributes::local_clock::value_type);
-
-}  // namespace sdk
-}  // namespace viam
diff --git a/src/viam/sdk/log/private/log_backend.cpp b/src/viam/sdk/log/private/log_backend.cpp
index ca104cec7..1b0ab08e0 100644
--- a/src/viam/sdk/log/private/log_backend.cpp
+++ b/src/viam/sdk/log/private/log_backend.cpp
@@ -1,5 +1,6 @@
 #include <viam/sdk/log/private/log_backend.hpp>
 
+#include <boost/date_time/posix_time/conversion.hpp>
 #include <boost/smart_ptr/make_shared.hpp>
 
 #include <viam/sdk/log/private/keywords.hpp>
@@ -8,11 +9,20 @@ namespace viam {
 namespace sdk {
 namespace impl {
 
-void LogBackend::consume(const boost::log::record_view& rec) {
-    // auto time = *rec[attr_time];
+time_pt ptime_convert(const boost::posix_time::ptime& from) {
+    boost::posix_time::time_duration const time_since_epoch =
+        from - boost::posix_time::from_time_t(0);
+    time_pt t = std::chrono::system_clock::from_time_t(time_since_epoch.total_seconds());
+    long nsec =
+        time_since_epoch.fractional_seconds() * (1000000000 / time_since_epoch.ticks_per_second());
+    return t + std::chrono::nanoseconds(nsec);
+}
 
-    parent->log(
-        *rec[attr_channel], to_string(*rec[attr_sev]), *rec[boost::log::expressions::smessage], {});
+void LogBackend::consume(const boost::log::record_view& rec) {
+    parent->log(*rec[attr_channel_type{}],
+                to_string(*rec[attr_sev_type{}]),
+                *rec[boost::log::expressions::smessage],
+                ptime_convert(*rec[attr_time_type{}]));
 }
 
 boost::shared_ptr<SinkType> LogBackend::create(RobotClient* p) {

From 5eb243b72ca5d1eb07cb90d808ba286f53552219 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 7 Mar 2025 13:45:07 -0500
Subject: [PATCH 29/86] init grpc logging in module service impl ready, and
 disable console logging

---
 src/viam/sdk/module/service.cpp | 19 +++++++++------
 src/viam/sdk/module/service.hpp |  4 ++++
 src/viam/sdk/robot/client.cpp   | 41 ++++++++++++++++-----------------
 src/viam/sdk/robot/client.hpp   |  2 +-
 4 files changed, 37 insertions(+), 29 deletions(-)

diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp
index 0d9c08aab..5acabe126 100644
--- a/src/viam/sdk/module/service.cpp
+++ b/src/viam/sdk/module/service.cpp
@@ -182,7 +182,12 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         const std::lock_guard<std::mutex> lock(parent.lock_);
         const viam::module::v1::HandlerMap hm = to_proto(parent.module_->handles());
         *response->mutable_handlermap() = hm;
-        parent.parent_addr_ = request->parent_address();
+        auto new_parent_addr = request->parent_address();
+        if (parent.parent_addr_ != new_parent_addr) {
+            parent.parent_addr_ = std::move(new_parent_addr);
+            parent.parent_ = RobotClient::at_local_socket(parent.parent_addr_, {0, boost::none});
+            parent.parent_->connect_logging();
+        }
         response->set_ready(parent.module_->ready());
         return grpc::Status();
     }
@@ -208,6 +213,7 @@ Dependencies ModuleService::get_dependencies_(
 
 std::shared_ptr<Resource> ModuleService::get_parent_resource_(const Name& name) {
     if (!parent_) {
+        // LS: I think maybe this is never hit
         parent_ = RobotClient::at_local_socket(parent_addr_, {0, boost::none});
         parent_->connect_logging();
     }
@@ -230,7 +236,7 @@ ModuleService::ModuleService(int argc,
           }
           return argv[1];
       }()) {
-    set_logger_severity_from_args(argc, argv);
+    Logger::get().set_global_log_level(argc, argv);
 
     for (auto&& mr : registrations) {
         Registry::get().register_model(mr);
@@ -240,14 +246,14 @@ ModuleService::ModuleService(int argc,
 
 ModuleService::~ModuleService() {
     // TODO(RSDK-5509): Run registered cleanup functions here.
-    BOOST_LOG_TRIVIAL(info) << "Shutting down gracefully.";
+    VIAM_LOG(info) << "Shutting down gracefully.";
     server_->shutdown();
 
     if (parent_) {
         try {
             parent_->close();
         } catch (const std::exception& exc) {
-            BOOST_LOG_TRIVIAL(error) << exc.what();
+            VIAM_LOG(error) << exc.what();
         }
     }
 }
@@ -265,9 +271,8 @@ void ModuleService::serve() {
     module_->set_ready();
     server_->start();
 
-    BOOST_LOG_TRIVIAL(info) << "Module listening on " << module_->addr();
-    BOOST_LOG_TRIVIAL(info) << "Module handles the following API/model pairs:\n"
-                            << module_->handles();
+    VIAM_LOG(info) << "Module listening on " << module_->addr();
+    VIAM_LOG(info) << "Module handles the following API/model pairs:\n" << module_->handles();
 
     signal_manager_.wait();
 }
diff --git a/src/viam/sdk/module/service.hpp b/src/viam/sdk/module/service.hpp
index 799ba1ab5..714d55051 100644
--- a/src/viam/sdk/module/service.hpp
+++ b/src/viam/sdk/module/service.hpp
@@ -65,10 +65,14 @@ class ModuleService {
     std::shared_ptr<Resource> get_parent_resource_(const Name& name);
 
     std::mutex lock_;
+
     std::unique_ptr<Module> module_;
+
     std::shared_ptr<RobotClient> parent_;
     std::string parent_addr_;
+
     std::unique_ptr<Server> server_;
+
     SignalManager signal_manager_;
 
     std::unique_ptr<ServiceImpl> impl_;
diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp
index 2ec71752f..3e21adfa4 100644
--- a/src/viam/sdk/robot/client.cpp
+++ b/src/viam/sdk/robot/client.cpp
@@ -104,6 +104,7 @@ void RobotClient::connect_logging() {
         sink = sdk::impl::LogBackend::create(this);
         sink->set_filter(Logger::Filter{&Logger::get()});
 
+        Logger::get().disable_console_logging();
         boost::log::core::get()->add_sink(sink);
     }
 }
@@ -113,9 +114,9 @@ RobotClient::~RobotClient() {
         try {
             this->close();
         } catch (const std::exception& e) {
-            BOOST_LOG_TRIVIAL(error) << "Received err while closing RobotClient: " << e.what();
+            VIAM_LOG(error) << "Received err while closing RobotClient: " << e.what();
         } catch (...) {
-            BOOST_LOG_TRIVIAL(error) << "Received unknown err while closing RobotClient";
+            VIAM_LOG(error) << "Received unknown err while closing RobotClient";
         }
     }
 }
@@ -142,7 +143,7 @@ std::vector<RobotClient::operation> RobotClient::get_operations() {
 
     grpc::Status const response = impl_->stub_->GetOperations(ctx, req, &resp);
     if (is_error_response(response)) {
-        BOOST_LOG_TRIVIAL(error) << "Error getting operations: " << response.error_message();
+        VIAM_LOG(error) << "Error getting operations: " << response.error_message();
     }
 
     for (int i = 0; i < resp.operations().size(); ++i) {
@@ -160,7 +161,7 @@ void RobotClient::cancel_operation(std::string id) {
     req.set_id(id);
     const grpc::Status response = impl_->stub_->CancelOperation(ctx, req, &resp);
     if (is_error_response(response)) {
-        BOOST_LOG_TRIVIAL(error) << "Error canceling operation with id " << id;
+        VIAM_LOG(error) << "Error canceling operation with id " << id;
     }
 }
 
@@ -173,7 +174,7 @@ void RobotClient::block_for_operation(std::string id) {
 
     const grpc::Status response = impl_->stub_->BlockForOperation(ctx, req, &resp);
     if (is_error_response(response)) {
-        BOOST_LOG_TRIVIAL(error) << "Error blocking for operation with id " << id;
+        VIAM_LOG(error) << "Error blocking for operation with id " << id;
     }
 }
 
@@ -184,7 +185,7 @@ void RobotClient::refresh() {
 
     const grpc::Status response = impl_->stub_->ResourceNames(ctx, req, &resp);
     if (is_error_response(response)) {
-        BOOST_LOG_TRIVIAL(error) << "Error getting resource names: " << response.error_message();
+        VIAM_LOG(error) << "Error getting resource names: " << response.error_message();
     }
 
     std::unordered_map<Name, std::shared_ptr<Resource>> new_resources;
@@ -208,8 +209,8 @@ void RobotClient::refresh() {
                 const Name name_({name.namespace_(), name.type(), name.subtype()}, "", name.name());
                 new_resources.emplace(name_, rpc_client);
             } catch (const std::exception& exc) {
-                BOOST_LOG_TRIVIAL(debug)
-                    << "Error registering component " << name.subtype() << ": " << exc.what();
+                VIAM_LOG(debug) << "Error registering component " << name.subtype() << ": "
+                                << exc.what();
             }
         }
     }
@@ -254,18 +255,17 @@ std::vector<Name> RobotClient::resource_names() const {
     return resource_names_;
 }
 
-void RobotClient::log(
-    const std::string& name,
-    const std::string& level,
-    const std::string& message,
-    std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> time) {
+void RobotClient::log(const std::string& name,
+                      const std::string& level,
+                      const std::string& message,
+                      time_pt time) {
     robot::v1::LogRequest req;
     common::v1::LogEntry log;
 
     *log.mutable_logger_name() = name;
     log.set_level(level);
     *log.mutable_message() = message;
-    (void)time;
+    *log.mutable_time() = to_proto(time);
     req.mutable_logs()->Add(std::move(log));
 
     robot::v1::LogResponse resp;
@@ -326,8 +326,7 @@ std::vector<RobotClient::frame_system_config> RobotClient::get_frame_system_conf
 
     const grpc::Status response = impl_->stub_->FrameSystemConfig(ctx, req, &resp);
     if (is_error_response(response)) {
-        BOOST_LOG_TRIVIAL(error) << "Error getting frame system config: "
-                                 << response.error_message();
+        VIAM_LOG(error) << "Error getting frame system config: " << response.error_message();
     }
 
     const RepeatedPtrField<FrameSystemConfig> configs = resp.frame_system_configs();
@@ -355,7 +354,7 @@ pose_in_frame RobotClient::transform_pose(
 
     const grpc::Status response = impl_->stub_->TransformPose(ctx, req, &resp);
     if (is_error_response(response)) {
-        BOOST_LOG_TRIVIAL(error) << "Error getting PoseInFrame: " << response.error_message();
+        VIAM_LOG(error) << "Error getting PoseInFrame: " << response.error_message();
     }
 
     return from_proto(resp.pose());
@@ -390,8 +389,8 @@ void RobotClient::stop_all(const std::unordered_map<Name, ProtoStruct>& extra) {
     }
     const grpc::Status response = impl_->stub_->StopAll(ctx, req, &resp);
     if (is_error_response(response)) {
-        BOOST_LOG_TRIVIAL(error) << "Error stopping all: " << response.error_message()
-                                 << response.error_details();
+        VIAM_LOG(error) << "Error stopping all: " << response.error_message()
+                        << response.error_details();
     }
 }
 
@@ -419,8 +418,8 @@ RobotClient::status RobotClient::get_machine_status() const {
 
     const grpc::Status response = impl_->stub_->GetMachineStatus(ctx, req, &resp);
     if (is_error_response(response)) {
-        BOOST_LOG_TRIVIAL(error) << "Error getting machine status: " << response.error_message()
-                                 << response.error_details();
+        VIAM_LOG(error) << "Error getting machine status: " << response.error_message()
+                        << response.error_details();
     }
     switch (resp.state()) {
         case robot::v1::GetMachineStatusResponse_State_STATE_INITIALIZING:
diff --git a/src/viam/sdk/robot/client.hpp b/src/viam/sdk/robot/client.hpp
index a818dcca5..e60eb0edc 100644
--- a/src/viam/sdk/robot/client.hpp
+++ b/src/viam/sdk/robot/client.hpp
@@ -91,7 +91,7 @@ class RobotClient {
     void log(const std::string& name,
              const std::string& level,
              const std::string& message,
-             std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> time);
+             time_pt time);
 
     void connect_logging();
 

From 6e311bc253d709fee7895e6fe2532a29df546a4f Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 7 Mar 2025 13:45:26 -0500
Subject: [PATCH 30/86] remove set severity from args

---
 src/viam/sdk/common/utils.cpp |  9 ---------
 src/viam/sdk/common/utils.hpp | 10 ----------
 2 files changed, 19 deletions(-)

diff --git a/src/viam/sdk/common/utils.cpp b/src/viam/sdk/common/utils.cpp
index 5825c2e7f..5b1134fec 100644
--- a/src/viam/sdk/common/utils.cpp
+++ b/src/viam/sdk/common/utils.cpp
@@ -96,15 +96,6 @@ std::string bytes_to_string(const std::vector<unsigned char>& b) {
     return img_string;
 }
 
-void set_logger_severity_from_args(int argc, char** argv) {
-    if (argc >= 3 && strcmp(argv[2], "--log-level=debug") == 0) {
-        boost::log::core::get()->set_filter(boost::log::trivial::severity >=
-                                            boost::log::trivial::debug);
-        return;
-    }
-    boost::log::core::get()->set_filter(boost::log::trivial::severity >= boost::log::trivial::info);
-}
-
 std::string random_debug_key() {
     static const char alphanum[] = "abcdefghijklmnopqrstuvwxyz";
     static std::default_random_engine generator(
diff --git a/src/viam/sdk/common/utils.hpp b/src/viam/sdk/common/utils.hpp
index f5067de55..51e360042 100644
--- a/src/viam/sdk/common/utils.hpp
+++ b/src/viam/sdk/common/utils.hpp
@@ -110,16 +110,6 @@ ProtoStruct with_debug_entry(ProtoStruct&& map, std::string debug_key);
 /// @returns the new ProtoStruct
 ProtoStruct with_debug_entry(ProtoStruct&& map);
 
-/// @brief Set the boost trivial logger's severity depending on args.
-/// @param argc The number of args.
-/// @param argv The commandline arguments to parse.
-///
-/// Sets the boost trivial logger's severity to debug if "--log-level=debug" is the
-/// the third argument. Sets severity to info otherwise. Useful for module
-/// implementations. See LogLevel documentation in the RDK for more information on
-/// how to start modules with a "log-level" commandline argument.
-void set_logger_severity_from_args(int argc, char** argv);
-
 /// @brief Used in modular filter components to get the 'fromDataManagement' value from an extra
 /// ProtoStruct.
 /// @param extra The extra ProtoStruct.

From f84fe6e4ea6c3dcb79753e13121465afa3cbcc88 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 7 Mar 2025 13:45:58 -0500
Subject: [PATCH 31/86] update boost_log_trivial invocations

---
 src/viam/sdk/common/client_helper.cpp      | 7 +++----
 src/viam/sdk/module/handler_map.cpp        | 2 +-
 src/viam/sdk/resource/resource_manager.cpp | 9 ++++-----
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/src/viam/sdk/common/client_helper.cpp b/src/viam/sdk/common/client_helper.cpp
index b05bb37fe..24eeb7db8 100644
--- a/src/viam/sdk/common/client_helper.cpp
+++ b/src/viam/sdk/common/client_helper.cpp
@@ -5,9 +5,8 @@
 #include <grpcpp/client_context.h>
 #include <grpcpp/support/status.h>
 
-#include <boost/log/trivial.hpp>
-
 #include <viam/sdk/common/private/version_metadata.hpp>
+#include <viam/sdk/log/logger.hpp>
 
 namespace viam {
 namespace sdk {
@@ -15,8 +14,8 @@ namespace sdk {
 namespace client_helper_details {
 
 [[noreturn]] void errorHandlerReturnedUnexpectedly(const ::grpc::Status* status) noexcept {
-    BOOST_LOG_TRIVIAL(fatal) << "ClientHelper error handler callback returned instead of throwing: "
-                             << status->error_message() << '(' << status->error_details() << ')';
+    VIAM_LOG(fatal) << "ClientHelper error handler callback returned instead of throwing: "
+                    << status->error_message() << '(' << status->error_details() << ')';
     std::abort();
 }
 
diff --git a/src/viam/sdk/module/handler_map.cpp b/src/viam/sdk/module/handler_map.cpp
index 4d2ba34bf..b538ad734 100644
--- a/src/viam/sdk/module/handler_map.cpp
+++ b/src/viam/sdk/module/handler_map.cpp
@@ -66,7 +66,7 @@ HandlerMap_ from_proto_impl<module::v1::HandlerMap>::operator()(
             try {
                 hm.add_model(Model::from_str(mod), handle);
             } catch (const std::exception& ex) {
-                BOOST_LOG_TRIVIAL(error) << "Error " << ex.what() << " processing model " + mod;
+                VIAM_LOG(error) << "Error " << ex.what() << " processing model " + mod;
             }
         }
     }
diff --git a/src/viam/sdk/resource/resource_manager.cpp b/src/viam/sdk/resource/resource_manager.cpp
index 8f2940c5f..d67e93972 100644
--- a/src/viam/sdk/resource/resource_manager.cpp
+++ b/src/viam/sdk/resource/resource_manager.cpp
@@ -52,7 +52,7 @@ void ResourceManager::replace_all(
         try {
             do_add(resource.first, resource.second);
         } catch (std::exception& exc) {
-            BOOST_LOG_TRIVIAL(error) << "Error replacing all resources" << exc.what();
+            VIAM_LOG(error) << "Error replacing all resources" << exc.what();
             return;
         }
     }
@@ -97,7 +97,7 @@ void ResourceManager::add(const Name& name, std::shared_ptr<Resource> resource)
     try {
         do_add(name, std::move(resource));
     } catch (std::exception& exc) {
-        BOOST_LOG_TRIVIAL(error) << "Error adding resource to subtype service: " << exc.what();
+        VIAM_LOG(error) << "Error adding resource to subtype service: " << exc.what();
     }
 };
 
@@ -134,7 +134,7 @@ void ResourceManager::remove(const Name& name) {
     try {
         do_remove(name);
     } catch (std::exception& exc) {
-        BOOST_LOG_TRIVIAL(error) << "unable to remove resource: " << exc.what();
+        VIAM_LOG(error) << "unable to remove resource: " << exc.what();
     };
 };
 
@@ -144,8 +144,7 @@ void ResourceManager::replace_one(const Name& name, std::shared_ptr<Resource> re
         do_remove(name);
         do_add(name, std::move(resource));
     } catch (std::exception& exc) {
-        BOOST_LOG_TRIVIAL(error) << "failed to replace resource " << name.to_string() << ": "
-                                 << exc.what();
+        VIAM_LOG(error) << "failed to replace resource " << name.to_string() << ": " << exc.what();
     }
 }
 

From 93119904855a54a4e84458ffae222e5fcaa5353d Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 7 Mar 2025 13:53:06 -0500
Subject: [PATCH 32/86] remove now-unused boost includes and uses of boost log
 trivial

---
 src/viam/sdk/common/utils.cpp              | 3 ---
 src/viam/sdk/log/private/log_backend.cpp   | 2 --
 src/viam/sdk/module/handler_map.cpp        | 2 --
 src/viam/sdk/module/module.cpp             | 3 +--
 src/viam/sdk/module/service.cpp            | 5 ++---
 src/viam/sdk/registry/registry.cpp         | 1 -
 src/viam/sdk/resource/resource.hpp         | 2 --
 src/viam/sdk/resource/resource_manager.cpp | 2 --
 src/viam/sdk/robot/client.cpp              | 1 -
 src/viam/sdk/rpc/server.cpp                | 2 --
 10 files changed, 3 insertions(+), 20 deletions(-)

diff --git a/src/viam/sdk/common/utils.cpp b/src/viam/sdk/common/utils.cpp
index 5b1134fec..42039d18f 100644
--- a/src/viam/sdk/common/utils.cpp
+++ b/src/viam/sdk/common/utils.cpp
@@ -10,9 +10,6 @@
 
 #include <boost/algorithm/string.hpp>
 #include <boost/blank.hpp>
-#include <boost/log/core.hpp>
-#include <boost/log/expressions.hpp>
-#include <boost/log/trivial.hpp>
 #include <boost/optional/optional.hpp>
 
 #include <viam/api/common/v1/common.pb.h>
diff --git a/src/viam/sdk/log/private/log_backend.cpp b/src/viam/sdk/log/private/log_backend.cpp
index 1b0ab08e0..9f0a34a7f 100644
--- a/src/viam/sdk/log/private/log_backend.cpp
+++ b/src/viam/sdk/log/private/log_backend.cpp
@@ -3,8 +3,6 @@
 #include <boost/date_time/posix_time/conversion.hpp>
 #include <boost/smart_ptr/make_shared.hpp>
 
-#include <viam/sdk/log/private/keywords.hpp>
-
 namespace viam {
 namespace sdk {
 namespace impl {
diff --git a/src/viam/sdk/module/handler_map.cpp b/src/viam/sdk/module/handler_map.cpp
index b538ad734..14f5d656d 100644
--- a/src/viam/sdk/module/handler_map.cpp
+++ b/src/viam/sdk/module/handler_map.cpp
@@ -2,8 +2,6 @@
 
 #include <memory>
 
-#include <boost/log/trivial.hpp>
-
 #include <viam/api/common/v1/common.pb.h>
 #include <viam/api/module/v1/module.pb.h>
 #include <viam/api/robot/v1/robot.pb.h>
diff --git a/src/viam/sdk/module/module.cpp b/src/viam/sdk/module/module.cpp
index 3c09df767..6ccc4b62c 100644
--- a/src/viam/sdk/module/module.cpp
+++ b/src/viam/sdk/module/module.cpp
@@ -1,6 +1,5 @@
 #include <viam/sdk/module/module.hpp>
 
-#include <boost/log/trivial.hpp>
 #include <grpcpp/channel.h>
 #include <grpcpp/create_channel.h>
 #include <grpcpp/security/credentials.h>
@@ -10,7 +9,7 @@
 namespace viam {
 namespace sdk {
 
-Module::Module(std::string addr) : addr_(std::move(addr)){};
+Module::Module(std::string addr) : addr_(std::move(addr)) {};
 
 void Module::set_ready() {
     ready_ = true;
diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp
index 5acabe126..fa9d55674 100644
--- a/src/viam/sdk/module/service.cpp
+++ b/src/viam/sdk/module/service.cpp
@@ -7,7 +7,6 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 
-#include <boost/log/trivial.hpp>
 #include <boost/none.hpp>
 #include <google/protobuf/descriptor.h>
 #include <grpcpp/channel.h>
@@ -108,7 +107,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         try {
             Stoppable::stop_if_stoppable(res);
         } catch (const std::exception& err) {
-            BOOST_LOG_TRIVIAL(error) << "unable to stop resource: " << err.what();
+            VIAM_LOG(error) << "unable to stop resource: " << err.what();
         }
 
         const std::shared_ptr<const ModelRegistration> reg =
@@ -169,7 +168,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         try {
             Stoppable::stop_if_stoppable(res);
         } catch (const std::exception& err) {
-            BOOST_LOG_TRIVIAL(error) << "unable to stop resource: " << err.what();
+            VIAM_LOG(error) << "unable to stop resource: " << err.what();
         }
 
         manager->remove(name);
diff --git a/src/viam/sdk/registry/registry.cpp b/src/viam/sdk/registry/registry.cpp
index e538d2ec2..e0fff782b 100644
--- a/src/viam/sdk/registry/registry.cpp
+++ b/src/viam/sdk/registry/registry.cpp
@@ -5,7 +5,6 @@
 #include <string>
 #include <unordered_map>
 
-#include <boost/log/trivial.hpp>
 #include <google/protobuf/descriptor.h>
 #include <google/protobuf/struct.pb.h>
 #include <grpcpp/channel.h>
diff --git a/src/viam/sdk/resource/resource.hpp b/src/viam/sdk/resource/resource.hpp
index 918a988f0..6d05b7cec 100644
--- a/src/viam/sdk/resource/resource.hpp
+++ b/src/viam/sdk/resource/resource.hpp
@@ -2,8 +2,6 @@
 
 #include <unordered_map>
 
-#include <boost/log/trivial.hpp>
-
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/config/resource.hpp>
 #include <viam/sdk/log/logger.hpp>
diff --git a/src/viam/sdk/resource/resource_manager.cpp b/src/viam/sdk/resource/resource_manager.cpp
index d67e93972..c68fc0aac 100644
--- a/src/viam/sdk/resource/resource_manager.cpp
+++ b/src/viam/sdk/resource/resource_manager.cpp
@@ -7,8 +7,6 @@
 #include <vector>
 
 #include <boost/algorithm/string.hpp>
-#include <boost/log/trivial.hpp>
-#include <boost/optional/optional.hpp>
 #include <grpcpp/impl/service_type.h>
 #include <grpcpp/support/status.h>
 
diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp
index 3e21adfa4..de1890dbc 100644
--- a/src/viam/sdk/robot/client.cpp
+++ b/src/viam/sdk/robot/client.cpp
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include <boost/log/core/core.hpp>
-#include <boost/log/trivial.hpp>
 #include <grpcpp/channel.h>
 #include <grpcpp/client_context.h>
 #include <grpcpp/grpcpp.h>
diff --git a/src/viam/sdk/rpc/server.cpp b/src/viam/sdk/rpc/server.cpp
index 9964abbac..c16394b26 100644
--- a/src/viam/sdk/rpc/server.cpp
+++ b/src/viam/sdk/rpc/server.cpp
@@ -2,8 +2,6 @@
 
 #include <sstream>
 
-#include <boost/log/trivial.hpp>
-
 #include <grpcpp/impl/service_type.h>
 #include <grpcpp/security/server_credentials.h>
 #include <grpcpp/server_builder.h>

From a09901f2735a0375ac5c6cb140b097add104123b Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 7 Mar 2025 13:53:28 -0500
Subject: [PATCH 33/86] remove unused log trivial include

---
 src/viam/examples/modules/complex/main.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/viam/examples/modules/complex/main.cpp b/src/viam/examples/modules/complex/main.cpp
index 253e69d78..cb8fab385 100644
--- a/src/viam/examples/modules/complex/main.cpp
+++ b/src/viam/examples/modules/complex/main.cpp
@@ -1,7 +1,6 @@
 #include <memory>
 #include <signal.h>
 
-#include <boost/log/trivial.hpp>
 #include <grpcpp/grpcpp.h>
 #include <grpcpp/server_context.h>
 

From f9b1cb3e454c3a626527aeb25382eac3628fb94d Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 7 Mar 2025 15:46:47 -0500
Subject: [PATCH 34/86] readability member arrangement/spacing

---
 src/viam/sdk/config/resource.hpp | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/viam/sdk/config/resource.hpp b/src/viam/sdk/config/resource.hpp
index 73b5c8450..15bb2a5a2 100644
--- a/src/viam/sdk/config/resource.hpp
+++ b/src/viam/sdk/config/resource.hpp
@@ -61,18 +61,26 @@ class ResourceConfig {
     const ProtoStruct& attributes() const;
 
    private:
+    void fix_api();
+
     API api_;
+
     LinkConfig frame_;
+
     Model model_;
+
     std::string name_;
     std::string namespace__;
     std::string type_;
+
     std::vector<std::string> depends_on_;
+
     std::vector<ResourceLevelServiceConfig> service_config_;
+
     ProtoStruct attributes_;
     ProtoValue converted_attributes_;
+
     std::vector<std::string> implicit_depends_on_;
-    void fix_api();
 };
 
 namespace proto_convert_details {

From 68add9dc372f99f7b61f715ebe50361712fc0714 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 7 Mar 2025 15:50:44 -0500
Subject: [PATCH 35/86] readability spacing

---
 src/viam/sdk/config/resource.hpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/viam/sdk/config/resource.hpp b/src/viam/sdk/config/resource.hpp
index 15bb2a5a2..f129a25be 100644
--- a/src/viam/sdk/config/resource.hpp
+++ b/src/viam/sdk/config/resource.hpp
@@ -50,14 +50,21 @@ class ResourceConfig {
     ResourceConfig(std::string type);
 
     Name resource_name();
+
     const API& api() const;
+
     const LinkConfig& frame() const;
+
     const Model& model() const;
+
     const std::string& name() const;
     const std::string& namespace_() const;
     const std::string& type() const;
+
     const std::vector<std::string>& depends_on() const;
+
     const std::vector<ResourceLevelServiceConfig>& service_config() const;
+
     const ProtoStruct& attributes() const;
 
    private:

From 05eddd0c09b902a971e20d8e190f77f7321805d2 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 7 Mar 2025 16:33:40 -0500
Subject: [PATCH 36/86] add from string

---
 src/viam/sdk/log/logger.cpp | 24 ++++++++++++++++++++++++
 src/viam/sdk/log/logger.hpp |  2 ++
 2 files changed, 26 insertions(+)

diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index d3046e82a..95800f9d5 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -26,6 +26,7 @@ std::string to_string(log_level lvl) {
             return "info";
         case log_level::warn:
             return "warning";
+        // RDK does not support fatal so we send error regardless
         case log_level::error:  // fallthrough
         case log_level::fatal:
             return "error";
@@ -34,6 +35,29 @@ std::string to_string(log_level lvl) {
     }
 }
 
+log_level level_from_string(std::string str) {
+    using ll = log_level;
+
+    std::transform(str.begin(), str.end(), str.begin(), ::tolower);
+
+    if (str == "info") {
+        return ll::info;
+    } else if (str == "warn" || str == "warning") {
+        return ll::warn;
+    } else if (str == "error") {
+        return ll::error;
+    } else if (str == "debug") {
+        return ll::debug;
+    } else if (str == "trace") {
+        return ll::trace;
+    } else if (str == "fatal") {
+        return ll::fatal;
+    }
+
+    VIAM_LOG(warn) << "Returning unknown log level " << str << " as info";
+    return ll::info;
+}
+
 std::ostream& operator<<(std::ostream& os, log_level lvl) {
     os << to_string(lvl);
     return os;
diff --git a/src/viam/sdk/log/logger.hpp b/src/viam/sdk/log/logger.hpp
index b860631f8..82fe12334 100644
--- a/src/viam/sdk/log/logger.hpp
+++ b/src/viam/sdk/log/logger.hpp
@@ -27,6 +27,8 @@ enum class log_level : std::int8_t {
 
 std::string to_string(log_level);
 
+log_level level_from_string(std::string level);
+
 std::ostream& operator<<(std::ostream&, log_level);
 
 using LogSource = boost::log::sources::severity_channel_logger_mt<log_level>;

From 5debae11719fbd0e31a82fe8b1f95c03fc270ca4 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 7 Mar 2025 16:33:51 -0500
Subject: [PATCH 37/86] add log level

---
 src/viam/sdk/config/resource.cpp | 14 +++++++++++---
 src/viam/sdk/config/resource.hpp |  8 +++++++-
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/src/viam/sdk/config/resource.cpp b/src/viam/sdk/config/resource.cpp
index 00d0296f7..3d3251d49 100644
--- a/src/viam/sdk/config/resource.cpp
+++ b/src/viam/sdk/config/resource.cpp
@@ -26,14 +26,16 @@ ResourceConfig::ResourceConfig(std::string type,
                                ProtoStruct attributes,
                                std::string api,
                                Model model,
-                               LinkConfig frame)
+                               LinkConfig frame,
+                               sdk::log_level lvl)
     : api_({kRDK, type, ""}),
       frame_(std::move(frame)),
       model_(std::move(model)),
       name_(std::move(name)),
       namespace__(std::move(namespace_)),
       type_(std::move(type)),
-      attributes_(std::move(attributes)) {
+      attributes_(std::move(attributes)),
+      log_level_(lvl) {
     if (api.find(':') != std::string::npos) {
         api_ = API::from_string(std::move(api));
     }
@@ -81,6 +83,10 @@ const std::string& ResourceConfig::type() const {
     return type_;
 }
 
+log_level ResourceConfig::log_level() const {
+    return log_level_;
+}
+
 const std::vector<std::string>& viam::sdk::ResourceConfig::depends_on() const {
     return depends_on_;
 }
@@ -137,6 +143,7 @@ void to_proto_impl<ResourceConfig>::operator()(const ResourceConfig& self,
     *proto->mutable_namespace_() = self.namespace_();
     *proto->mutable_type() = self.type();
     *proto->mutable_api() = self.api().to_string();
+    *proto->mutable_log_configuration()->mutable_level() = to_string(self.log_level());
     *proto->mutable_model() = self.model().to_string();
     *proto->mutable_attributes() = to_proto(self.attributes());
 
@@ -154,7 +161,8 @@ ResourceConfig from_proto_impl<app::v1::ComponentConfig>::operator()(
                           from_proto(proto->attributes()),
                           proto->api(),
                           Model::from_str(proto->model()),
-                          proto->has_frame() ? from_proto(proto->frame()) : LinkConfig{});
+                          proto->has_frame() ? from_proto(proto->frame()) : LinkConfig{},
+                          level_from_string(proto->log_configuration().level()));
 }
 
 std::vector<ResourceConfig>
diff --git a/src/viam/sdk/config/resource.hpp b/src/viam/sdk/config/resource.hpp
index f129a25be..ee55d4112 100644
--- a/src/viam/sdk/config/resource.hpp
+++ b/src/viam/sdk/config/resource.hpp
@@ -5,6 +5,7 @@
 
 #include <viam/sdk/common/proto_convert.hpp>
 #include <viam/sdk/common/proto_value.hpp>
+#include <viam/sdk/log/logger.hpp>
 #include <viam/sdk/referenceframe/frame.hpp>
 #include <viam/sdk/resource/resource_api.hpp>
 
@@ -45,7 +46,8 @@ class ResourceConfig {
                    ProtoStruct attributes,
                    std::string api,
                    Model model,
-                   LinkConfig frame);
+                   LinkConfig frame,
+                   sdk::log_level lvl = sdk::log_level::info);
 
     ResourceConfig(std::string type);
 
@@ -61,6 +63,8 @@ class ResourceConfig {
     const std::string& namespace_() const;
     const std::string& type() const;
 
+    log_level log_level() const;
+
     const std::vector<std::string>& depends_on() const;
 
     const std::vector<ResourceLevelServiceConfig>& service_config() const;
@@ -88,6 +92,8 @@ class ResourceConfig {
     ProtoValue converted_attributes_;
 
     std::vector<std::string> implicit_depends_on_;
+
+    sdk::log_level log_level_;
 };
 
 namespace proto_convert_details {

From 68665679f7f6c9b94027b9ccf2a67078ab79dba2 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 10 Mar 2025 12:08:20 -0400
Subject: [PATCH 38/86] add basic logging tests

---
 src/viam/sdk/tests/CMakeLists.txt |  1 +
 src/viam/sdk/tests/test_log.cpp   | 93 +++++++++++++++++++++++++++++++
 2 files changed, 94 insertions(+)
 create mode 100644 src/viam/sdk/tests/test_log.cpp

diff --git a/src/viam/sdk/tests/CMakeLists.txt b/src/viam/sdk/tests/CMakeLists.txt
index de1fbf0b7..20103f761 100644
--- a/src/viam/sdk/tests/CMakeLists.txt
+++ b/src/viam/sdk/tests/CMakeLists.txt
@@ -61,6 +61,7 @@ viamcppsdk_add_boost_test(test_discovery.cpp)
 viamcppsdk_add_boost_test(test_gantry.cpp)
 viamcppsdk_add_boost_test(test_gripper.cpp)
 viamcppsdk_add_boost_test(test_generics.cpp)
+viamcppsdk_add_boost_test(test_log.cpp)
 viamcppsdk_add_boost_test(test_mlmodel.cpp)
 viamcppsdk_add_boost_test(test_motor.cpp)
 viamcppsdk_add_boost_test(test_motion.cpp)
diff --git a/src/viam/sdk/tests/test_log.cpp b/src/viam/sdk/tests/test_log.cpp
new file mode 100644
index 000000000..78d13746d
--- /dev/null
+++ b/src/viam/sdk/tests/test_log.cpp
@@ -0,0 +1,93 @@
+#define BOOST_TEST_MODULE test module test_log
+#include <viam/sdk/log/logger.hpp>
+
+#include <iostream>
+#include <sstream>
+
+#include <boost/test/included/unit_test.hpp>
+
+#include <viam/sdk/resource/resource.hpp>
+
+namespace viam {
+namespace sdktests {
+
+// Buffer output filter to test console logging.
+// In practice this is a pain and makes it hard to inspect Boost.Test output,
+// so rather than using it as a test fixture we manually instantiate it in an optional.
+// Further log testing is done in the complex module example tests.
+// https://stackoverflow.com/a/5405268
+struct cout_redirect {
+    cout_redirect() : old(std::cout.rdbuf(os.rdbuf())) {}
+
+    void release() {
+        std::cout.rdbuf(old);
+    }
+
+    ~cout_redirect() {
+        release();
+    }
+
+    std::ostringstream os;
+
+   private:
+    std::streambuf* old;
+};
+
+BOOST_AUTO_TEST_CASE(test_cout_logging) {
+    cout_redirect redirect;
+
+    VIAM_LOG(info) << "log1";
+
+    using namespace std::string_literals;
+
+    const std::string rec = redirect.os.str();
+    redirect.release();
+
+    for (const std::string& s :
+         {"log1"s, to_string(sdk::log_level::info), sdk::global_resource_name()}) {
+        BOOST_CHECK(rec.find(s) != std::string::npos);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(test_global_filter) {
+    cout_redirect redirect;
+
+    VIAM_LOG(info) << "info1";
+    VIAM_LOG(error) << "error1";
+    VIAM_LOG(trace) << "trace1";  // not logged
+
+    auto& logger = sdk::Logger::get();
+
+    using ll = sdk::log_level;
+
+    logger.set_global_log_level(ll::trace);
+
+    VIAM_LOG(trace) << "trace2";
+    VIAM_LOG(info) << "info2";
+
+    logger.set_global_log_level(ll::error);
+
+    VIAM_LOG(info) << "info3";  // not logged
+    VIAM_LOG(error) << "error2";
+
+    logger.set_global_log_level(ll::info);
+
+    VIAM_LOG(info) << "info4";
+    VIAM_LOG(trace) << "trace3";  // once again not logged
+
+    const std::string rec = redirect.os.str();
+    redirect.release();
+
+    for (const char* logged : {"info1", "error1", "trace2", "info2", "error2", "info4"}) {
+        BOOST_TEST_INFO("Checking for " << logged << " in log rec\n" << rec);
+        BOOST_CHECK(rec.find(logged) != std::string::npos);
+    }
+
+    for (const char* not_logged : {"trace1", "info3", "trace3"}) {
+        BOOST_TEST_INFO("Checking for " << not_logged << " not in log rec\n" << rec);
+        BOOST_CHECK(rec.find(not_logged) == std::string::npos);
+    }
+}
+
+}  // namespace sdktests
+}  // namespace viam

From f02ce0f75d81baa28d62e88f9771e46132cab562 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 12 Mar 2025 13:44:21 -0400
Subject: [PATCH 39/86] reorder includes

---
 src/viam/sdk/tests/test_mlmodel.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/viam/sdk/tests/test_mlmodel.cpp b/src/viam/sdk/tests/test_mlmodel.cpp
index 5d2db8010..8bc6a62f0 100644
--- a/src/viam/sdk/tests/test_mlmodel.cpp
+++ b/src/viam/sdk/tests/test_mlmodel.cpp
@@ -12,20 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#define BOOST_TEST_MODULE test module test_mlmodel
 #include <viam/sdk/services/mlmodel.hpp>
 
 #include <memory>
 #include <tuple>
 #include <unordered_map>
 
+#include <boost/test/included/unit_test.hpp>
 #include <boost/variant/get.hpp>
 
 #include <viam/sdk/tests/mocks/mlmodel_mocks.hpp>
 #include <viam/sdk/tests/test_utils.hpp>
 
-#define BOOST_TEST_MODULE test module test_mlmodel
-#include <boost/test/included/unit_test.hpp>
-
 namespace viam {
 namespace sdk {
 

From e80bf807136a7b956d1744ac204e27fe6fd39f14 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 12 Mar 2025 13:45:01 -0400
Subject: [PATCH 40/86] conditionally find unit test framework and link it to
 tests

---
 CMakeLists.txt                    | 6 +++++-
 src/viam/sdk/tests/CMakeLists.txt | 1 +
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4bebd7564..c025261b6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -324,7 +324,11 @@ set(VIAMCPPSDK_XTL_VERSION_MINIMUM 0.7.2)
 set(VIAMCPPSDK_XTENSOR_VERSION_MINIMUM 0.24.3)
 
 # Time to find `BOOST`.
-find_package(Boost ${VIAMCPPSDK_BOOST_VERSION_MINIMUM} REQUIRED COMPONENTS headers log program_options)
+if (VIAMCPPSDK_BUILD_TESTS)
+  set(VIAMCPPSDK_BOOST_TEST "unit_test_framework")
+endif()
+
+find_package(Boost ${VIAMCPPSDK_BOOST_VERSION_MINIMUM} REQUIRED COMPONENTS headers log program_options ${VIAMCPPSDK_BOOST_TEST})
 
 # Time to find `protobuf` and `gRPC[++]`. Normally this would just be
 # something like `find_package(gRPC <version> CONFIG REQUIRED)`, and
diff --git a/src/viam/sdk/tests/CMakeLists.txt b/src/viam/sdk/tests/CMakeLists.txt
index de1fbf0b7..fa2d1560d 100644
--- a/src/viam/sdk/tests/CMakeLists.txt
+++ b/src/viam/sdk/tests/CMakeLists.txt
@@ -47,6 +47,7 @@ target_include_directories(viamsdk_test
 
 target_link_libraries(viamsdk_test
   PUBLIC viam-cpp-sdk::viamsdk
+  PUBLIC Boost::unit_test_framework
 )
 
 viamcppsdk_link_viam_api(viamsdk_test PUBLIC)

From 185121ae13f80aa0c84858e7130349b7a46afd99 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 12 Mar 2025 13:45:44 -0400
Subject: [PATCH 41/86] registry get cannot create instance by default

---
 src/viam/sdk/common/instance.cpp   | 10 +++++++---
 src/viam/sdk/common/instance.hpp   | 11 ++++++++++-
 src/viam/sdk/registry/registry.cpp |  2 +-
 src/viam/sdk/tests/test_utils.cpp  |  2 +-
 src/viam/sdk/tests/test_utils.hpp  | 12 ++++++++++++
 5 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/src/viam/sdk/common/instance.cpp b/src/viam/sdk/common/instance.cpp
index bc52367f6..86443f458 100644
--- a/src/viam/sdk/common/instance.cpp
+++ b/src/viam/sdk/common/instance.cpp
@@ -34,10 +34,14 @@ Instance::~Instance() {
     impl_.reset();
 }
 
-Instance& Instance::current() {
+Instance& Instance::current(Instance::Creation creation) {
     if (!current_instance.load()) {
-        // This variable declaration calls the default ctor, storing a current instance.
-        static Instance inst;  // NOLINT (misc-const-correctness)
+        if (creation == Creation::if_needed) {
+            // This variable declaration calls the default ctor, storing a current instance.
+            static Instance inst;  // NOLINT (misc-const-correctness)
+        } else {
+            throw Exception("Instance has not yet been created");
+        }
     }
 
     Instance* current = current_instance.load();
diff --git a/src/viam/sdk/common/instance.hpp b/src/viam/sdk/common/instance.hpp
index 64bb430c2..1dd0901df 100644
--- a/src/viam/sdk/common/instance.hpp
+++ b/src/viam/sdk/common/instance.hpp
@@ -12,10 +12,19 @@ namespace sdk {
 /// objects in the same program is an error.
 class Instance {
    public:
+    /// @brief Enumeration for creation behavior of @ref current
+    enum class Creation {
+        open_existing,  ///< Instance must already exist
+        if_needed       ///< Use existing instance if present, else create one.
+    };
+
     Instance();
     ~Instance();
 
-    static Instance& current();
+    /// @brief Get the current Instance according to the Creation behavior.
+    /// Calling current(Creation::open_existing) when an instance has not yet been constructed is an
+    /// error.
+    static Instance& current(Creation);
 
    private:
     friend class Registry;
diff --git a/src/viam/sdk/registry/registry.cpp b/src/viam/sdk/registry/registry.cpp
index e538d2ec2..de74d314d 100644
--- a/src/viam/sdk/registry/registry.cpp
+++ b/src/viam/sdk/registry/registry.cpp
@@ -92,7 +92,7 @@ const Model& ModelRegistration::model() const {
 };
 
 Registry& Registry::get() {
-    static Registry& result = Instance::current().impl_->registry;
+    static Registry& result = Instance::current(Instance::Creation::open_existing).impl_->registry;
 
     return result;
 }
diff --git a/src/viam/sdk/tests/test_utils.cpp b/src/viam/sdk/tests/test_utils.cpp
index 0ce5c83ca..c83e59580 100644
--- a/src/viam/sdk/tests/test_utils.cpp
+++ b/src/viam/sdk/tests/test_utils.cpp
@@ -10,8 +10,8 @@
 #include <viam/sdk/spatialmath/orientation_types.hpp>
 
 namespace viam {
-namespace sdktests {
 
+namespace sdktests {
 using namespace viam::sdk;
 
 ProtoStruct fake_map() {
diff --git a/src/viam/sdk/tests/test_utils.hpp b/src/viam/sdk/tests/test_utils.hpp
index 11519a551..201451188 100644
--- a/src/viam/sdk/tests/test_utils.hpp
+++ b/src/viam/sdk/tests/test_utils.hpp
@@ -2,6 +2,9 @@
 
 #include <grpcpp/grpcpp.h>
 
+#include <boost/test/unit_test.hpp>
+
+#include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/config/resource.hpp>
 #include <viam/sdk/registry/registry.hpp>
 #include <viam/sdk/resource/resource.hpp>
@@ -10,6 +13,15 @@
 namespace viam {
 namespace sdktests {
 
+struct GlobalFixture {
+    GlobalFixture() {
+        BOOST_TEST_MESSAGE("Creating instance");
+        (void)sdk::Instance::current(sdk::Instance::Creation::if_needed);
+    }
+};
+
+BOOST_TEST_GLOBAL_FIXTURE(GlobalFixture);
+
 using namespace viam::sdk;
 
 ProtoStruct fake_map();

From 7a747ec8f99fd9d529db8c37f3713b9d7a6bc7dc Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 12 Mar 2025 13:48:39 -0400
Subject: [PATCH 42/86] instance models fixture

---
 src/viam/sdk/tests/test_utils.hpp | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/src/viam/sdk/tests/test_utils.hpp b/src/viam/sdk/tests/test_utils.hpp
index 201451188..43825b8e2 100644
--- a/src/viam/sdk/tests/test_utils.hpp
+++ b/src/viam/sdk/tests/test_utils.hpp
@@ -13,16 +13,8 @@
 namespace viam {
 namespace sdktests {
 
-struct GlobalFixture {
-    GlobalFixture() {
-        BOOST_TEST_MESSAGE("Creating instance");
-        (void)sdk::Instance::current(sdk::Instance::Creation::if_needed);
-    }
-};
-
-BOOST_TEST_GLOBAL_FIXTURE(GlobalFixture);
-
 using namespace viam::sdk;
+BOOST_TEST_GLOBAL_FIXTURE(Instance);
 
 ProtoStruct fake_map();
 std::vector<GeometryConfig> fake_geometries();

From 9c4dc97111a2ce43bd8f0f917e0b3566601e0bdd Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 12 Mar 2025 14:05:15 -0400
Subject: [PATCH 43/86] create if needed instance in fixture

---
 src/viam/sdk/tests/test_utils.hpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/viam/sdk/tests/test_utils.hpp b/src/viam/sdk/tests/test_utils.hpp
index 43825b8e2..bb69e2499 100644
--- a/src/viam/sdk/tests/test_utils.hpp
+++ b/src/viam/sdk/tests/test_utils.hpp
@@ -13,8 +13,15 @@
 namespace viam {
 namespace sdktests {
 
+struct GlobalFixture {
+    GlobalFixture() {
+        (void)sdk::Instance::current(sdk::Instance::Creation::if_needed);
+    }
+};
+
+BOOST_TEST_GLOBAL_FIXTURE(GlobalFixture);
+
 using namespace viam::sdk;
-BOOST_TEST_GLOBAL_FIXTURE(Instance);
 
 ProtoStruct fake_map();
 std::vector<GeometryConfig> fake_geometries();

From 7409dc78319586f9141d4070f61b1f30f0b95cab Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 12 Mar 2025 14:08:11 -0400
Subject: [PATCH 44/86] revert namespace ws

---
 src/viam/sdk/tests/test_utils.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/viam/sdk/tests/test_utils.cpp b/src/viam/sdk/tests/test_utils.cpp
index c83e59580..9e76e2568 100644
--- a/src/viam/sdk/tests/test_utils.cpp
+++ b/src/viam/sdk/tests/test_utils.cpp
@@ -10,7 +10,6 @@
 #include <viam/sdk/spatialmath/orientation_types.hpp>
 
 namespace viam {
-
 namespace sdktests {
 using namespace viam::sdk;
 

From c67166becbff5a66000dbd8212c2e5fe0bcdb93c Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 13 Mar 2025 15:08:14 -0400
Subject: [PATCH 45/86] move cout redirect to test_utils

---
 src/viam/sdk/tests/test_log.cpp   | 22 ----------------------
 src/viam/sdk/tests/test_utils.hpp | 22 ++++++++++++++++++++++
 2 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/src/viam/sdk/tests/test_log.cpp b/src/viam/sdk/tests/test_log.cpp
index d21e3787b..bcf770d7c 100644
--- a/src/viam/sdk/tests/test_log.cpp
+++ b/src/viam/sdk/tests/test_log.cpp
@@ -12,28 +12,6 @@
 namespace viam {
 namespace sdktests {
 
-// Buffer output filter to test console logging.
-// In practice this is a pain and makes it hard to inspect Boost.Test output,
-// so rather than using it as a test fixture we manually instantiate it in an optional.
-// Further log testing is done in the complex module example tests.
-// https://stackoverflow.com/a/5405268
-struct cout_redirect {
-    cout_redirect() : old(std::cout.rdbuf(os.rdbuf())) {}
-
-    void release() {
-        std::cout.rdbuf(old);
-    }
-
-    ~cout_redirect() {
-        release();
-    }
-
-    std::ostringstream os;
-
-   private:
-    std::streambuf* old;
-};
-
 BOOST_AUTO_TEST_CASE(test_cout_logging) {
     cout_redirect redirect;
 
diff --git a/src/viam/sdk/tests/test_utils.hpp b/src/viam/sdk/tests/test_utils.hpp
index bb69e2499..3184588ab 100644
--- a/src/viam/sdk/tests/test_utils.hpp
+++ b/src/viam/sdk/tests/test_utils.hpp
@@ -21,6 +21,28 @@ struct GlobalFixture {
 
 BOOST_TEST_GLOBAL_FIXTURE(GlobalFixture);
 
+// Buffer output filter to test console logging.
+// In practice this is a pain and makes it hard to inspect Boost.Test output,
+// so rather than using it as a test fixture we manually instantiate it in an optional.
+// Further log testing is done in the complex module example tests.
+// https://stackoverflow.com/a/5405268
+struct cout_redirect {
+    cout_redirect() : old(std::cout.rdbuf(os.rdbuf())) {}
+
+    void release() {
+        std::cout.rdbuf(old);
+    }
+
+    ~cout_redirect() {
+        release();
+    }
+
+    std::ostringstream os;
+
+   private:
+    std::streambuf* old;
+};
+
 using namespace viam::sdk;
 
 ProtoStruct fake_map();

From 0745e16320acd9751a00b4ab84ea47cf948b6ace Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 13 Mar 2025 17:15:03 -0400
Subject: [PATCH 46/86] implement set resource log level

---
 src/viam/sdk/log/logger.cpp | 4 ++++
 src/viam/sdk/log/logger.hpp | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index 370ddf89a..736ad7011 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -100,6 +100,10 @@ void Logger::set_global_log_level(int argc, char** argv) {
     }
 }
 
+void Logger::set_resource_log_level(const std::string& resource, log_level lvl) {
+    resource_levels_[resource] = lvl;
+}
+
 void Logger::init_logging() {
     sdk_logger_.channel(global_resource_name());
     boost::log::core::get()->add_global_attribute("TimeStamp",
diff --git a/src/viam/sdk/log/logger.hpp b/src/viam/sdk/log/logger.hpp
index 82fe12334..a6c955f74 100644
--- a/src/viam/sdk/log/logger.hpp
+++ b/src/viam/sdk/log/logger.hpp
@@ -55,7 +55,7 @@ class Logger {
     /// how to start modules with a "log-level" commandline argument.
     void set_global_log_level(int argc, char** argv);
 
-    void set_resource_log_level(std::string resource, log_level);
+    void set_resource_log_level(const std::string& resource, log_level);
 
     LogSource& logger() {
         return sdk_logger_;

From 04edd0d5c6821505584d355b60511005b784fe46 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 13 Mar 2025 17:15:39 -0400
Subject: [PATCH 47/86] implement set resource log level for resource itself

---
 src/viam/sdk/resource/resource.cpp | 4 ++++
 src/viam/sdk/resource/resource.hpp | 3 +++
 2 files changed, 7 insertions(+)

diff --git a/src/viam/sdk/resource/resource.cpp b/src/viam/sdk/resource/resource.cpp
index 48bbb0f37..3b46fb00d 100644
--- a/src/viam/sdk/resource/resource.cpp
+++ b/src/viam/sdk/resource/resource.cpp
@@ -29,5 +29,9 @@ Name Resource::get_resource_name() const {
     return get_resource_name(kResource);
 }
 
+void Resource::set_log_level(log_level ll) const {
+    Logger::get().set_resource_log_level(name_, ll);
+}
+
 }  // namespace sdk
 }  // namespace viam
diff --git a/src/viam/sdk/resource/resource.hpp b/src/viam/sdk/resource/resource.hpp
index 6d05b7cec..afd3f87d5 100644
--- a/src/viam/sdk/resource/resource.hpp
+++ b/src/viam/sdk/resource/resource.hpp
@@ -28,6 +28,9 @@ class Resource {
     /// @brief Return the resource's name.
     virtual std::string name() const;
 
+    /// @brief Set the log level for log messages originating from this Resource.
+    void set_log_level(log_level) const;
+
    private:
     std::string name_;
 

From 537ea67b856b1946225ad2712d9a355e5cb1abcc Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 13 Mar 2025 17:15:58 -0400
Subject: [PATCH 48/86] test resource level logging in unit tests

---
 src/viam/sdk/tests/test_log.cpp | 55 +++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/src/viam/sdk/tests/test_log.cpp b/src/viam/sdk/tests/test_log.cpp
index bcf770d7c..b06a5c7a1 100644
--- a/src/viam/sdk/tests/test_log.cpp
+++ b/src/viam/sdk/tests/test_log.cpp
@@ -7,6 +7,7 @@
 #include <sstream>
 
 #include <viam/sdk/resource/resource.hpp>
+#include <viam/sdk/tests/mocks/mock_sensor.hpp>
 #include <viam/sdk/tests/test_utils.hpp>
 
 namespace viam {
@@ -68,5 +69,59 @@ BOOST_AUTO_TEST_CASE(test_global_filter) {
     }
 }
 
+struct LogSensor : sensor::MockSensor {
+    using sensor::MockSensor::MockSensor;
+
+    sdk::ProtoStruct get_readings(const sdk::ProtoStruct& extra) override {
+        VIAM_RESOURCE_LOG(info) << "sensor info";
+        VIAM_RESOURCE_LOG(error) << "sensor error";
+        return sensor::MockSensor::get_readings(extra);
+    }
+};
+
+BOOST_AUTO_TEST_CASE(test_resource_filter) {
+    cout_redirect redirect;
+
+    auto defaultSensor = std::make_shared<LogSensor>("DefaultSensor");
+    auto errorSensor = std::make_shared<LogSensor>("ErrorSensor");
+    errorSensor->set_log_level(sdk::log_level::error);
+
+    for (auto sensor : {defaultSensor, errorSensor}) {
+        client_to_mock_pipeline<Sensor>(sensor,
+                                        [&](Sensor& client) { (void)client.get_readings({}); });
+    }
+
+    std::vector<std::string> defaultLogs;
+    std::vector<std::string> errLogs;
+
+    const std::string rec = redirect.os.str();
+    BOOST_TEST_INFO("Log records\n" << rec);
+
+    std::stringstream ss(rec);
+    redirect.release();
+
+    {
+        std::string local;
+
+        while (std::getline(ss, local, '\n')) {
+            if (local.find("DefaultSensor") != std::string::npos) {
+                defaultLogs.push_back(std::move(local));
+            } else if (local.find("ErrorSensor") != std::string::npos) {
+                errLogs.push_back(std::move(local));
+            }
+        }
+    }
+
+    BOOST_ASSERT(defaultLogs.size() == 2);
+    {
+        BOOST_TEST_MESSAGE("default logs\n" << defaultLogs.front() << "\n" << defaultLogs.back());
+        BOOST_CHECK(defaultLogs.front().find("sensor info") != std::string::npos);
+        BOOST_CHECK(defaultLogs.back().find("sensor error") != std::string::npos);
+    }
+
+    BOOST_ASSERT(errLogs.size() == 1);
+    BOOST_CHECK(errLogs.back().find("sensor error") != std::string::npos);
+}
+
 }  // namespace sdktests
 }  // namespace viam

From 56e706c298f903c6cbafe9bd77ee0dab6bf9e51a Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 13 Mar 2025 17:16:26 -0400
Subject: [PATCH 49/86] set resource log level from config in start/reconfigure

---
 src/viam/sdk/module/service.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp
index fa9d55674..423acd143 100644
--- a/src/viam/sdk/module/service.cpp
+++ b/src/viam/sdk/module/service.cpp
@@ -60,6 +60,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         if (reg) {
             try {
                 res = reg->construct_resource(deps, cfg);
+                res->set_log_level(cfg.log_level());
             } catch (const std::exception& exc) {
                 return grpc::Status(::grpc::INTERNAL, exc.what());
             }
@@ -98,6 +99,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         }
         try {
             Reconfigurable::reconfigure_if_reconfigurable(res, deps, cfg);
+            res->set_log_level(cfg.log_level());
             return grpc::Status();
         } catch (const std::exception& exc) {
             return grpc::Status(::grpc::INTERNAL, exc.what());

From 818825a8fb73bcaf48c01e3cddb75845f7dca7f3 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 14 Mar 2025 11:27:49 -0400
Subject: [PATCH 50/86] conditionally find program options as well

---
 CMakeLists.txt | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0a1f835df..cf42ab901 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -328,12 +328,16 @@ if (VIAMCPPSDK_BUILD_TESTS)
   set(VIAMCPPSDK_BOOST_TEST "unit_test_framework")
 endif()
 
+if (VIAMCPPSDK_BUILD_EXAMPLES)
+  set(VIAMCPPSDK_BOOST_PROGRAM_OPTIONS "program_options")
+endif()
+
 find_package(Boost ${VIAMCPPSDK_BOOST_VERSION_MINIMUM} REQUIRED 
   COMPONENTS 
     headers 
     log 
     log_setup 
-    program_options 
+    ${VIAMCPPSDK_BOOST_PROGRAM_OPTIONS}
     ${VIAMCPPSDK_BOOST_TEST}
 )
 

From 6d43bbd18b3cfec917bd894362ef3595ca59eb37 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 14 Mar 2025 12:02:55 -0400
Subject: [PATCH 51/86] remove trailing semicolon

---
 src/viam/sdk/module/module.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/viam/sdk/module/module.cpp b/src/viam/sdk/module/module.cpp
index 6ccc4b62c..7af0fa34b 100644
--- a/src/viam/sdk/module/module.cpp
+++ b/src/viam/sdk/module/module.cpp
@@ -9,7 +9,7 @@
 namespace viam {
 namespace sdk {
 
-Module::Module(std::string addr) : addr_(std::move(addr)) {};
+Module::Module(std::string addr) : addr_(std::move(addr)) {}
 
 void Module::set_ready() {
     ready_ = true;

From de5e33aee351ca13d361e28fd3e8ac86b03b944e Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 14 Mar 2025 12:18:11 -0400
Subject: [PATCH 52/86] private/friend decls

---
 src/viam/sdk/robot/client.hpp | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/src/viam/sdk/robot/client.hpp b/src/viam/sdk/robot/client.hpp
index e60eb0edc..8b00d4250 100644
--- a/src/viam/sdk/robot/client.hpp
+++ b/src/viam/sdk/robot/client.hpp
@@ -20,6 +20,10 @@
 namespace viam {
 namespace sdk {
 
+namespace impl {
+struct LogBackend;
+}
+
 /// @defgroup Robot Classes related to a Robot representation.
 
 /// @class RobotClient client.hpp "robot/client.hpp"
@@ -88,13 +92,6 @@ class RobotClient {
 
     RobotClient(std::shared_ptr<ViamChannel> channel);
 
-    void log(const std::string& name,
-             const std::string& level,
-             const std::string& message,
-             time_pt time);
-
-    void connect_logging();
-
     std::vector<Name> resource_names() const;
 
     /// @brief Lookup and return a `shared_ptr` to a resource.
@@ -156,6 +153,16 @@ class RobotClient {
     status get_machine_status() const;
 
    private:
+    friend class ModuleService;
+    friend struct impl::LogBackend;
+
+    void log(const std::string& name,
+             const std::string& level,
+             const std::string& message,
+             time_pt time);
+
+    void connect_logging();
+
     void refresh_every();
 
     std::vector<std::shared_ptr<std::thread>> threads_;

From a2865084478500c21f3960a15ecbec73a05eebc0 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 14 Mar 2025 12:18:19 -0400
Subject: [PATCH 53/86] docs

---
 src/viam/sdk/log/logger.cpp              |  4 ++
 src/viam/sdk/log/logger.hpp              | 59 +++++++++++++++++++++---
 src/viam/sdk/log/private/log_backend.hpp |  1 +
 3 files changed, 58 insertions(+), 6 deletions(-)

diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index 736ad7011..565e67faf 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -90,6 +90,10 @@ Logger& Logger::get() {
     return result;
 }
 
+LogSource& Logger::logger() {
+    return sdk_logger_;
+}
+
 void Logger::set_global_log_level(log_level lvl) {
     global_level_ = lvl;
 }
diff --git a/src/viam/sdk/log/logger.hpp b/src/viam/sdk/log/logger.hpp
index a6c955f74..c4bf50936 100644
--- a/src/viam/sdk/log/logger.hpp
+++ b/src/viam/sdk/log/logger.hpp
@@ -1,3 +1,6 @@
+/// @file log/logger.hpp
+///
+/// @brief Defines logging infrastructure
 #pragma once
 
 #include <cstdint>
@@ -16,6 +19,10 @@
 namespace viam {
 namespace sdk {
 
+/// @defgroup Log Classes related to logging.
+
+/// @brief Severity levels for the logger.
+/// @ingroup Log
 enum class log_level : std::int8_t {
     trace = -2,
     debug = -1,
@@ -31,10 +38,23 @@ log_level level_from_string(std::string level);
 
 std::ostream& operator<<(std::ostream&, log_level);
 
+/// @brief Type alias for the log source in the C++ SDK.
+/// @ingroup Log
+///
+/// In the paradigm of Boost.Log the C++ SDK has precisely one logging source, namely the source of
+/// messages generated by the user invoking one of the logging macros.
 using LogSource = boost::log::sources::severity_channel_logger_mt<log_level>;
 
+/// @brief Returns the "channel name" of general log messages not originating from resources.
+/// @ingroup Log
 std::string global_resource_name();
 
+/// @class Logger logger.hpp "log/logger.hpp"
+/// @brief Manages the logging infrastructure in the SDDK.
+/// @ingroup Log
+///
+/// Handles initialization and bookkeeping for logging infrastructure in the C++ SDK. This includes
+/// console logging, if active, and both global and resource-level log filtering.
 class Logger {
    public:
     struct Filter {
@@ -43,29 +63,47 @@ class Logger {
         bool operator()(const boost::log::attribute_value_set&);
     };
 
+    /// @brief Returns the unique logger instance.
+    ///
+    /// This is the only way to access the logger.
     static Logger& get();
 
+    /// @brief Set the global logger severity.
     void set_global_log_level(log_level);
 
-    /// @brief Set the SDK logger severity from a command line argument vector.
+    /// @brief Set the global logger severity from a command line argument vector.
     ///
-    /// Sets the boost trivial logger's severity to debug if "--log-level=debug" is the
+    /// Sets severity to debug if "--log-level=debug" is
     /// the third argument. Sets severity to info otherwise. Useful for module
     /// implementations. See LogLevel documentation in the RDK for more information on
     /// how to start modules with a "log-level" commandline argument.
     void set_global_log_level(int argc, char** argv);
 
+    /// @brief Set the logger severity for a resource.
+    ///
+    /// Logger can maintain separate severity levels for individual resources. For example, you may
+    /// want to only report "error" messages for global logs and logs for one component of a modular
+    /// resource, but enable verbose logging for a new component that is still being debugged.
+    /// @see @ref Resource, which has a set_log_level method which automatically provides its own
+    /// resource name.
     void set_resource_log_level(const std::string& resource, log_level);
 
-    LogSource& logger() {
-        return sdk_logger_;
-    }
+    /// @brief Return the SDK global log source.
+    ///
+    /// Users should prefer to log messages using the logging macros below.
+    LogSource& logger();
 
    private:
     friend class RobotClient;
     friend class Instance;
     Logger() = default;
 
+    Logger(const Logger&) = delete;
+    Logger(Logger&&) = delete;
+
+    Logger& operator=(const Logger&) = delete;
+    Logger& operator=(Logger&&) = delete;
+
     void init_logging();
     void disable_console_logging();
 
@@ -86,14 +124,23 @@ BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_time,
                                  "TimeStamp",
                                  boost::log::attributes::local_clock::value_type);
 
-// Logging macro for general SDK logs
 #define VIAM_LOG_IMPL(lg, level)                                            \
     BOOST_LOG_SEV((lg), ::viam::sdk::log_level::level)                      \
         << ::boost::log::add_value(::viam::sdk::attr_file_type{}, __FILE__) \
         << ::boost::log::add_value(::viam::sdk::attr_line_type{}, __LINE__)
 
+/// @brief Log macro for general SDK logs.
+/// @ingroup Log
+///
+/// Use this macro to generate log messages pertaining to the SDK at large.
 #define VIAM_LOG(level) VIAM_LOG_IMPL(::viam::sdk::Logger::get().logger(), level)
 
+/// @brief Log macro for resource-level logs.
+/// @ingroup Log
+///
+/// This macro can only be called from the definition of a member function of a class inheriting
+/// @ref Resource. It will log messages to the log source of that specific resource, allowing
+/// resource-level log filtering.
 #define VIAM_RESOURCE_LOG(level) VIAM_LOG_IMPL(this->logger_, level)
 
 }  // namespace sdk
diff --git a/src/viam/sdk/log/private/log_backend.hpp b/src/viam/sdk/log/private/log_backend.hpp
index 8bb2dc4cb..216621e65 100644
--- a/src/viam/sdk/log/private/log_backend.hpp
+++ b/src/viam/sdk/log/private/log_backend.hpp
@@ -14,6 +14,7 @@ struct LogBackend;
 
 using SinkType = boost::log::sinks::synchronous_sink<LogBackend>;
 
+// Log backend which implements sending messages to RDK.
 struct LogBackend : boost::log::sinks::basic_sink_backend<boost::log::sinks::synchronized_feeding> {
     LogBackend(RobotClient* p) : parent(p) {}
 

From e45bafc8b3be02507099fd3005e8309561c638b1 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 10:17:35 -0400
Subject: [PATCH 54/86] update method name to avoid conflict

---
 src/viam/sdk/config/resource.cpp | 4 ++--
 src/viam/sdk/config/resource.hpp | 2 +-
 src/viam/sdk/module/service.cpp  | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/viam/sdk/config/resource.cpp b/src/viam/sdk/config/resource.cpp
index 3d3251d49..23b206efb 100644
--- a/src/viam/sdk/config/resource.cpp
+++ b/src/viam/sdk/config/resource.cpp
@@ -83,7 +83,7 @@ const std::string& ResourceConfig::type() const {
     return type_;
 }
 
-log_level ResourceConfig::log_level() const {
+log_level ResourceConfig::get_log_level() const {
     return log_level_;
 }
 
@@ -143,7 +143,7 @@ void to_proto_impl<ResourceConfig>::operator()(const ResourceConfig& self,
     *proto->mutable_namespace_() = self.namespace_();
     *proto->mutable_type() = self.type();
     *proto->mutable_api() = self.api().to_string();
-    *proto->mutable_log_configuration()->mutable_level() = to_string(self.log_level());
+    *proto->mutable_log_configuration()->mutable_level() = to_string(self.get_log_level());
     *proto->mutable_model() = self.model().to_string();
     *proto->mutable_attributes() = to_proto(self.attributes());
 
diff --git a/src/viam/sdk/config/resource.hpp b/src/viam/sdk/config/resource.hpp
index ee55d4112..622c12780 100644
--- a/src/viam/sdk/config/resource.hpp
+++ b/src/viam/sdk/config/resource.hpp
@@ -63,7 +63,7 @@ class ResourceConfig {
     const std::string& namespace_() const;
     const std::string& type() const;
 
-    log_level log_level() const;
+    log_level get_log_level() const;
 
     const std::vector<std::string>& depends_on() const;
 
diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp
index 423acd143..3c3b31e04 100644
--- a/src/viam/sdk/module/service.cpp
+++ b/src/viam/sdk/module/service.cpp
@@ -60,7 +60,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         if (reg) {
             try {
                 res = reg->construct_resource(deps, cfg);
-                res->set_log_level(cfg.log_level());
+                res->set_log_level(cfg.get_log_level());
             } catch (const std::exception& exc) {
                 return grpc::Status(::grpc::INTERNAL, exc.what());
             }
@@ -99,7 +99,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         }
         try {
             Reconfigurable::reconfigure_if_reconfigurable(res, deps, cfg);
-            res->set_log_level(cfg.log_level());
+            res->set_log_level(cfg.get_log_level());
             return grpc::Status();
         } catch (const std::exception& exc) {
             return grpc::Status(::grpc::INTERNAL, exc.what());

From 14a60496540aafa711054141e5d8aec9fa232adb Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 11:38:36 -0400
Subject: [PATCH 55/86] const qualify method and member ptr

---
 src/viam/sdk/log/logger.cpp | 2 +-
 src/viam/sdk/log/logger.hpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index 565e67faf..bdd7a51a2 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -67,7 +67,7 @@ std::string global_resource_name() {
     return "Viam C++ SDK";
 }
 
-bool Logger::Filter::operator()(const boost::log::attribute_value_set& attrs) {
+bool Logger::Filter::operator()(const boost::log::attribute_value_set& attrs) const {
     auto sev = attrs[attr_sev_type{}];
     if (!sev) {
         return false;
diff --git a/src/viam/sdk/log/logger.hpp b/src/viam/sdk/log/logger.hpp
index c4bf50936..fff95a277 100644
--- a/src/viam/sdk/log/logger.hpp
+++ b/src/viam/sdk/log/logger.hpp
@@ -58,9 +58,9 @@ std::string global_resource_name();
 class Logger {
    public:
     struct Filter {
-        Logger* parent;
+        const Logger* parent;
 
-        bool operator()(const boost::log::attribute_value_set&);
+        bool operator()(const boost::log::attribute_value_set&) const;
     };
 
     /// @brief Returns the unique logger instance.

From 216df1d4556cd7ef8fcafc7133298e79b495a3fc Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 12:40:34 -0400
Subject: [PATCH 56/86] silence tidy error

---
 src/viam/sdk/log/logger.cpp | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index bdd7a51a2..111f09808 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -35,26 +35,26 @@ std::string to_string(log_level lvl) {
     }
 }
 
-log_level level_from_string(std::string str) {
+log_level level_from_string(std::string level) {
     using ll = log_level;
 
-    std::transform(str.begin(), str.end(), str.begin(), ::tolower);
+    std::transform(level.begin(), level.end(), level.begin(), ::tolower);
 
-    if (str == "info") {
+    if (level == "info") {
         return ll::info;
-    } else if (str == "warn" || str == "warning") {
+    } else if (level == "warn" || level == "warning") {
         return ll::warn;
-    } else if (str == "error") {
+    } else if (level == "error") {
         return ll::error;
-    } else if (str == "debug") {
+    } else if (level == "debug") {
         return ll::debug;
-    } else if (str == "trace") {
+    } else if (level == "trace") {
         return ll::trace;
-    } else if (str == "fatal") {
+    } else if (level == "fatal") {
         return ll::fatal;
     }
 
-    VIAM_LOG(warn) << "Returning unknown log level " << str << " as info";
+    VIAM_LOG(warn) << "Returning unknown log level " << level << " as info";
     return ll::info;
 }
 

From f4b0f119d74e3ef0daaccbad884b5756bb4b34dd Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 14:29:41 -0400
Subject: [PATCH 57/86] fix file logging

---
 src/viam/sdk/log/logger.cpp | 17 +++++++++++++++++
 src/viam/sdk/log/logger.hpp | 18 ++++++++++++++----
 2 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index 111f09808..1e5dd7d40 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -137,5 +137,22 @@ void Logger::disable_console_logging() {
     boost::log::core::get()->remove_sink(console_sink_);
 }
 
+namespace log_detail {
+
+boost::string_view trim_filename(const char* file) {
+    boost::string_view result(file);
+    const std::size_t second_last = result  //
+                                        .substr(0, result.find_last_of('/'))
+                                        .find_last_of('/');
+
+    if (second_last != boost::string_view::npos) {
+        return result.substr(second_last + 1, result.size() - second_last);
+    }
+
+    return result;
+}
+
+}  // namespace log_detail
+
 }  // namespace sdk
 }  // namespace viam
diff --git a/src/viam/sdk/log/logger.hpp b/src/viam/sdk/log/logger.hpp
index fff95a277..93817c142 100644
--- a/src/viam/sdk/log/logger.hpp
+++ b/src/viam/sdk/log/logger.hpp
@@ -15,6 +15,7 @@
 #include <boost/log/sinks/text_ostream_backend.hpp>
 #include <boost/log/sources/severity_channel_logger.hpp>
 #include <boost/log/utility/manipulators/add_value.hpp>
+#include <boost/utility/string_view.hpp>
 
 namespace viam {
 namespace sdk {
@@ -116,17 +117,26 @@ class Logger {
     std::map<std::string, log_level> resource_levels_;
 };
 
+namespace log_detail {
+
+// Some of the filenames in the SDK are not unique, eg config/resource.hpp, resource/resource.hpp.
+// This function trims a full filename /path/to/some/file.xpp to some/file.xpp
+boost::string_view trim_filename(const char* file);
+
+}  // namespace log_detail
+
 BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_channel, "Channel", std::string);
 BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_sev, "Severity", viam::sdk::log_level);
-BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_file, "file", const char*);
+BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_file, "file", boost::string_view);
 BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_line, "line", unsigned int);
 BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_time,
                                  "TimeStamp",
                                  boost::log::attributes::local_clock::value_type);
 
-#define VIAM_LOG_IMPL(lg, level)                                            \
-    BOOST_LOG_SEV((lg), ::viam::sdk::log_level::level)                      \
-        << ::boost::log::add_value(::viam::sdk::attr_file_type{}, __FILE__) \
+#define VIAM_LOG_IMPL(lg, level)                                                     \
+    BOOST_LOG_SEV((lg), ::viam::sdk::log_level::level)                               \
+        << ::boost::log::add_value(::viam::sdk::attr_file_type{},                    \
+                                   ::viam::sdk::log_detail::trim_filename(__FILE__)) \
         << ::boost::log::add_value(::viam::sdk::attr_line_type{}, __LINE__)
 
 /// @brief Log macro for general SDK logs.

From e3e269730a5d7fe451b9255fe8a1add01c204fce Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 14:40:09 -0400
Subject: [PATCH 58/86] handle empty string and clarify level formatting

---
 src/viam/sdk/config/resource.cpp | 3 ++-
 src/viam/sdk/log/logger.cpp      | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/viam/sdk/config/resource.cpp b/src/viam/sdk/config/resource.cpp
index 23b206efb..2f0b131ce 100644
--- a/src/viam/sdk/config/resource.cpp
+++ b/src/viam/sdk/config/resource.cpp
@@ -155,6 +155,7 @@ void to_proto_impl<ResourceConfig>::operator()(const ResourceConfig& self,
 
 ResourceConfig from_proto_impl<app::v1::ComponentConfig>::operator()(
     const app::v1::ComponentConfig* proto) const {
+    const std::string& level_str = proto->log_configuration().level();
     return ResourceConfig(proto->type(),
                           proto->name(),
                           proto->namespace_(),
@@ -162,7 +163,7 @@ ResourceConfig from_proto_impl<app::v1::ComponentConfig>::operator()(
                           proto->api(),
                           Model::from_str(proto->model()),
                           proto->has_frame() ? from_proto(proto->frame()) : LinkConfig{},
-                          level_from_string(proto->log_configuration().level()));
+                          level_str.empty() ? log_level::info : level_from_string(level_str));
 }
 
 std::vector<ResourceConfig>
diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index 1e5dd7d40..b247dc6c2 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -54,7 +54,7 @@ log_level level_from_string(std::string level) {
         return ll::fatal;
     }
 
-    VIAM_LOG(warn) << "Returning unknown log level " << level << " as info";
+    VIAM_LOG(warn) << "Returning unknown log level `" << level << "` as info";
     return ll::info;
 }
 

From bdebbab0cfa60a20d504183eed5966e4e884aa67 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 14:47:14 -0400
Subject: [PATCH 59/86] log the logging

---
 src/viam/sdk/log/logger.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index b247dc6c2..90f8c994c 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -131,9 +131,11 @@ void Logger::init_logging() {
     console_sink_->set_formatter(fmt);
 
     boost::log::core::get()->add_sink(console_sink_);
+    VIAM_LOG(info) << "Initialized console logging";
 }
 
 void Logger::disable_console_logging() {
+    VIAM_LOG(info) << "Disabling console logging";
     boost::log::core::get()->remove_sink(console_sink_);
 }
 

From 8c372ccb2b16725f082d789ec1254fcca81db5b5 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 15:59:06 -0400
Subject: [PATCH 60/86] linter warnings...........

---
 src/viam/sdk/log/private/log_backend.cpp | 13 +++++++------
 src/viam/sdk/log/private/log_backend.hpp |  2 +-
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/src/viam/sdk/log/private/log_backend.cpp b/src/viam/sdk/log/private/log_backend.cpp
index 9f0a34a7f..6e917adc5 100644
--- a/src/viam/sdk/log/private/log_backend.cpp
+++ b/src/viam/sdk/log/private/log_backend.cpp
@@ -8,15 +8,16 @@ namespace sdk {
 namespace impl {
 
 time_pt ptime_convert(const boost::posix_time::ptime& from) {
-    boost::posix_time::time_duration const time_since_epoch =
-        from - boost::posix_time::from_time_t(0);
-    time_pt t = std::chrono::system_clock::from_time_t(time_since_epoch.total_seconds());
-    long nsec =
-        time_since_epoch.fractional_seconds() * (1000000000 / time_since_epoch.ticks_per_second());
+    namespace posix_time = boost::posix_time;
+
+    posix_time::time_duration const time_since_epoch = from - posix_time::from_time_t(0);
+    const time_pt t = std::chrono::system_clock::from_time_t(time_since_epoch.total_seconds());
+    const long nsec = time_since_epoch.fractional_seconds() *
+                      (1000000000 / posix_time::time_duration::ticks_per_second());
     return t + std::chrono::nanoseconds(nsec);
 }
 
-void LogBackend::consume(const boost::log::record_view& rec) {
+void LogBackend::consume(const boost::log::record_view& rec) const {
     parent->log(*rec[attr_channel_type{}],
                 to_string(*rec[attr_sev_type{}]),
                 *rec[boost::log::expressions::smessage],
diff --git a/src/viam/sdk/log/private/log_backend.hpp b/src/viam/sdk/log/private/log_backend.hpp
index 216621e65..a3d2207c0 100644
--- a/src/viam/sdk/log/private/log_backend.hpp
+++ b/src/viam/sdk/log/private/log_backend.hpp
@@ -18,7 +18,7 @@ using SinkType = boost::log::sinks::synchronous_sink<LogBackend>;
 struct LogBackend : boost::log::sinks::basic_sink_backend<boost::log::sinks::synchronized_feeding> {
     LogBackend(RobotClient* p) : parent(p) {}
 
-    void consume(const boost::log::record_view&);
+    void consume(const boost::log::record_view&) const;
 
     static boost::shared_ptr<SinkType> create(RobotClient* p);
 

From 1744939c2a8666ab432223f9a37cd5dfdcfae9af Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 16:00:44 -0400
Subject: [PATCH 61/86] linter.....

---
 src/viam/sdk/log/logger.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index 90f8c994c..dcb2f97ad 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -113,7 +113,7 @@ void Logger::init_logging() {
     boost::log::core::get()->add_global_attribute("TimeStamp",
                                                   boost::log::attributes::local_clock());
 
-    boost::log::formatter fmt =
+    const boost::log::formatter fmt =
         boost::log::expressions::stream
         << boost::log::expressions::format_date_time<boost::posix_time::ptime>(
                "TimeStamp", "%Y--%m--%d %H:%M:%S")
@@ -142,7 +142,7 @@ void Logger::disable_console_logging() {
 namespace log_detail {
 
 boost::string_view trim_filename(const char* file) {
-    boost::string_view result(file);
+    const boost::string_view result(file);
     const std::size_t second_last = result  //
                                         .substr(0, result.find_last_of('/'))
                                         .find_last_of('/');

From d7842c18aa55ebb66db5a3936fe81336fd796fa4 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 17:11:43 -0400
Subject: [PATCH 62/86] add log_setup

---
 src/viam/config/viam-cpp-sdkConfig.cmake.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/viam/config/viam-cpp-sdkConfig.cmake.in b/src/viam/config/viam-cpp-sdkConfig.cmake.in
index 2ee214306..82dbf36fc 100644
--- a/src/viam/config/viam-cpp-sdkConfig.cmake.in
+++ b/src/viam/config/viam-cpp-sdkConfig.cmake.in
@@ -2,7 +2,7 @@
 
 include(CMakeFindDependencyMacro)
 
-find_dependency(Boost @Boost_VERSION_MAJOR@.@Boost_VERSION_MINOR@ COMPONENTS headers log)
+find_dependency(Boost @Boost_VERSION_MAJOR@.@Boost_VERSION_MINOR@ COMPONENTS headers log log_setup)
 
 if (@gRPC_FOUND@)
   find_dependency(gRPC @gRPC_VERSION_MAJOR@.@gRPC_VERSION_MINOR@ CONFIG)

From 9829e4418a4ac3d6fafa052c4d9ef1e3fa2cb1ea Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 17:17:09 -0400
Subject: [PATCH 63/86] remove sdk::

---
 src/viam/sdk/config/resource.cpp | 2 +-
 src/viam/sdk/config/resource.hpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/viam/sdk/config/resource.cpp b/src/viam/sdk/config/resource.cpp
index 2f0b131ce..0e02d5b27 100644
--- a/src/viam/sdk/config/resource.cpp
+++ b/src/viam/sdk/config/resource.cpp
@@ -27,7 +27,7 @@ ResourceConfig::ResourceConfig(std::string type,
                                std::string api,
                                Model model,
                                LinkConfig frame,
-                               sdk::log_level lvl)
+                               log_level lvl)
     : api_({kRDK, type, ""}),
       frame_(std::move(frame)),
       model_(std::move(model)),
diff --git a/src/viam/sdk/config/resource.hpp b/src/viam/sdk/config/resource.hpp
index 622c12780..9aa8d46c7 100644
--- a/src/viam/sdk/config/resource.hpp
+++ b/src/viam/sdk/config/resource.hpp
@@ -47,7 +47,7 @@ class ResourceConfig {
                    std::string api,
                    Model model,
                    LinkConfig frame,
-                   sdk::log_level lvl = sdk::log_level::info);
+                   log_level lvl = sdk::log_level::info);
 
     ResourceConfig(std::string type);
 
@@ -93,7 +93,7 @@ class ResourceConfig {
 
     std::vector<std::string> implicit_depends_on_;
 
-    sdk::log_level log_level_;
+    log_level log_level_;
 };
 
 namespace proto_convert_details {

From e10df010f862a43431c4c20fe9ea0887907d57f2 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 17:22:44 -0400
Subject: [PATCH 64/86] global resource is const char*

---
 src/viam/sdk/log/logger.cpp     | 2 +-
 src/viam/sdk/log/logger.hpp     | 2 +-
 src/viam/sdk/tests/test_log.cpp | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index dcb2f97ad..8c5ff1363 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -63,7 +63,7 @@ std::ostream& operator<<(std::ostream& os, log_level lvl) {
     return os;
 }
 
-std::string global_resource_name() {
+const char* global_resource_name() {
     return "Viam C++ SDK";
 }
 
diff --git a/src/viam/sdk/log/logger.hpp b/src/viam/sdk/log/logger.hpp
index 93817c142..7071f3197 100644
--- a/src/viam/sdk/log/logger.hpp
+++ b/src/viam/sdk/log/logger.hpp
@@ -48,7 +48,7 @@ using LogSource = boost::log::sources::severity_channel_logger_mt<log_level>;
 
 /// @brief Returns the "channel name" of general log messages not originating from resources.
 /// @ingroup Log
-std::string global_resource_name();
+const char* global_resource_name();
 
 /// @class Logger logger.hpp "log/logger.hpp"
 /// @brief Manages the logging infrastructure in the SDDK.
diff --git a/src/viam/sdk/tests/test_log.cpp b/src/viam/sdk/tests/test_log.cpp
index b06a5c7a1..c5c5bc265 100644
--- a/src/viam/sdk/tests/test_log.cpp
+++ b/src/viam/sdk/tests/test_log.cpp
@@ -24,7 +24,7 @@ BOOST_AUTO_TEST_CASE(test_cout_logging) {
     redirect.release();
 
     for (const std::string& s :
-         {"log1"s, to_string(sdk::log_level::info), sdk::global_resource_name()}) {
+         {"log1"s, to_string(sdk::log_level::info), std::string{sdk::global_resource_name()}}) {
         BOOST_CHECK(rec.find(s) != std::string::npos);
     }
 }

From 192b29fea6dfc33ce74f0ec565ca5a4fb4fef8e8 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 17:25:27 -0400
Subject: [PATCH 65/86] log log as debug

---
 src/viam/sdk/log/logger.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index 8c5ff1363..a29716a4f 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -131,11 +131,11 @@ void Logger::init_logging() {
     console_sink_->set_formatter(fmt);
 
     boost::log::core::get()->add_sink(console_sink_);
-    VIAM_LOG(info) << "Initialized console logging";
+    VIAM_LOG(debug) << "Initialized console logging";
 }
 
 void Logger::disable_console_logging() {
-    VIAM_LOG(info) << "Disabling console logging";
+    VIAM_LOG(debug) << "Disabling console logging";
     boost::log::core::get()->remove_sink(console_sink_);
 }
 

From 2de9f40a41e3385dc820d4e2a769d9d00a34c01b Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 17:37:53 -0400
Subject: [PATCH 66/86] fix out of date comment

---
 src/viam/sdk/tests/test_utils.hpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/viam/sdk/tests/test_utils.hpp b/src/viam/sdk/tests/test_utils.hpp
index 3184588ab..a06de3a30 100644
--- a/src/viam/sdk/tests/test_utils.hpp
+++ b/src/viam/sdk/tests/test_utils.hpp
@@ -23,8 +23,7 @@ BOOST_TEST_GLOBAL_FIXTURE(GlobalFixture);
 
 // Buffer output filter to test console logging.
 // In practice this is a pain and makes it hard to inspect Boost.Test output,
-// so rather than using it as a test fixture we manually instantiate it in an optional.
-// Further log testing is done in the complex module example tests.
+// so rather than using it as a test fixture we manually instantiate it and call release when done.
 // https://stackoverflow.com/a/5405268
 struct cout_redirect {
     cout_redirect() : old(std::cout.rdbuf(os.rdbuf())) {}

From d681f6f98c6974bd163a3249253f5bf36c8c1857 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 17:48:43 -0400
Subject: [PATCH 67/86] rename to log manager and global logger

---
 src/viam/sdk/common/instance.cpp         |  2 +-
 src/viam/sdk/common/instance.hpp         |  2 +-
 src/viam/sdk/common/private/instance.hpp |  2 +-
 src/viam/sdk/log/logger.cpp              | 18 +++++++--------
 src/viam/sdk/log/logger.hpp              | 29 ++++++++++++------------
 src/viam/sdk/module/service.cpp          |  2 +-
 src/viam/sdk/resource/resource.cpp       |  2 +-
 src/viam/sdk/robot/client.cpp            |  4 ++--
 src/viam/sdk/tests/test_log.cpp          |  2 +-
 9 files changed, 32 insertions(+), 31 deletions(-)

diff --git a/src/viam/sdk/common/instance.cpp b/src/viam/sdk/common/instance.cpp
index 349ae0107..06baf29f4 100644
--- a/src/viam/sdk/common/instance.cpp
+++ b/src/viam/sdk/common/instance.cpp
@@ -27,7 +27,7 @@ Instance::Instance() {
 
     impl_ = std::make_unique<Instance::Impl>();
     impl_->registry.initialize();
-    impl_->logger.init_logging();
+    impl_->log_mgr.init_logging();
 }
 
 Instance::~Instance() {
diff --git a/src/viam/sdk/common/instance.hpp b/src/viam/sdk/common/instance.hpp
index 47e3369e7..de10ae896 100644
--- a/src/viam/sdk/common/instance.hpp
+++ b/src/viam/sdk/common/instance.hpp
@@ -28,7 +28,7 @@ class Instance {
 
    private:
     friend class Registry;
-    friend class Logger;
+    friend class LogManager;
 
     struct Impl;
     std::unique_ptr<Impl> impl_;
diff --git a/src/viam/sdk/common/private/instance.hpp b/src/viam/sdk/common/private/instance.hpp
index 622cfe354..0ffe4dc13 100644
--- a/src/viam/sdk/common/private/instance.hpp
+++ b/src/viam/sdk/common/private/instance.hpp
@@ -9,7 +9,7 @@ namespace sdk {
 
 struct Instance::Impl {
     Registry registry;
-    Logger logger;
+    LogManager log_mgr;
 };
 
 }  // namespace sdk
diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logger.cpp
index a29716a4f..3ba95a1af 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logger.cpp
@@ -67,7 +67,7 @@ const char* global_resource_name() {
     return "Viam C++ SDK";
 }
 
-bool Logger::Filter::operator()(const boost::log::attribute_value_set& attrs) const {
+bool LogManager::Filter::operator()(const boost::log::attribute_value_set& attrs) const {
     auto sev = attrs[attr_sev_type{}];
     if (!sev) {
         return false;
@@ -84,31 +84,31 @@ bool Logger::Filter::operator()(const boost::log::attribute_value_set& attrs) co
     return *sev >= parent->global_level_;
 }
 
-Logger& Logger::get() {
-    static Logger& result = Instance::current(Instance::Creation::open_existing).impl_->logger;
+LogManager& LogManager::get() {
+    static LogManager& result = Instance::current(Instance::Creation::open_existing).impl_->log_mgr;
 
     return result;
 }
 
-LogSource& Logger::logger() {
+LogSource& LogManager::global_logger() {
     return sdk_logger_;
 }
 
-void Logger::set_global_log_level(log_level lvl) {
+void LogManager::set_global_log_level(log_level lvl) {
     global_level_ = lvl;
 }
 
-void Logger::set_global_log_level(int argc, char** argv) {
+void LogManager::set_global_log_level(int argc, char** argv) {
     if (argc >= 3 && strcmp(argv[2], "--log-level=debug") == 0) {
         set_global_log_level(log_level::debug);
     }
 }
 
-void Logger::set_resource_log_level(const std::string& resource, log_level lvl) {
+void LogManager::set_resource_log_level(const std::string& resource, log_level lvl) {
     resource_levels_[resource] = lvl;
 }
 
-void Logger::init_logging() {
+void LogManager::init_logging() {
     sdk_logger_.channel(global_resource_name());
     boost::log::core::get()->add_global_attribute("TimeStamp",
                                                   boost::log::attributes::local_clock());
@@ -134,7 +134,7 @@ void Logger::init_logging() {
     VIAM_LOG(debug) << "Initialized console logging";
 }
 
-void Logger::disable_console_logging() {
+void LogManager::disable_console_logging() {
     VIAM_LOG(debug) << "Disabling console logging";
     boost::log::core::get()->remove_sink(console_sink_);
 }
diff --git a/src/viam/sdk/log/logger.hpp b/src/viam/sdk/log/logger.hpp
index 7071f3197..c2d592566 100644
--- a/src/viam/sdk/log/logger.hpp
+++ b/src/viam/sdk/log/logger.hpp
@@ -50,16 +50,16 @@ using LogSource = boost::log::sources::severity_channel_logger_mt<log_level>;
 /// @ingroup Log
 const char* global_resource_name();
 
-/// @class Logger logger.hpp "log/logger.hpp"
+/// @class LogManager logger.hpp "log/logger.hpp"
 /// @brief Manages the logging infrastructure in the SDDK.
 /// @ingroup Log
 ///
 /// Handles initialization and bookkeeping for logging infrastructure in the C++ SDK. This includes
 /// console logging, if active, and both global and resource-level log filtering.
-class Logger {
+class LogManager {
    public:
     struct Filter {
-        const Logger* parent;
+        const LogManager* parent;
 
         bool operator()(const boost::log::attribute_value_set&) const;
     };
@@ -67,7 +67,7 @@ class Logger {
     /// @brief Returns the unique logger instance.
     ///
     /// This is the only way to access the logger.
-    static Logger& get();
+    static LogManager& get();
 
     /// @brief Set the global logger severity.
     void set_global_log_level(log_level);
@@ -82,9 +82,10 @@ class Logger {
 
     /// @brief Set the logger severity for a resource.
     ///
-    /// Logger can maintain separate severity levels for individual resources. For example, you may
-    /// want to only report "error" messages for global logs and logs for one component of a modular
-    /// resource, but enable verbose logging for a new component that is still being debugged.
+    /// LogManager can maintain separate severity levels for individual resources. For example, you
+    /// may want to only report "error" messages for global logs and logs for one component of a
+    /// modular resource, but enable verbose logging for a new component that is still being
+    /// debugged.
     /// @see @ref Resource, which has a set_log_level method which automatically provides its own
     /// resource name.
     void set_resource_log_level(const std::string& resource, log_level);
@@ -92,18 +93,18 @@ class Logger {
     /// @brief Return the SDK global log source.
     ///
     /// Users should prefer to log messages using the logging macros below.
-    LogSource& logger();
+    LogSource& global_logger();
 
    private:
     friend class RobotClient;
     friend class Instance;
-    Logger() = default;
+    LogManager() = default;
 
-    Logger(const Logger&) = delete;
-    Logger(Logger&&) = delete;
+    LogManager(const LogManager&) = delete;
+    LogManager(LogManager&&) = delete;
 
-    Logger& operator=(const Logger&) = delete;
-    Logger& operator=(Logger&&) = delete;
+    LogManager& operator=(const LogManager&) = delete;
+    LogManager& operator=(LogManager&&) = delete;
 
     void init_logging();
     void disable_console_logging();
@@ -143,7 +144,7 @@ BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_time,
 /// @ingroup Log
 ///
 /// Use this macro to generate log messages pertaining to the SDK at large.
-#define VIAM_LOG(level) VIAM_LOG_IMPL(::viam::sdk::Logger::get().logger(), level)
+#define VIAM_LOG(level) VIAM_LOG_IMPL(::viam::sdk::LogManager::get().global_logger(), level)
 
 /// @brief Log macro for resource-level logs.
 /// @ingroup Log
diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp
index 3c3b31e04..a79cb99b1 100644
--- a/src/viam/sdk/module/service.cpp
+++ b/src/viam/sdk/module/service.cpp
@@ -237,7 +237,7 @@ ModuleService::ModuleService(int argc,
           }
           return argv[1];
       }()) {
-    Logger::get().set_global_log_level(argc, argv);
+    LogManager::get().set_global_log_level(argc, argv);
 
     for (auto&& mr : registrations) {
         Registry::get().register_model(mr);
diff --git a/src/viam/sdk/resource/resource.cpp b/src/viam/sdk/resource/resource.cpp
index 3b46fb00d..3408f0104 100644
--- a/src/viam/sdk/resource/resource.cpp
+++ b/src/viam/sdk/resource/resource.cpp
@@ -30,7 +30,7 @@ Name Resource::get_resource_name() const {
 }
 
 void Resource::set_log_level(log_level ll) const {
-    Logger::get().set_resource_log_level(name_, ll);
+    LogManager::get().set_resource_log_level(name_, ll);
 }
 
 }  // namespace sdk
diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp
index 549583ebf..162e3ee1b 100644
--- a/src/viam/sdk/robot/client.cpp
+++ b/src/viam/sdk/robot/client.cpp
@@ -101,9 +101,9 @@ void RobotClient::connect_logging() {
     auto& sink = impl_->log_sink;
     if (!sink) {
         sink = sdk::impl::LogBackend::create(this);
-        sink->set_filter(Logger::Filter{&Logger::get()});
+        sink->set_filter(LogManager::Filter{&LogManager::get()});
 
-        Logger::get().disable_console_logging();
+        LogManager::get().disable_console_logging();
         boost::log::core::get()->add_sink(sink);
     }
 }
diff --git a/src/viam/sdk/tests/test_log.cpp b/src/viam/sdk/tests/test_log.cpp
index c5c5bc265..4533ed987 100644
--- a/src/viam/sdk/tests/test_log.cpp
+++ b/src/viam/sdk/tests/test_log.cpp
@@ -36,7 +36,7 @@ BOOST_AUTO_TEST_CASE(test_global_filter) {
     VIAM_LOG(error) << "error1";
     VIAM_LOG(trace) << "trace1";  // not logged
 
-    auto& logger = sdk::Logger::get();
+    auto& logger = sdk::LogManager::get();
 
     using ll = sdk::log_level;
 

From 3b8d7dcac0db2b1a3d99d399d9b6aae217c85727 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 17 Mar 2025 17:52:33 -0400
Subject: [PATCH 68/86] rename files to logging

---
 src/viam/sdk/CMakeLists.txt                  | 4 ++--
 src/viam/sdk/common/client_helper.cpp        | 2 +-
 src/viam/sdk/common/private/instance.hpp     | 2 +-
 src/viam/sdk/config/resource.hpp             | 2 +-
 src/viam/sdk/log/{logger.cpp => logging.cpp} | 2 +-
 src/viam/sdk/log/{logger.hpp => logging.hpp} | 4 ++--
 src/viam/sdk/resource/resource.hpp           | 2 +-
 src/viam/sdk/tests/test_log.cpp              | 2 +-
 8 files changed, 10 insertions(+), 10 deletions(-)
 rename src/viam/sdk/log/{logger.cpp => logging.cpp} (99%)
 rename src/viam/sdk/log/{logger.hpp => logging.hpp} (98%)

diff --git a/src/viam/sdk/CMakeLists.txt b/src/viam/sdk/CMakeLists.txt
index efbd6d2fd..b758e97ed 100644
--- a/src/viam/sdk/CMakeLists.txt
+++ b/src/viam/sdk/CMakeLists.txt
@@ -103,7 +103,7 @@ target_sources(viamsdk
     components/sensor.cpp
     components/servo.cpp
     config/resource.cpp
-    log/logger.cpp
+    log/logging.cpp
     log/private/log_backend.cpp
     module/handler_map.cpp
     module/module.cpp
@@ -172,7 +172,7 @@ target_sources(viamsdk
       ../../viam/sdk/components/sensor.hpp
       ../../viam/sdk/components/servo.hpp
       ../../viam/sdk/config/resource.hpp
-      ../../viam/sdk/log/logger.hpp
+      ../../viam/sdk/log/logging.hpp
       ../../viam/sdk/module/handler_map.hpp
       ../../viam/sdk/module/module.hpp
       ../../viam/sdk/module/service.hpp
diff --git a/src/viam/sdk/common/client_helper.cpp b/src/viam/sdk/common/client_helper.cpp
index 24eeb7db8..dccd6380b 100644
--- a/src/viam/sdk/common/client_helper.cpp
+++ b/src/viam/sdk/common/client_helper.cpp
@@ -6,7 +6,7 @@
 #include <grpcpp/support/status.h>
 
 #include <viam/sdk/common/private/version_metadata.hpp>
-#include <viam/sdk/log/logger.hpp>
+#include <viam/sdk/log/logging.hpp>
 
 namespace viam {
 namespace sdk {
diff --git a/src/viam/sdk/common/private/instance.hpp b/src/viam/sdk/common/private/instance.hpp
index 0ffe4dc13..cb09480d9 100644
--- a/src/viam/sdk/common/private/instance.hpp
+++ b/src/viam/sdk/common/private/instance.hpp
@@ -1,7 +1,7 @@
 #pragma once
 
 #include <viam/sdk/common/instance.hpp>
-#include <viam/sdk/log/logger.hpp>
+#include <viam/sdk/log/logging.hpp>
 #include <viam/sdk/registry/registry.hpp>
 
 namespace viam {
diff --git a/src/viam/sdk/config/resource.hpp b/src/viam/sdk/config/resource.hpp
index 9aa8d46c7..d2531b5ff 100644
--- a/src/viam/sdk/config/resource.hpp
+++ b/src/viam/sdk/config/resource.hpp
@@ -5,7 +5,7 @@
 
 #include <viam/sdk/common/proto_convert.hpp>
 #include <viam/sdk/common/proto_value.hpp>
-#include <viam/sdk/log/logger.hpp>
+#include <viam/sdk/log/logging.hpp>
 #include <viam/sdk/referenceframe/frame.hpp>
 #include <viam/sdk/resource/resource_api.hpp>
 
diff --git a/src/viam/sdk/log/logger.cpp b/src/viam/sdk/log/logging.cpp
similarity index 99%
rename from src/viam/sdk/log/logger.cpp
rename to src/viam/sdk/log/logging.cpp
index 3ba95a1af..9193a5739 100644
--- a/src/viam/sdk/log/logger.cpp
+++ b/src/viam/sdk/log/logging.cpp
@@ -1,4 +1,4 @@
-#include <viam/sdk/log/logger.hpp>
+#include <viam/sdk/log/logging.hpp>
 
 #include <iostream>
 
diff --git a/src/viam/sdk/log/logger.hpp b/src/viam/sdk/log/logging.hpp
similarity index 98%
rename from src/viam/sdk/log/logger.hpp
rename to src/viam/sdk/log/logging.hpp
index c2d592566..00fc03c15 100644
--- a/src/viam/sdk/log/logger.hpp
+++ b/src/viam/sdk/log/logging.hpp
@@ -1,4 +1,4 @@
-/// @file log/logger.hpp
+/// @file log/logging.hpp
 ///
 /// @brief Defines logging infrastructure
 #pragma once
@@ -50,7 +50,7 @@ using LogSource = boost::log::sources::severity_channel_logger_mt<log_level>;
 /// @ingroup Log
 const char* global_resource_name();
 
-/// @class LogManager logger.hpp "log/logger.hpp"
+/// @class LogManager logging.hpp "log/logging.hpp"
 /// @brief Manages the logging infrastructure in the SDDK.
 /// @ingroup Log
 ///
diff --git a/src/viam/sdk/resource/resource.hpp b/src/viam/sdk/resource/resource.hpp
index afd3f87d5..b6f31359b 100644
--- a/src/viam/sdk/resource/resource.hpp
+++ b/src/viam/sdk/resource/resource.hpp
@@ -4,7 +4,7 @@
 
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/config/resource.hpp>
-#include <viam/sdk/log/logger.hpp>
+#include <viam/sdk/log/logging.hpp>
 #include <viam/sdk/resource/resource_api.hpp>
 
 namespace viam {
diff --git a/src/viam/sdk/tests/test_log.cpp b/src/viam/sdk/tests/test_log.cpp
index 4533ed987..a25ea536e 100644
--- a/src/viam/sdk/tests/test_log.cpp
+++ b/src/viam/sdk/tests/test_log.cpp
@@ -1,7 +1,7 @@
 #define BOOST_TEST_MODULE test module test_log
 #include <boost/test/included/unit_test.hpp>
 
-#include <viam/sdk/log/logger.hpp>
+#include <viam/sdk/log/logging.hpp>
 
 #include <iostream>
 #include <sstream>

From 82eb40362c652a3ca57e5b3751507b54a768ad79 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 18 Mar 2025 11:58:38 -0400
Subject: [PATCH 69/86] use has_log_configuration

---
 src/viam/sdk/config/resource.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/viam/sdk/config/resource.cpp b/src/viam/sdk/config/resource.cpp
index 0e02d5b27..8f19dadef 100644
--- a/src/viam/sdk/config/resource.cpp
+++ b/src/viam/sdk/config/resource.cpp
@@ -155,7 +155,6 @@ void to_proto_impl<ResourceConfig>::operator()(const ResourceConfig& self,
 
 ResourceConfig from_proto_impl<app::v1::ComponentConfig>::operator()(
     const app::v1::ComponentConfig* proto) const {
-    const std::string& level_str = proto->log_configuration().level();
     return ResourceConfig(proto->type(),
                           proto->name(),
                           proto->namespace_(),
@@ -163,7 +162,9 @@ ResourceConfig from_proto_impl<app::v1::ComponentConfig>::operator()(
                           proto->api(),
                           Model::from_str(proto->model()),
                           proto->has_frame() ? from_proto(proto->frame()) : LinkConfig{},
-                          level_str.empty() ? log_level::info : level_from_string(level_str));
+                          proto->has_log_configuration()
+                              ? level_from_string(proto->log_configuration().level())
+                              : log_level::info);
 }
 
 std::vector<ResourceConfig>

From ad079c8f056127ab028e193de0aaf1a2cf82d97c Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 18 Mar 2025 12:06:32 -0400
Subject: [PATCH 70/86] unit test trim filename

---
 src/viam/sdk/tests/test_log.cpp | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/viam/sdk/tests/test_log.cpp b/src/viam/sdk/tests/test_log.cpp
index a25ea536e..ad1c69f3c 100644
--- a/src/viam/sdk/tests/test_log.cpp
+++ b/src/viam/sdk/tests/test_log.cpp
@@ -123,5 +123,15 @@ BOOST_AUTO_TEST_CASE(test_resource_filter) {
     BOOST_CHECK(errLogs.back().find("sensor error") != std::string::npos);
 }
 
+BOOST_AUTO_TEST_CASE(filename_trim) {
+    using namespace log_detail;
+
+    BOOST_CHECK(trim_filename("") == "");
+    BOOST_CHECK(trim_filename("no_delim") == "no_delim");
+    BOOST_CHECK(trim_filename("one/slash.cpp") == "one/slash.cpp");
+    BOOST_CHECK(trim_filename("a/full/path.cpp") == "full/path.cpp");
+    BOOST_CHECK(trim_filename("a/b/c/d.cpp") == "c/d.cpp");
+}
+
 }  // namespace sdktests
 }  // namespace viam

From 6b83aedab2ef065e21075b0e6d4cd7bfe099022a Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Tue, 18 Mar 2025 17:16:47 -0400
Subject: [PATCH 71/86] handle fallback console logging for grpc log failures

---
 src/viam/sdk/log/logging.cpp             | 14 +++++++++++++-
 src/viam/sdk/log/private/log_backend.hpp |  5 +++++
 src/viam/sdk/robot/client.cpp            | 18 ++++++++++++++++--
 3 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/src/viam/sdk/log/logging.cpp b/src/viam/sdk/log/logging.cpp
index 9193a5739..839fed7d0 100644
--- a/src/viam/sdk/log/logging.cpp
+++ b/src/viam/sdk/log/logging.cpp
@@ -12,6 +12,7 @@
 
 #include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/private/instance.hpp>
+#include <viam/sdk/log/private/log_backend.hpp>
 
 namespace viam {
 namespace sdk {
@@ -136,7 +137,18 @@ void LogManager::init_logging() {
 
 void LogManager::disable_console_logging() {
     VIAM_LOG(debug) << "Disabling console logging";
-    boost::log::core::get()->remove_sink(console_sink_);
+
+    // Set a filter which ignores all console logs unless they contain a console force flag which is
+    // set to true.
+    console_sink_->set_filter(
+        [filter = Filter{this}](const boost::log::attribute_value_set& attrs) {
+            auto force = attrs[impl::attr_console_force_type{}];
+            if (force && *force) {
+                return filter(attrs);
+            }
+
+            return false;
+        });
 }
 
 namespace log_detail {
diff --git a/src/viam/sdk/log/private/log_backend.hpp b/src/viam/sdk/log/private/log_backend.hpp
index a3d2207c0..73e26fc3f 100644
--- a/src/viam/sdk/log/private/log_backend.hpp
+++ b/src/viam/sdk/log/private/log_backend.hpp
@@ -25,6 +25,11 @@ struct LogBackend : boost::log::sinks::basic_sink_backend<boost::log::sinks::syn
     RobotClient* parent;
 };
 
+// TBD if we want to expose this to users, but for now it is an implementation detail.
+// Disabling console logging is implemented by changing the filter on the console sink to reject all
+// messages unless they have a "force" flag set to true.
+BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_console_force, "force", bool);
+
 }  // namespace impl
 }  // namespace sdk
 }  // namespace viam
diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp
index 162e3ee1b..147300502 100644
--- a/src/viam/sdk/robot/client.cpp
+++ b/src/viam/sdk/robot/client.cpp
@@ -101,7 +101,15 @@ void RobotClient::connect_logging() {
     auto& sink = impl_->log_sink;
     if (!sink) {
         sink = sdk::impl::LogBackend::create(this);
-        sink->set_filter(LogManager::Filter{&LogManager::get()});
+        sink->set_filter([filter = LogManager::Filter{&LogManager::get()}](
+                             const boost::log::attribute_value_set& attrs) {
+            auto force = attrs[sdk::impl::attr_console_force_type{}];
+            if (force && *force) {
+                return false;
+            }
+
+            return filter(attrs);
+        });
 
         LogManager::get().disable_console_logging();
         boost::log::core::get()->add_sink(sink);
@@ -270,7 +278,13 @@ void RobotClient::log(const std::string& name,
     robot::v1::LogResponse resp;
     ClientContext ctx;
     const auto response = impl_->stub_->Log(ctx, req, &resp);
-    (void)response;
+    if (is_error_response(response)) {
+        // Manually override to force this to get logged to console so we don't set off an infinite
+        // loop
+        VIAM_LOG(error) << boost::log::add_value(sdk::impl::attr_console_force_type{}, true)
+                        << "Error sending log message over grpc: " << response.error_message()
+                        << response.error_details();
+    }
 }
 
 std::shared_ptr<RobotClient> RobotClient::with_channel(std::shared_ptr<ViamChannel> channel,

From 4eb8643121400d4efa567424981d5e87d65a429a Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 19 Mar 2025 13:07:00 -0400
Subject: [PATCH 72/86] add logging to simple module example

---
 src/viam/examples/modules/simple/main.cpp | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/viam/examples/modules/simple/main.cpp b/src/viam/examples/modules/simple/main.cpp
index e85c2fe3a..10f7f667c 100644
--- a/src/viam/examples/modules/simple/main.cpp
+++ b/src/viam/examples/modules/simple/main.cpp
@@ -7,6 +7,7 @@
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/components/sensor.hpp>
 #include <viam/sdk/config/resource.hpp>
+#include <viam/sdk/log/logging.hpp>
 #include <viam/sdk/module/service.hpp>
 #include <viam/sdk/registry/registry.hpp>
 #include <viam/sdk/resource/reconfigurable.hpp>
@@ -65,7 +66,9 @@ void MySensor::reconfigure(const Dependencies&, const ResourceConfig& cfg) {
 
 ProtoStruct MySensor::do_command(const ProtoStruct& command) {
     for (const auto& entry : command) {
-        std::cout << "Command entry " << entry.first;
+        // The VIAM_RESOURCE_LOG macro will associate log messages to the current resource and can
+        // be filtered upon
+        VIAM_RESOURCE_LOG(info) << "Command entry " << entry.first;
     }
 
     return command;
@@ -80,6 +83,9 @@ int main(int argc, char** argv) try {
     // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
     Instance inst;
 
+    // Write general log statements with the VIAM_LOG macro
+    VIAM_LOG(info) << "Starting up simple sensor module";
+
     Model mysensor_model("viam", "sensor", "mysensor");
 
     std::shared_ptr<ModelRegistration> mr = std::make_shared<ModelRegistration>(

From 418f882aabd7ec0a6ee55d922a8b03f9d25325b1 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 19 Mar 2025 13:12:29 -0400
Subject: [PATCH 73/86] add logging to complex module example

---
 src/viam/examples/modules/complex/base/impl.cpp  | 5 +++--
 src/viam/examples/modules/complex/gizmo/impl.cpp | 1 -
 src/viam/examples/modules/complex/main.cpp       | 4 ++++
 3 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/viam/examples/modules/complex/base/impl.cpp b/src/viam/examples/modules/complex/base/impl.cpp
index 14ed4bb4d..0672076a7 100644
--- a/src/viam/examples/modules/complex/base/impl.cpp
+++ b/src/viam/examples/modules/complex/base/impl.cpp
@@ -2,7 +2,6 @@
 
 #include <exception>
 #include <fstream>
-#include <iostream>
 #include <sstream>
 
 #include <grpcpp/support/status.h>
@@ -10,6 +9,7 @@
 #include <viam/sdk/components/base.hpp>
 #include <viam/sdk/components/component.hpp>
 #include <viam/sdk/config/resource.hpp>
+#include <viam/sdk/log/logging.hpp>
 #include <viam/sdk/resource/resource.hpp>
 
 using namespace viam::sdk;
@@ -106,7 +106,8 @@ void MyBase::set_power(const Vector3& linear, const Vector3& angular, const Prot
 }
 
 ProtoStruct MyBase::do_command(const ProtoStruct& command) {
-    std::cout << "Received DoCommand request for MyBase " << Resource::name() << std::endl;
+    // The VIAM_RESOURCE_LOG macro will associate log messages to the current resource
+    VIAM_RESOURCE_LOG(info) << "Received DoCommand request";
     return command;
 }
 
diff --git a/src/viam/examples/modules/complex/gizmo/impl.cpp b/src/viam/examples/modules/complex/gizmo/impl.cpp
index adbf8675a..93a21fb18 100644
--- a/src/viam/examples/modules/complex/gizmo/impl.cpp
+++ b/src/viam/examples/modules/complex/gizmo/impl.cpp
@@ -1,7 +1,6 @@
 #include "impl.hpp"
 
 #include <fstream>
-#include <iostream>
 #include <sstream>
 #include <vector>
 
diff --git a/src/viam/examples/modules/complex/main.cpp b/src/viam/examples/modules/complex/main.cpp
index cb8fab385..46042597a 100644
--- a/src/viam/examples/modules/complex/main.cpp
+++ b/src/viam/examples/modules/complex/main.cpp
@@ -8,6 +8,7 @@
 #include <viam/sdk/components/base.hpp>
 #include <viam/sdk/components/component.hpp>
 #include <viam/sdk/config/resource.hpp>
+#include <viam/sdk/log/logging.hpp>
 #include <viam/sdk/module/module.hpp>
 #include <viam/sdk/module/service.hpp>
 #include <viam/sdk/registry/registry.hpp>
@@ -47,6 +48,9 @@ int main(int argc, char** argv) {
         [](Dependencies deps, ResourceConfig cfg) { return std::make_unique<MyGizmo>(deps, cfg); },
         MyGizmo::validate);
 
+    // Write general log statements with the VIAM_LOG macro
+    VIAM_LOG(info) << "Registered mybase and mygizmo";
+
     Model mysummation_model("viam", "summation", "mysummation");
 
     std::shared_ptr<ModelRegistration> mysummation_mr = std::make_shared<ModelRegistration>(

From 40b540025479b6e706223071fbddbd104dea0ce1 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 20 Mar 2025 11:48:34 -0400
Subject: [PATCH 74/86] use VIAM_SDK macro prefix

---
 src/viam/examples/modules/complex/main.cpp |  4 +--
 src/viam/examples/modules/simple/main.cpp  |  4 +--
 src/viam/sdk/common/client_helper.cpp      |  4 +--
 src/viam/sdk/log/logging.cpp               |  6 ++--
 src/viam/sdk/log/logging.hpp               |  6 ++--
 src/viam/sdk/module/handler_map.cpp        |  2 +-
 src/viam/sdk/module/service.cpp            | 12 ++++----
 src/viam/sdk/resource/resource_manager.cpp |  9 +++---
 src/viam/sdk/robot/client.cpp              | 34 +++++++++++-----------
 src/viam/sdk/tests/test_log.cpp            | 20 ++++++-------
 10 files changed, 51 insertions(+), 50 deletions(-)

diff --git a/src/viam/examples/modules/complex/main.cpp b/src/viam/examples/modules/complex/main.cpp
index 46042597a..91dbe3a2c 100644
--- a/src/viam/examples/modules/complex/main.cpp
+++ b/src/viam/examples/modules/complex/main.cpp
@@ -48,8 +48,8 @@ int main(int argc, char** argv) {
         [](Dependencies deps, ResourceConfig cfg) { return std::make_unique<MyGizmo>(deps, cfg); },
         MyGizmo::validate);
 
-    // Write general log statements with the VIAM_LOG macro
-    VIAM_LOG(info) << "Registered mybase and mygizmo";
+    // Write general log statements with the VIAM_SDK_LOG macro
+    VIAM_SDK_LOG(info) << "Registered mybase and mygizmo";
 
     Model mysummation_model("viam", "summation", "mysummation");
 
diff --git a/src/viam/examples/modules/simple/main.cpp b/src/viam/examples/modules/simple/main.cpp
index 10f7f667c..10f791d76 100644
--- a/src/viam/examples/modules/simple/main.cpp
+++ b/src/viam/examples/modules/simple/main.cpp
@@ -83,8 +83,8 @@ int main(int argc, char** argv) try {
     // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
     Instance inst;
 
-    // Write general log statements with the VIAM_LOG macro
-    VIAM_LOG(info) << "Starting up simple sensor module";
+    // Write general log statements with the VIAM_SDK_LOG macro
+    VIAM_SDK_LOG(info) << "Starting up simple sensor module";
 
     Model mysensor_model("viam", "sensor", "mysensor");
 
diff --git a/src/viam/sdk/common/client_helper.cpp b/src/viam/sdk/common/client_helper.cpp
index dccd6380b..a6853a748 100644
--- a/src/viam/sdk/common/client_helper.cpp
+++ b/src/viam/sdk/common/client_helper.cpp
@@ -14,8 +14,8 @@ namespace sdk {
 namespace client_helper_details {
 
 [[noreturn]] void errorHandlerReturnedUnexpectedly(const ::grpc::Status* status) noexcept {
-    VIAM_LOG(fatal) << "ClientHelper error handler callback returned instead of throwing: "
-                    << status->error_message() << '(' << status->error_details() << ')';
+    VIAM_SDK_LOG(fatal) << "ClientHelper error handler callback returned instead of throwing: "
+                        << status->error_message() << '(' << status->error_details() << ')';
     std::abort();
 }
 
diff --git a/src/viam/sdk/log/logging.cpp b/src/viam/sdk/log/logging.cpp
index 839fed7d0..024a622ca 100644
--- a/src/viam/sdk/log/logging.cpp
+++ b/src/viam/sdk/log/logging.cpp
@@ -55,7 +55,7 @@ log_level level_from_string(std::string level) {
         return ll::fatal;
     }
 
-    VIAM_LOG(warn) << "Returning unknown log level `" << level << "` as info";
+    VIAM_SDK_LOG(warn) << "Returning unknown log level `" << level << "` as info";
     return ll::info;
 }
 
@@ -132,11 +132,11 @@ void LogManager::init_logging() {
     console_sink_->set_formatter(fmt);
 
     boost::log::core::get()->add_sink(console_sink_);
-    VIAM_LOG(debug) << "Initialized console logging";
+    VIAM_SDK_LOG(debug) << "Initialized console logging";
 }
 
 void LogManager::disable_console_logging() {
-    VIAM_LOG(debug) << "Disabling console logging";
+    VIAM_SDK_LOG(debug) << "Disabling console logging";
 
     // Set a filter which ignores all console logs unless they contain a console force flag which is
     // set to true.
diff --git a/src/viam/sdk/log/logging.hpp b/src/viam/sdk/log/logging.hpp
index 00fc03c15..93bdaa044 100644
--- a/src/viam/sdk/log/logging.hpp
+++ b/src/viam/sdk/log/logging.hpp
@@ -134,7 +134,7 @@ BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_time,
                                  "TimeStamp",
                                  boost::log::attributes::local_clock::value_type);
 
-#define VIAM_LOG_IMPL(lg, level)                                                     \
+#define VIAM_SDK_LOG_IMPL(lg, level)                                                 \
     BOOST_LOG_SEV((lg), ::viam::sdk::log_level::level)                               \
         << ::boost::log::add_value(::viam::sdk::attr_file_type{},                    \
                                    ::viam::sdk::log_detail::trim_filename(__FILE__)) \
@@ -144,7 +144,7 @@ BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_time,
 /// @ingroup Log
 ///
 /// Use this macro to generate log messages pertaining to the SDK at large.
-#define VIAM_LOG(level) VIAM_LOG_IMPL(::viam::sdk::LogManager::get().global_logger(), level)
+#define VIAM_SDK_LOG(level) VIAM_SDK_LOG_IMPL(::viam::sdk::LogManager::get().global_logger(), level)
 
 /// @brief Log macro for resource-level logs.
 /// @ingroup Log
@@ -152,7 +152,7 @@ BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE(attr_time,
 /// This macro can only be called from the definition of a member function of a class inheriting
 /// @ref Resource. It will log messages to the log source of that specific resource, allowing
 /// resource-level log filtering.
-#define VIAM_RESOURCE_LOG(level) VIAM_LOG_IMPL(this->logger_, level)
+#define VIAM_RESOURCE_LOG(level) VIAM_SDK_LOG_IMPL(this->logger_, level)
 
 }  // namespace sdk
 }  // namespace viam
diff --git a/src/viam/sdk/module/handler_map.cpp b/src/viam/sdk/module/handler_map.cpp
index 14f5d656d..44a902e66 100644
--- a/src/viam/sdk/module/handler_map.cpp
+++ b/src/viam/sdk/module/handler_map.cpp
@@ -64,7 +64,7 @@ HandlerMap_ from_proto_impl<module::v1::HandlerMap>::operator()(
             try {
                 hm.add_model(Model::from_str(mod), handle);
             } catch (const std::exception& ex) {
-                VIAM_LOG(error) << "Error " << ex.what() << " processing model " + mod;
+                VIAM_SDK_LOG(error) << "Error " << ex.what() << " processing model " + mod;
             }
         }
     }
diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp
index a79cb99b1..c22e21fe9 100644
--- a/src/viam/sdk/module/service.cpp
+++ b/src/viam/sdk/module/service.cpp
@@ -109,7 +109,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         try {
             Stoppable::stop_if_stoppable(res);
         } catch (const std::exception& err) {
-            VIAM_LOG(error) << "unable to stop resource: " << err.what();
+            VIAM_SDK_LOG(error) << "unable to stop resource: " << err.what();
         }
 
         const std::shared_ptr<const ModelRegistration> reg =
@@ -170,7 +170,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service {
         try {
             Stoppable::stop_if_stoppable(res);
         } catch (const std::exception& err) {
-            VIAM_LOG(error) << "unable to stop resource: " << err.what();
+            VIAM_SDK_LOG(error) << "unable to stop resource: " << err.what();
         }
 
         manager->remove(name);
@@ -247,14 +247,14 @@ ModuleService::ModuleService(int argc,
 
 ModuleService::~ModuleService() {
     // TODO(RSDK-5509): Run registered cleanup functions here.
-    VIAM_LOG(info) << "Shutting down gracefully.";
+    VIAM_SDK_LOG(info) << "Shutting down gracefully.";
     server_->shutdown();
 
     if (parent_) {
         try {
             parent_->close();
         } catch (const std::exception& exc) {
-            VIAM_LOG(error) << exc.what();
+            VIAM_SDK_LOG(error) << exc.what();
         }
     }
 }
@@ -272,8 +272,8 @@ void ModuleService::serve() {
     module_->set_ready();
     server_->start();
 
-    VIAM_LOG(info) << "Module listening on " << module_->addr();
-    VIAM_LOG(info) << "Module handles the following API/model pairs:\n" << module_->handles();
+    VIAM_SDK_LOG(info) << "Module listening on " << module_->addr();
+    VIAM_SDK_LOG(info) << "Module handles the following API/model pairs:\n" << module_->handles();
 
     signal_manager_.wait();
 }
diff --git a/src/viam/sdk/resource/resource_manager.cpp b/src/viam/sdk/resource/resource_manager.cpp
index c68fc0aac..40dc64ece 100644
--- a/src/viam/sdk/resource/resource_manager.cpp
+++ b/src/viam/sdk/resource/resource_manager.cpp
@@ -50,7 +50,7 @@ void ResourceManager::replace_all(
         try {
             do_add(resource.first, resource.second);
         } catch (std::exception& exc) {
-            VIAM_LOG(error) << "Error replacing all resources" << exc.what();
+            VIAM_SDK_LOG(error) << "Error replacing all resources" << exc.what();
             return;
         }
     }
@@ -95,7 +95,7 @@ void ResourceManager::add(const Name& name, std::shared_ptr<Resource> resource)
     try {
         do_add(name, std::move(resource));
     } catch (std::exception& exc) {
-        VIAM_LOG(error) << "Error adding resource to subtype service: " << exc.what();
+        VIAM_SDK_LOG(error) << "Error adding resource to subtype service: " << exc.what();
     }
 };
 
@@ -132,7 +132,7 @@ void ResourceManager::remove(const Name& name) {
     try {
         do_remove(name);
     } catch (std::exception& exc) {
-        VIAM_LOG(error) << "unable to remove resource: " << exc.what();
+        VIAM_SDK_LOG(error) << "unable to remove resource: " << exc.what();
     };
 };
 
@@ -142,7 +142,8 @@ void ResourceManager::replace_one(const Name& name, std::shared_ptr<Resource> re
         do_remove(name);
         do_add(name, std::move(resource));
     } catch (std::exception& exc) {
-        VIAM_LOG(error) << "failed to replace resource " << name.to_string() << ": " << exc.what();
+        VIAM_SDK_LOG(error) << "failed to replace resource " << name.to_string() << ": "
+                            << exc.what();
     }
 }
 
diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp
index 147300502..222870003 100644
--- a/src/viam/sdk/robot/client.cpp
+++ b/src/viam/sdk/robot/client.cpp
@@ -121,9 +121,9 @@ RobotClient::~RobotClient() {
         try {
             this->close();
         } catch (const std::exception& e) {
-            VIAM_LOG(error) << "Received err while closing RobotClient: " << e.what();
+            VIAM_SDK_LOG(error) << "Received err while closing RobotClient: " << e.what();
         } catch (...) {
-            VIAM_LOG(error) << "Received unknown err while closing RobotClient";
+            VIAM_SDK_LOG(error) << "Received unknown err while closing RobotClient";
         }
     }
 }
@@ -150,7 +150,7 @@ std::vector<RobotClient::operation> RobotClient::get_operations() {
 
     grpc::Status const response = impl_->stub_->GetOperations(ctx, req, &resp);
     if (is_error_response(response)) {
-        VIAM_LOG(error) << "Error getting operations: " << response.error_message();
+        VIAM_SDK_LOG(error) << "Error getting operations: " << response.error_message();
     }
 
     for (int i = 0; i < resp.operations().size(); ++i) {
@@ -168,7 +168,7 @@ void RobotClient::cancel_operation(std::string id) {
     req.set_id(id);
     const grpc::Status response = impl_->stub_->CancelOperation(ctx, req, &resp);
     if (is_error_response(response)) {
-        VIAM_LOG(error) << "Error canceling operation with id " << id;
+        VIAM_SDK_LOG(error) << "Error canceling operation with id " << id;
     }
 }
 
@@ -181,7 +181,7 @@ void RobotClient::block_for_operation(std::string id) {
 
     const grpc::Status response = impl_->stub_->BlockForOperation(ctx, req, &resp);
     if (is_error_response(response)) {
-        VIAM_LOG(error) << "Error blocking for operation with id " << id;
+        VIAM_SDK_LOG(error) << "Error blocking for operation with id " << id;
     }
 }
 
@@ -192,7 +192,7 @@ void RobotClient::refresh() {
 
     const grpc::Status response = impl_->stub_->ResourceNames(ctx, req, &resp);
     if (is_error_response(response)) {
-        VIAM_LOG(error) << "Error getting resource names: " << response.error_message();
+        VIAM_SDK_LOG(error) << "Error getting resource names: " << response.error_message();
     }
 
     std::unordered_map<Name, std::shared_ptr<Resource>> new_resources;
@@ -216,8 +216,8 @@ void RobotClient::refresh() {
                 const Name name_({name.namespace_(), name.type(), name.subtype()}, "", name.name());
                 new_resources.emplace(name_, rpc_client);
             } catch (const std::exception& exc) {
-                VIAM_LOG(debug) << "Error registering component " << name.subtype() << ": "
-                                << exc.what();
+                VIAM_SDK_LOG(debug)
+                    << "Error registering component " << name.subtype() << ": " << exc.what();
             }
         }
     }
@@ -281,9 +281,9 @@ void RobotClient::log(const std::string& name,
     if (is_error_response(response)) {
         // Manually override to force this to get logged to console so we don't set off an infinite
         // loop
-        VIAM_LOG(error) << boost::log::add_value(sdk::impl::attr_console_force_type{}, true)
-                        << "Error sending log message over grpc: " << response.error_message()
-                        << response.error_details();
+        VIAM_SDK_LOG(error) << boost::log::add_value(sdk::impl::attr_console_force_type{}, true)
+                            << "Error sending log message over grpc: " << response.error_message()
+                            << response.error_details();
     }
 }
 
@@ -339,7 +339,7 @@ std::vector<RobotClient::frame_system_config> RobotClient::get_frame_system_conf
 
     const grpc::Status response = impl_->stub_->FrameSystemConfig(ctx, req, &resp);
     if (is_error_response(response)) {
-        VIAM_LOG(error) << "Error getting frame system config: " << response.error_message();
+        VIAM_SDK_LOG(error) << "Error getting frame system config: " << response.error_message();
     }
 
     const RepeatedPtrField<FrameSystemConfig> configs = resp.frame_system_configs();
@@ -367,7 +367,7 @@ pose_in_frame RobotClient::transform_pose(
 
     const grpc::Status response = impl_->stub_->TransformPose(ctx, req, &resp);
     if (is_error_response(response)) {
-        VIAM_LOG(error) << "Error getting PoseInFrame: " << response.error_message();
+        VIAM_SDK_LOG(error) << "Error getting PoseInFrame: " << response.error_message();
     }
 
     return from_proto(resp.pose());
@@ -402,8 +402,8 @@ void RobotClient::stop_all(const std::unordered_map<Name, ProtoStruct>& extra) {
     }
     const grpc::Status response = impl_->stub_->StopAll(ctx, req, &resp);
     if (is_error_response(response)) {
-        VIAM_LOG(error) << "Error stopping all: " << response.error_message()
-                        << response.error_details();
+        VIAM_SDK_LOG(error) << "Error stopping all: " << response.error_message()
+                            << response.error_details();
     }
 }
 
@@ -431,8 +431,8 @@ RobotClient::status RobotClient::get_machine_status() const {
 
     const grpc::Status response = impl_->stub_->GetMachineStatus(ctx, req, &resp);
     if (is_error_response(response)) {
-        VIAM_LOG(error) << "Error getting machine status: " << response.error_message()
-                        << response.error_details();
+        VIAM_SDK_LOG(error) << "Error getting machine status: " << response.error_message()
+                            << response.error_details();
     }
     switch (resp.state()) {
         case robot::v1::GetMachineStatusResponse_State_STATE_INITIALIZING:
diff --git a/src/viam/sdk/tests/test_log.cpp b/src/viam/sdk/tests/test_log.cpp
index ad1c69f3c..d550cb8d4 100644
--- a/src/viam/sdk/tests/test_log.cpp
+++ b/src/viam/sdk/tests/test_log.cpp
@@ -16,7 +16,7 @@ namespace sdktests {
 BOOST_AUTO_TEST_CASE(test_cout_logging) {
     cout_redirect redirect;
 
-    VIAM_LOG(info) << "log1";
+    VIAM_SDK_LOG(info) << "log1";
 
     using namespace std::string_literals;
 
@@ -32,9 +32,9 @@ BOOST_AUTO_TEST_CASE(test_cout_logging) {
 BOOST_AUTO_TEST_CASE(test_global_filter) {
     cout_redirect redirect;
 
-    VIAM_LOG(info) << "info1";
-    VIAM_LOG(error) << "error1";
-    VIAM_LOG(trace) << "trace1";  // not logged
+    VIAM_SDK_LOG(info) << "info1";
+    VIAM_SDK_LOG(error) << "error1";
+    VIAM_SDK_LOG(trace) << "trace1";  // not logged
 
     auto& logger = sdk::LogManager::get();
 
@@ -42,18 +42,18 @@ BOOST_AUTO_TEST_CASE(test_global_filter) {
 
     logger.set_global_log_level(ll::trace);
 
-    VIAM_LOG(trace) << "trace2";
-    VIAM_LOG(info) << "info2";
+    VIAM_SDK_LOG(trace) << "trace2";
+    VIAM_SDK_LOG(info) << "info2";
 
     logger.set_global_log_level(ll::error);
 
-    VIAM_LOG(info) << "info3";  // not logged
-    VIAM_LOG(error) << "error2";
+    VIAM_SDK_LOG(info) << "info3";  // not logged
+    VIAM_SDK_LOG(error) << "error2";
 
     logger.set_global_log_level(ll::info);
 
-    VIAM_LOG(info) << "info4";
-    VIAM_LOG(trace) << "trace3";  // once again not logged
+    VIAM_SDK_LOG(info) << "info4";
+    VIAM_SDK_LOG(trace) << "trace3";  // once again not logged
 
     const std::string rec = redirect.os.str();
     redirect.release();

From 22d29e3867ed5b2613842b23efbd77ef68a0bb0a Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 20 Mar 2025 14:09:18 -0400
Subject: [PATCH 75/86] disable fail-fast

---
 .github/workflows/test.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index c7a7bf7d5..9f20772b8 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -14,6 +14,7 @@ jobs:
     runs-on: ubuntu-latest
     container: ghcr.io/viamrobotics/cpp-base:bullseye-amd64
     strategy:
+      fail-fast: false
       matrix:
         include:
           - BUILD_SHARED: ON

From 2e9d0159cbc04f5da80a9c60d89d0aff8b39d1a3 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 3 Apr 2025 18:16:44 -0400
Subject: [PATCH 76/86] link boost log and log_setup in public libs

---
 src/viam/sdk/config/viam-cpp-sdk-libviamsdk.pc.in | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/viam/sdk/config/viam-cpp-sdk-libviamsdk.pc.in b/src/viam/sdk/config/viam-cpp-sdk-libviamsdk.pc.in
index 005af602e..a042d0010 100644
--- a/src/viam/sdk/config/viam-cpp-sdk-libviamsdk.pc.in
+++ b/src/viam/sdk/config/viam-cpp-sdk-libviamsdk.pc.in
@@ -7,6 +7,5 @@ Description: @PROJECT_DESCRIPTION@
 URL: @PROJECT_HOMEPAGE_URL@
 Version: @PROJECT_VERSION@
 Requires: grpc++ >= @VIAMCPPSDK_GRPCXX_VERSION@ protobuf >= @VIAMCPPSDK_PROTOBUF_VERSION@ @PROJECT_NAME@-libviamapi >= @PROJECT_VERSION@
-Libs: -L${libdir} -L@Boost_LIBRARY_DIRS@ -lviamsdk -lviam_rust_utils
-Libs.private: -lboost_log-mt
+Libs: -L${libdir} -L@Boost_LIBRARY_DIRS@ -lviamsdk -lviam_rust_utils -lboost_log -lboost_log_setup
 Cflags: -I${includedir} -I@Boost_INCLUDE_DIR@

From b8dcf7c330c4dfe383e26aed30271acf319eac54 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Thu, 3 Apr 2025 18:20:25 -0400
Subject: [PATCH 77/86] set boost log dyn link in test script

---
 etc/docker/tests/run_test.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/etc/docker/tests/run_test.sh b/etc/docker/tests/run_test.sh
index 6ac4702e8..f9418de3a 100755
--- a/etc/docker/tests/run_test.sh
+++ b/etc/docker/tests/run_test.sh
@@ -34,7 +34,7 @@ popd
 if [ ${BUILD_SHARED} = "ON" ]
 then
 	pushd pkg-config
-	PKG_CONFIG_PATH=${INSTALL_DIR}/lib/pkgconfig make all
+	PKG_CONFIG_PATH=${INSTALL_DIR}/lib/pkgconfig CXXFLAGS="-DBOOST_LOG_DYN_LINK" make all
 	run_module
 	popd
 fi

From f6c21ff5a7da751f4c8029c716fa61cb475af955 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 4 Apr 2025 10:43:43 -0400
Subject: [PATCH 78/86] clean up example camera and use logging instead of cout

---
 src/viam/examples/camera/example_camera.cpp | 155 +++++++++-----------
 1 file changed, 69 insertions(+), 86 deletions(-)

diff --git a/src/viam/examples/camera/example_camera.cpp b/src/viam/examples/camera/example_camera.cpp
index d1eb9b80c..5124f0ee9 100644
--- a/src/viam/examples/camera/example_camera.cpp
+++ b/src/viam/examples/camera/example_camera.cpp
@@ -5,97 +5,80 @@
 
 #include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/camera.hpp>
+#include <viam/sdk/log/logging.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
 
-int main() {
-    using std::cerr;
-    using std::cout;
-    using std::endl;
+int main() try {
     namespace vs = ::viam::sdk;
-    try {
-        // Every Viam C++ SDK program must have one and only one Instance object which is created
-        // before
-        // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
-        vs::Instance inst;
-
-        // If you want to connect to a remote robot, this should be the url of the robot
-        // Ex: xxx.xxx.viam.cloud
-        std::string robot_address("localhost:8080");
-        // If you want to connect to a remote robot, you need some authentication secret
-        // You can find this on app.viam.com
-        vs::Credentials credentials("", "");
-
-        vs::DialOptions dial_options;
-
-        // If you have credentials, use this to pass them to the robot
-        // dial_options.credentials = credentials;
-
-        // This is for an example. Care should be taken before exercising this option in production.
-        dial_options.set_allow_insecure_downgrade(
-            (credentials.type().empty() && credentials.payload().empty()));
-
-        // Set the refresh interval of the robot (in seconds) (0 = auto refresh) and the dial
-        // options
-        vs::Options options = vs::Options(1, dial_options);
-
-        std::shared_ptr<vs::RobotClient> robot;
-        try {
-            robot = vs::RobotClient::at_address(robot_address, options);
-            cout << "Successfully connected to the robot" << endl;
-        } catch (const std::exception& e) {
-            cerr << "Failed to connect to the robot. Exiting." << endl;
-            throw;
-        }
-
-        std::vector<vs::Name> resource_names = robot->resource_names();
-
-        cout << "Resources of the robot:" << endl;
-        for (const auto& resource : resource_names) {
-            cout << " - " << resource.name() << " (" << resource.api().resource_subtype() << ")"
-                 << endl;
-        }
-
-        std::string camera_name("camera1");
-
-        cout << "Getting camera: " << camera_name << endl;
-        std::shared_ptr<vs::Camera> camera;
-        try {
-            camera = robot->resource_by_name<vs::Camera>(camera_name);
-        } catch (const std::exception& e) {
-            cerr << "Failed to find " << camera_name << ". Exiting." << endl;
-            throw;
-        }
-        vs::Camera::properties props = camera->get_properties();
-        vs::Camera::intrinsic_parameters intrinsics = props.intrinsic_parameters;
-        cout << "Image dimensions: " << intrinsics.width_px << " x " << intrinsics.height_px
-             << endl;
-
-        std::string output_file("img.png");
-        std::string image_mime_type("image/png");
-
-        cout << "Getting image from camera " << endl;
-        vs::Camera::raw_image img = camera->get_image(image_mime_type);
-        cout << "Got image of mime type: " << img.mime_type << endl;
-
-        cout << "Getting and saving image to " << output_file << endl;
-        std::ofstream fout;
-        fout.open(output_file, std::ios::binary | std::ios::out);
-        if (fout.fail()) {
-            throw std::runtime_error("Failed to open output file " + output_file);
-        }
-        fout.write(reinterpret_cast<char*>(img.bytes.data()), img.bytes.size());
-        fout.close();
-        if (fout.fail()) {
-            throw std::runtime_error("Failed to write and close output file " + output_file);
-        }
-    } catch (const std::exception& ex) {
-        cerr << "Program failed. Exception: " << std::string(ex.what()) << endl;
-        return EXIT_FAILURE;
-    } catch (...) {
-        cerr << "Program failed without exception message." << endl;
-        return EXIT_FAILURE;
+    // Every Viam C++ SDK program must have one and only one Instance object which is created
+    // before
+    // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
+    vs::Instance inst;
+
+    // If you want to connect to a remote robot, this should be the url of the robot
+    // Ex: xxx.xxx.viam.cloud
+    std::string robot_address("localhost:8080");
+    // If you want to connect to a remote robot, you need some authentication secret
+    // You can find this on app.viam.com
+    vs::Credentials credentials("", "");
+
+    vs::DialOptions dial_options;
+
+    // If you have credentials, use this to pass them to the robot
+    // dial_options.credentials = credentials;
+
+    // This is for an example. Care should be taken before exercising this option in production.
+    dial_options.set_allow_insecure_downgrade(
+        (credentials.type().empty() && credentials.payload().empty()));
+
+    // Set the refresh interval of the robot (in seconds) (0 = auto refresh) and the dial
+    // options
+    vs::Options options = vs::Options(1, dial_options);
+
+    std::shared_ptr<vs::RobotClient> robot = vs::RobotClient::at_address(robot_address, options);
+    VIAM_SDK_LOG(info) << "Successfully connected to the robot";
+
+    std::vector<vs::Name> resource_names = robot->resource_names();
+
+    VIAM_SDK_LOG(info) << "Resources of the robot:";
+    for (const auto& resource : resource_names) {
+        VIAM_SDK_LOG(info) << " - " << resource.name() << " (" << resource.api().resource_subtype()
+                           << ")";
     }
 
+    std::string camera_name("camera1");
+
+    VIAM_SDK_LOG(info) << "Getting camera: " << camera_name;
+    std::shared_ptr<vs::Camera> camera = robot->resource_by_name<vs::Camera>(camera_name);
+
+    vs::Camera::properties props = camera->get_properties();
+    vs::Camera::intrinsic_parameters intrinsics = props.intrinsic_parameters;
+    VIAM_SDK_LOG(info) << "Image dimensions: " << intrinsics.width_px << " x "
+                       << intrinsics.height_px;
+
+    std::string output_file("img.png");
+    std::string image_mime_type("image/png");
+
+    VIAM_SDK_LOG(info) << "Getting image from camera ";
+
+    vs::Camera::raw_image img = camera->get_image(image_mime_type);
+
+    VIAM_SDK_LOG(info) << "Got image of mime type: " << img.mime_type;
+
+    VIAM_SDK_LOG(info) << "Getting and saving image to " << output_file;
+
+    std::ofstream fout;
+
+    // Operations on the ofstream will throw on failure.
+    fout.exceptions(std::ofstream::failbit);
+    fout.open(output_file, std::ios::binary | std::ios::out);
+
+    fout.write(reinterpret_cast<char*>(img.bytes.data()), img.bytes.size());
+    fout.close();
+
     return EXIT_SUCCESS;
+} catch (const std::exception& ex) {
+    std::cerr << "Program failed. Exception: " << std::string(ex.what()) << "\n";
+    return EXIT_FAILURE;
 }

From ac25b4ebc2c589fe7b6331d7b158d72b1dd1004b Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 4 Apr 2025 13:58:27 -0400
Subject: [PATCH 79/86] update example code to use logging

---
 src/viam/examples/dial/example_dial.cpp       |   7 +-
 .../dial_api_key/example_dial_api_key.cpp     |   4 +-
 .../example_audio_classification_client.cpp   |   7 +-
 src/viam/examples/modules/complex/client.cpp  |  38 ++--
 src/viam/examples/modules/simple/client.cpp   |  15 +-
 src/viam/examples/modules/tflite/main.cpp     |   8 +-
 src/viam/examples/motor/example_motor.cpp     | 182 ++++++++----------
 7 files changed, 126 insertions(+), 135 deletions(-)

diff --git a/src/viam/examples/dial/example_dial.cpp b/src/viam/examples/dial/example_dial.cpp
index f39522c26..60c052fe1 100644
--- a/src/viam/examples/dial/example_dial.cpp
+++ b/src/viam/examples/dial/example_dial.cpp
@@ -11,6 +11,7 @@
 
 #include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/generic.hpp>
+#include <viam/sdk/log/logging.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
 
@@ -36,15 +37,15 @@ int main() {
 
     // ensure we can query resources
     std::vector<Name> resource_names = robot->resource_names();
-    std::cout << "Resources" << std::endl;
+    VIAM_SDK_LOG(info) << "Resources:";
     for (const Name& resource : resource_names) {
-        std::cout << "\t" << resource << "\n";
+        VIAM_SDK_LOG(info) << resource;
     }
 
     // ensure we can create clients to the robot
     auto gc = robot->resource_by_name<GenericComponent>("generic1");
     if (gc) {
-        std::cout << "got generic component client named " << gc->name() << std::endl;
+        VIAM_SDK_LOG(info) << "got generic component client named " << gc->name();
     }
 
     return EXIT_SUCCESS;
diff --git a/src/viam/examples/dial_api_key/example_dial_api_key.cpp b/src/viam/examples/dial_api_key/example_dial_api_key.cpp
index b723e4227..850fe0dc9 100644
--- a/src/viam/examples/dial_api_key/example_dial_api_key.cpp
+++ b/src/viam/examples/dial_api_key/example_dial_api_key.cpp
@@ -50,9 +50,9 @@ int main(int argc, char* argv[]) {
 
     // ensure we can query resources
     std::vector<Name> resource_names = robot->resource_names();
-    std::cout << "Resources" << std::endl;
+    VIAM_SDK_LOG(info) << "Resources:";
     for (const Name& resource : resource_names) {
-        std::cout << "\t" << resource << "\n";
+        VIAM_SDK_LOG(info) << resource;
     }
 
     return EXIT_SUCCESS;
diff --git a/src/viam/examples/mlmodel/example_audio_classification_client.cpp b/src/viam/examples/mlmodel/example_audio_classification_client.cpp
index 6be570449..a1d32b9c0 100644
--- a/src/viam/examples/mlmodel/example_audio_classification_client.cpp
+++ b/src/viam/examples/mlmodel/example_audio_classification_client.cpp
@@ -401,14 +401,11 @@ int main(int argc, char* argv[]) try {
         return EXIT_SUCCESS;
     }
 } catch (const std::exception& ex) {
-    std::cout << argv[0] << ": "
+    std::cerr << argv[0] << ": "
               << "Failed: a std::exception was thrown: `" << ex.what() << "``" << std::endl;
     return EXIT_FAILURE;
-} catch (const std::string& ex) {
-    std::cout << argv[0] << ": "
-              << "Failed: a std::string was thrown: `" << ex << "``" << std::endl;
 } catch (...) {
-    std::cout << argv[0] << ": "
+    std::cerr << argv[0] << ": "
               << "Failed: an unknown exception was thrown" << std::endl;
     return EXIT_FAILURE;
 }
diff --git a/src/viam/examples/modules/complex/client.cpp b/src/viam/examples/modules/complex/client.cpp
index 4c26f055f..28bd451dc 100644
--- a/src/viam/examples/modules/complex/client.cpp
+++ b/src/viam/examples/modules/complex/client.cpp
@@ -13,6 +13,7 @@
 
 #include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/motor.hpp>
+#include <viam/sdk/log/logging.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
 
@@ -48,54 +49,61 @@ int main() {
     // Connect to robot.
     std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options);
     // Print resources.
-    std::cout << "Resources" << std::endl;
+    VIAM_SDK_LOG(info) << "Resources:";
     std::vector<Name> resource_names = robot->resource_names();
     for (const Name& resource : resource_names) {
-        std::cout << "\t" << resource << "\n";
+        VIAM_SDK_LOG(info) << resource;
     }
 
     // Exercise Gizmo methods.
     auto gc = robot->resource_by_name<Gizmo>("gizmo1");
     if (!gc) {
-        std::cerr << "could not get 'gizmo1' resource from robot" << std::endl;
+        VIAM_SDK_LOG(error) << "could not get 'gizmo1' resource from robot" << std::endl;
         return EXIT_FAILURE;
     }
+
     bool do_one_ret = gc->do_one("arg1");
-    std::cout << "gizmo1 do_one returned: " << do_one_ret << std::endl;
+    VIAM_SDK_LOG(info) << "gizmo1 do_one returned: " << do_one_ret;
+
     bool do_one_client_stream_ret = gc->do_one_client_stream({"arg1", "arg1", "arg1"});
-    std::cout << "gizmo1 do_one_client_stream returned: " << do_one_client_stream_ret << std::endl;
+    VIAM_SDK_LOG(info) << "gizmo1 do_one_client_stream returned: " << do_one_client_stream_ret;
+
     std::string do_two_ret = gc->do_two(false);
-    std::cout << "gizmo1 do_two returned: " << do_two_ret << std::endl;
+    VIAM_SDK_LOG(info) << "gizmo1 do_two returned: " << do_two_ret;
+
     std::vector<bool> do_one_server_stream_ret = gc->do_one_server_stream("arg1");
-    std::cout << "gizmo1 do_one_server_stream returned: " << std::endl;
+    VIAM_SDK_LOG(info) << "gizmo1 do_one_server_stream returned: ";
     for (bool ret : do_one_server_stream_ret) {
-        std::cout << '\t' << ret << std::endl;
+        VIAM_SDK_LOG(info) << ret;
     }
+
     std::vector<bool> do_one_bidi_stream_ret = gc->do_one_bidi_stream({"arg1", "arg2", "arg3"});
-    std::cout << "gizmo1 do_one_bidi_stream returned: " << std::endl;
+    VIAM_SDK_LOG(info) << "gizmo1 do_one_bidi_stream returned: ";
     for (bool ret : do_one_bidi_stream_ret) {
-        std::cout << '\t' << ret << std::endl;
+        VIAM_SDK_LOG(info) << ret;
     }
 
     // Exercise Summation methods.
     auto sc = robot->resource_by_name<Summation>("mysum1");
     if (!sc) {
-        std::cerr << "could not get 'mysum1' resource from robot" << std::endl;
+        VIAM_SDK_LOG(error) << "could not get 'mysum1' resource from robot";
         return EXIT_FAILURE;
     }
+
     double sum = sc->sum({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
-    std::cout << "mysum1 sum of numbers [0, 10) is: " << sum << std::endl;
+    VIAM_SDK_LOG(info) << "mysum1 sum of numbers [0, 10) is: " << sum;
 
     // Exercise a Base method.
     auto mc = robot->resource_by_name<Motor>("motor1");
     if (!mc) {
-        std::cerr << "could not get 'motor1' resource from robot" << std::endl;
+        VIAM_SDK_LOG(error) << "could not get 'motor1' resource from robot";
         return EXIT_FAILURE;
     }
+
     if (mc->is_moving()) {
-        std::cout << "motor1 is moving" << std::endl;
+        VIAM_SDK_LOG(info) << "motor1 is moving";
     } else {
-        std::cout << "motor1 is not moving" << std::endl;
+        VIAM_SDK_LOG(info) << "motor1 is not moving";
     }
 
     return EXIT_SUCCESS;
diff --git a/src/viam/examples/modules/simple/client.cpp b/src/viam/examples/modules/simple/client.cpp
index f08410336..75ca90e34 100644
--- a/src/viam/examples/modules/simple/client.cpp
+++ b/src/viam/examples/modules/simple/client.cpp
@@ -5,6 +5,7 @@
 #include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/common/proto_value.hpp>
 #include <viam/sdk/components/sensor.hpp>
+#include <viam/sdk/log/logging.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
 
@@ -32,16 +33,16 @@ int main() {
     std::shared_ptr<RobotClient> robot = RobotClient::at_address(address, options);
 
     // Print resources
-    std::cout << "Resources\n";
+    VIAM_SDK_LOG(info) << "Resources";
     std::vector<Name> resource_names = robot->resource_names();
     for (const Name& resource : resource_names) {
-        std::cout << "\t" << resource << "\n";
+        VIAM_SDK_LOG(info) << resource;
     }
 
     // Exercise sensor methods
     auto sensor = robot->resource_by_name<Sensor>("mysensor");
     if (!sensor) {
-        std::cerr << "could not get 'mysensor' resource from robot\n";
+        VIAM_SDK_LOG(error) << "could not get 'mysensor' resource from robot";
         return EXIT_FAILURE;
     }
 
@@ -49,7 +50,7 @@ int main() {
     ProtoStruct resp = sensor->do_command(command);
 
     if (command != resp) {
-        std::cerr << "Got unexpected result from 'mysensor'\n";
+        VIAM_SDK_LOG(error) << "Got unexpected result from 'mysensor'";
         return EXIT_FAILURE;
     }
 
@@ -57,15 +58,15 @@ int main() {
 
     auto itr = readings.find("signal");
     if (itr == readings.end()) {
-        std::cerr << "Expected signal not found in sensor readings\n";
+        VIAM_SDK_LOG(error) << "Expected signal not found in sensor readings";
         return EXIT_FAILURE;
     }
 
     const double* signal = itr->second.get<double>();
     if (signal) {
-        std::cout << "\t" << itr->first << ": " << *signal << "\n";
+        VIAM_SDK_LOG(info) << itr->first << ": " << *signal;
     } else {
-        std::cerr << "Unexpected value type for sensor reading\n";
+        VIAM_SDK_LOG(error) << "Unexpected value type for sensor reading";
         return EXIT_FAILURE;
     }
 
diff --git a/src/viam/examples/modules/tflite/main.cpp b/src/viam/examples/modules/tflite/main.cpp
index 65ca8c191..4bebd97d2 100644
--- a/src/viam/examples/modules/tflite/main.cpp
+++ b/src/viam/examples/modules/tflite/main.cpp
@@ -757,10 +757,10 @@ int serve(const std::string& socket_path) try {
 
     return EXIT_SUCCESS;
 } catch (const std::exception& ex) {
-    std::cout << "ERROR: A std::exception was thrown from `serve`: " << ex.what() << std::endl;
+    std::cerr << "ERROR: A std::exception was thrown from `serve`: " << ex.what() << std::endl;
     return EXIT_FAILURE;
 } catch (...) {
-    std::cout << "ERROR: An unknown exception was thrown from `serve`" << std::endl;
+    std::cerr << "ERROR: An unknown exception was thrown from `serve`" << std::endl;
     return EXIT_FAILURE;
 }
 
@@ -770,8 +770,8 @@ int main(int argc, char* argv[]) {
     const std::string usage = "usage: mlmodelservice_tflite /path/to/unix/socket";
 
     if (argc < 2) {
-        std::cout << "ERROR: insufficient arguments\n";
-        std::cout << usage << "\n";
+        std::cerr << "ERROR: insufficient arguments\n";
+        std::cerr << usage << "\n";
         return EXIT_FAILURE;
     }
 
diff --git a/src/viam/examples/motor/example_motor.cpp b/src/viam/examples/motor/example_motor.cpp
index 3d5a640ef..4cc781f0f 100644
--- a/src/viam/examples/motor/example_motor.cpp
+++ b/src/viam/examples/motor/example_motor.cpp
@@ -8,6 +8,7 @@
 
 #include <viam/sdk/common/instance.hpp>
 #include <viam/sdk/components/motor.hpp>
+#include <viam/sdk/log/logging.hpp>
 #include <viam/sdk/robot/client.hpp>
 #include <viam/sdk/rpc/dial.hpp>
 
@@ -15,116 +16,99 @@ void print_motor_position(std::shared_ptr<viam::sdk::Motor> motor) {
     // Whether the motor supports returning its position
     if (motor->get_properties().position_reporting) {
         // Position is measured in rotations (position is a typedef double)
-        std::cout << "Motor pos: " << motor->get_position() << std::endl;
+        VIAM_SDK_LOG(info) << "Motor pos: " << motor->get_position();
     } else {
-        std::cout << "Motor position unavailable" << std::endl;
+        VIAM_SDK_LOG(info) << "Motor position unavailable";
     }
 }
 
-int main() {
-    using std::cerr;
-    using std::cout;
-    using std::endl;
+int main() try {
     namespace vs = ::viam::sdk;
 
-    try {
-        // Every Viam C++ SDK program must have one and only one Instance object which is created
-        // before any other C++ SDK objects and stays alive until all Viam C++ SDK objects are
-        // destroyed.
-        vs::Instance inst;
-
-        // If you want to connect to a remote robot, this should be the url of the robot
-        // Ex: xxx.xxx.viam.cloud
-        std::string robot_address("localhost:8080");
-        // If you want to connect to a remote robot, you need some authentication secret
-        // You can find this on app.viam.com
-        vs::Credentials credentials("", "");
-
-        vs::DialOptions dial_options;
-
-        // If you have credentials, use this to pass them to the robot
-        // dial_options.credentials = credentials;
-
-        // This is for an example. Care should be taken before exercising this option in production.
-        dial_options.set_allow_insecure_downgrade(
-            (credentials.type().empty() && credentials.payload().empty()));
-
-        // Set the refresh interval of the robot (in seconds) (0 = auto refresh) and the dial
-        // options
-        vs::Options options = vs::Options(1, dial_options);
-
-        std::shared_ptr<vs::RobotClient> robot;
-        try {
-            robot = vs::RobotClient::at_address(robot_address, options);
-            cout << "Successfully connected to the robot" << endl;
-        } catch (const std::exception& e) {
-            cerr << "Failed to connect to the robot. Exiting." << endl;
-            throw;
-        }
-
-        std::vector<vs::Name> resource_names = robot->resource_names();
-
-        cout << "Resources of the robot:" << endl;
-        for (const vs::Name& resource : resource_names) {
-            cout << resource << endl;
-        }
-
-        std::string motor_name("motor1");
-
-        cout << "Getting motor: " << motor_name << endl;
-        std::shared_ptr<vs::Motor> motor;
-        try {
-            motor = robot->resource_by_name<vs::Motor>(motor_name);
-        } catch (const std::exception& e) {
-            cerr << "Failed to find " << motor_name << ". Exiting." << endl;
-            throw;
-        }
-
-        // Motors are always turned off if there is no attached client so this is expected to be 0
-        cout << "Motor power: " << motor->get_power_status().power_pct << endl;
-        cout << "Setting motor power to 50%" << endl;
-        motor->set_power(0.5);
-        cout << "Motor power: " << motor->get_power_status().power_pct << endl;
+    // Every Viam C++ SDK program must have one and only one Instance object which is created
+    // before any other C++ SDK objects and stays alive until all Viam C++ SDK objects are
+    // destroyed.
+    vs::Instance inst;
 
-        print_motor_position(motor);
+    // If you want to connect to a remote robot, this should be the url of the robot
+    // Ex: xxx.xxx.viam.cloud
+    std::string robot_address("localhost:8080");
+    // If you want to connect to a remote robot, you need some authentication secret
+    // You can find this on app.viam.com
+    vs::Credentials credentials("", "");
 
-        cout << "Moving motor by 0.5 rotations at 10rpm" << endl;
-        motor->go_for(10, 0.5);
-        print_motor_position(motor);
-        cout << "Moving motor back to starting location" << endl;
-        if (motor->get_properties().position_reporting) {
-            // More accurate
-            motor->go_to(10, 0);
-        } else {
-            // If position reporting is unavailable, we cannot call go_to
-            // but we can defer to reverting past actions
-            motor->go_for(10, -0.5);
-        }
-        print_motor_position(motor);
-        cout << "Moving motor forward for 2 seconds at 30rpm" << endl;
-        // With `revolutions`==0, the call will be non-blocking
-        // and the motor will rotate indefintely.
-        motor->go_for(30, 0);
+    vs::DialOptions dial_options;
 
-        print_motor_position(motor);
+    // If you have credentials, use this to pass them to the robot
+    // dial_options.credentials = credentials;
+
+    // This is for an example. Care should be taken before exercising this option in production.
+    dial_options.set_allow_insecure_downgrade(
+        (credentials.type().empty() && credentials.payload().empty()));
+
+    // Set the refresh interval of the robot (in seconds) (0 = auto refresh) and the dial
+    // options
+    vs::Options options = vs::Options(1, dial_options);
+
+    std::shared_ptr<vs::RobotClient> robot = vs::RobotClient::at_address(robot_address, options);
+    VIAM_SDK_LOG(info) << "Successfully connected to the robot";
 
-        // Wait for 2 seconds, print position every 0.5 seconds
-        for (int i = 0; i < 4; i++) {
-            std::this_thread::sleep_for(std::chrono::milliseconds(500));
-            print_motor_position(motor);
-        }
-
-        // Explicitly stop the motor
-        cout << "Stopping motor" << endl;
-        motor->stop({});
-
-    } catch (const std::exception& ex) {
-        cerr << "Program failed. Exception: " << std::string(ex.what()) << endl;
-        return EXIT_FAILURE;
-    } catch (...) {
-        cerr << "Program failed without exception message." << endl;
-        return EXIT_FAILURE;
+    std::vector<vs::Name> resource_names = robot->resource_names();
+
+    VIAM_SDK_LOG(info) << "Resources of the robot:";
+    for (const vs::Name& resource : resource_names) {
+        VIAM_SDK_LOG(info) << resource;
     }
 
+    std::string motor_name("motor1");
+
+    VIAM_SDK_LOG(info) << "Getting motor: " << motor_name;
+    std::shared_ptr<vs::Motor> motor = robot->resource_by_name<vs::Motor>(motor_name);
+
+    // Motors are always turned off if there is no attached client so this is expected to be 0
+    VIAM_SDK_LOG(info) << "Motor power: " << motor->get_power_status().power_pct;
+    VIAM_SDK_LOG(info) << "Setting motor power to 50%";
+    motor->set_power(0.5);
+    VIAM_SDK_LOG(info) << "Motor power: " << motor->get_power_status().power_pct;
+
+    print_motor_position(motor);
+
+    VIAM_SDK_LOG(info) << "Moving motor by 0.5 rotations at 10rpm";
+    motor->go_for(10, 0.5);
+
+    print_motor_position(motor);
+
+    VIAM_SDK_LOG(info) << "Moving motor back to starting location";
+    if (motor->get_properties().position_reporting) {
+        // More accurate
+        motor->go_to(10, 0);
+    } else {
+        // If position reporting is unavailable, we cannot call go_to
+        // but we can defer to reverting past actions
+        motor->go_for(10, -0.5);
+    }
+
+    print_motor_position(motor);
+
+    VIAM_SDK_LOG(info) << "Moving motor forward for 2 seconds at 30rpm";
+    // With `revolutions`==0, the call will be non-blocking
+    // and the motor will rotate indefintely.
+    motor->go_for(30, 0);
+
+    print_motor_position(motor);
+
+    // Wait for 2 seconds, print position every 0.5 seconds
+    for (int i = 0; i < 4; i++) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(500));
+        print_motor_position(motor);
+    }
+
+    // Explicitly stop the motor
+    VIAM_SDK_LOG(info) << "Stopping motor";
+    motor->stop({});
+
     return EXIT_SUCCESS;
+} catch (const std::exception& ex) {
+    std::cerr << "Program failed. Exception: " << std::string(ex.what()) << "\n";
+    return EXIT_FAILURE;
 }

From 04c92ce54006c2b884f436e3e332e49fd049dfeb Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Fri, 4 Apr 2025 14:56:17 -0400
Subject: [PATCH 80/86] add test and use set_global_resource_name

---
 .../example_audio_classification_client.cpp   | 107 +++++++++---------
 src/viam/sdk/log/logging.cpp                  |   5 +
 src/viam/sdk/log/logging.hpp                  |   3 +
 src/viam/sdk/tests/test_log.cpp               |  17 +++
 4 files changed, 80 insertions(+), 52 deletions(-)

diff --git a/src/viam/examples/mlmodel/example_audio_classification_client.cpp b/src/viam/examples/mlmodel/example_audio_classification_client.cpp
index a1d32b9c0..51e5731ea 100644
--- a/src/viam/examples/mlmodel/example_audio_classification_client.cpp
+++ b/src/viam/examples/mlmodel/example_audio_classification_client.cpp
@@ -83,6 +83,18 @@ int main(int argc, char* argv[]) try {
     // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed.
     viam::sdk::Instance inst;
 
+    // By default, log messages with VIAM_SDK_LOG have "Viam C++ SDK" as the resource name.
+    // Here we override it with the name of the executable, trimming the leading path if present.
+
+    std::string exe_name{argv[0]};
+
+    const auto last_slash_pos = exe_name.find_last_of('/');
+    if (last_slash_pos != std::string::npos) {
+        exe_name = exe_name.substr(last_slash_pos);
+    }
+
+    viam::sdk::LogManager::get().set_global_resource_name(exe_name);
+
     // Build up our command line options. The example operates in two
     // modes. In the "--generate" mode, it takes command line
     // parameters needed to satisfy the interpolation points in the
@@ -168,35 +180,33 @@ int main(int argc, char* argv[]) try {
     if (opt_generating) {
         // Validate that we have the right options for generation.
         if (opt_robot_host || opt_api_key || opt_api_key_id) {
-            std::cout << argv[0]
-                      << ": With `--generate`, do not provide `--robot-{host,api-key,api-key-id}`"
-                      << std::endl;
+            VIAM_SDK_LOG(error)
+                << "With `--generate`, do not provide `--robot-{host,api-key,api-key-id}`";
             return EXIT_FAILURE;
         }
 
         if (!opt_model_path) {
-            std::cout << argv[0] << ": With `--generate`, a `--model-path` is required"
-                      << std::endl;
+            VIAM_SDK_LOG(error) << "With `--generate`, a `--model-path` is required";
             return EXIT_FAILURE;
         }
+
         const bf::path model_path(opt_model_path.get());
         if (!bf::is_regular_file(model_path)) {
-            std::cout << argv[0] << ": The path `" << model_path.c_str()
-                      << "` provided for `--model-path` is not an existing regular file"
-                      << std::endl;
+            VIAM_SDK_LOG(error) << "The path `" << model_path.c_str()
+                                << "` provided for `--model-path` is not an existing regular file";
             return EXIT_FAILURE;
         }
 
         if (!opt_tflite_module_path) {
-            std::cout << argv[0] << ": With `--generate`, a `--tflite-module-path` is required"
-                      << std::endl;
+            VIAM_SDK_LOG(error) << "With `--generate`, a `--tflite-module-path` is required";
             return EXIT_FAILURE;
         }
+
         const bf::path tflite_module_path(opt_tflite_module_path.get());
         if (!bf::is_regular_file(tflite_module_path)) {
-            std::cout << argv[0] << ": The path `" << tflite_module_path.c_str()
-                      << "` provided for `--tflite-module-path` is not an existing regular file"
-                      << std::endl;
+            VIAM_SDK_LOG(error)
+                << "The path `" << tflite_module_path.c_str()
+                << "` provided for `--tflite-module-path` is not an existing regular file";
             return EXIT_FAILURE;
         }
 
@@ -207,28 +217,25 @@ int main(int argc, char* argv[]) try {
         const auto config = boost::format(kRobotConfigTemplate) % bf::absolute(model_path).c_str() %
                             bf::absolute(tflite_module_path).c_str();
 
-        std::cout << config << std::endl;
+        VIAM_SDK_LOG(info) << config << std::endl;
 
     } else {
         // Validate that we have the right options for classification mode.
         if (opt_model_path || opt_tflite_module_path) {
-            std::cout << argv[0] << ": Without `--generate`, do not provide `--*path*` arguments"
-                      << std::endl;
+            VIAM_SDK_LOG(error) << "Without `--generate`, do not provide `--*path*` arguments";
             return EXIT_FAILURE;
         }
 
         if (!opt_robot_host) {
-            std::cout << argv[0]
-                      << ": The `--robot-host` argument is required when connecting to a robot"
-                      << std::endl;
+            VIAM_SDK_LOG(error)
+                << "The `--robot-host` argument is required when connecting to a robot";
             return EXIT_FAILURE;
         }
 
         if (!opt_api_key || !opt_api_key_id) {
-            std::cout << argv[0]
-                      << ": The `--robot-api-key` and the `--robot-api-key-id` argument are "
-                         "required when connecting to a robot"
-                      << std::endl;
+            VIAM_SDK_LOG(error)
+                << "The `--robot-api-key` and the `--robot-api-key-id` argument are "
+                   "required when connecting to a robot";
             return EXIT_FAILURE;
         }
 
@@ -248,10 +255,9 @@ int main(int argc, char* argv[]) try {
         auto yamnet_service =
             robot->resource_by_name<vsdk::MLModelService>("yamnet_classification_tflite");
         if (!yamnet_service) {
-            std::cout << argv[0] << ": "
-                      << "Failed: did not find the `yamnet_classification_tflite` resource, cannot "
-                         "continue"
-                      << std::endl;
+            VIAM_SDK_LOG(error)
+                << " did not find the `yamnet_classification_tflite` resource, cannot "
+                   "continue";
             return EXIT_FAILURE;
         }
 
@@ -296,8 +302,7 @@ int main(int argc, char* argv[]) try {
         // the robot configuration.
         auto categories = result->find("categories");
         if (categories == result->end()) {
-            std::cout << argv[0] << ": "
-                      << "Failed: a `categories` tensor was not returned" << std::endl;
+            VIAM_SDK_LOG(error) << "A `categories` tensor was not returned";
             return EXIT_FAILURE;
         }
 
@@ -311,10 +316,8 @@ int main(int argc, char* argv[]) try {
         const auto* const categories_float =
             boost::get<vsdk::MLModelService::tensor_view<float>>(&categories->second);
         if (!categories_float) {
-            std::cout
-                << argv[0] << ": "
-                << "Failed: a `categories` tensor was returned, but it was not of type `float`"
-                << std::endl;
+            VIAM_SDK_LOG(error)
+                << "A `categories` tensor was returned, but it was not of type `float`";
             return EXIT_FAILURE;
         }
 
@@ -323,23 +326,23 @@ int main(int argc, char* argv[]) try {
         // results and print out the label and score.
         if (!opt_model_label_path) {
             for (const auto& val : *categories_float) {
-                std::cout << val << std::endl;
+                VIAM_SDK_LOG(info) << val;
             }
         } else {
             // Ensure that the label path is something we can actually read from.
             const bf::path model_label_path(opt_model_label_path.get());
             if (!bf::is_regular_file(model_label_path)) {
-                std::cout << argv[0] << ": Failed: The path `" << model_label_path.c_str()
-                          << "` provided for `--model-label-path` is not an existing regular file"
-                          << std::endl;
+                VIAM_SDK_LOG(error)
+                    << "The path `" << model_label_path.c_str()
+                    << "` provided for `--model-label-path` is not an existing regular file";
                 return EXIT_FAILURE;
             }
 
             // Open the labels file, or bail.
             std::ifstream labels_stream(model_label_path.c_str());
             if (!labels_stream) {
-                std::cout << argv[0] << ": Failed: Unable to open label path `"
-                          << model_label_path.c_str() << "`" << std::endl;
+                VIAM_SDK_LOG(error)
+                    << "Unable to open label path `" << model_label_path.c_str() << "`";
                 return EXIT_FAILURE;
             }
 
@@ -353,9 +356,8 @@ int main(int argc, char* argv[]) try {
             // If the tensor size doesn't match the labels file size, then the labels file
             // is probably incorrect.
             if (categories_float->size() != labels.size()) {
-                std::cout << argv[0]
-                          << ": Failed: Size mismatch between category scores and label files"
-                          << std::endl;
+                VIAM_SDK_LOG(error)
+                    << "Size mismatch between category scores and label files" << std::endl;
                 return EXIT_FAILURE;
             }
 
@@ -364,10 +366,12 @@ int main(int argc, char* argv[]) try {
                 const std::string* label;
                 float score;
             };
+
             std::vector<scored_label> scored_labels;
             for (size_t i = 0; i != labels.size(); ++i) {
                 scored_labels.push_back({&labels[i], (*categories_float)[i]});
             }
+
             std::sort(begin(scored_labels), end(scored_labels), [](const auto& l, const auto& r) {
                 return l.score > r.score;
             });
@@ -375,37 +379,36 @@ int main(int argc, char* argv[]) try {
             // Print out the top 5 (or fewer) label/score pairs.
             for (size_t i = 0; i != std::min(5UL, scored_labels.size()); ++i) {
                 // TODO: Avoid hardcoding the width here.
-                std::cout << boost::format("%1%: %2% %|40t|%3%\n") % i % *scored_labels[i].label %
-                                 scored_labels[i].score;
+                VIAM_SDK_LOG(info) << boost::format("%1%: %2% %|40t|%3%\n") % i %
+                                          *scored_labels[i].label % scored_labels[i].score;
             }
-            std::cout.flush();
         }
 
         // Run 100 rounds of inference, accumulate some descriptive
         // statistics, and report them.
-        std::cout << "\nMeasuring inference latency ...\n";
+        VIAM_SDK_LOG(info) << "\nMeasuring inference latency ...";
         bacc::accumulator_set<double, bacc::stats<bacc::tag::mean, bacc::tag::moment<2>>>
             accumulator;
+
         for (std::size_t i = 0; i != 100; ++i) {
             const auto start = std::chrono::steady_clock::now();
             static_cast<void>(yamnet_service->infer(inputs));
             const auto finish = std::chrono::steady_clock::now();
             const std::chrono::duration<double> elapsed = finish - start;
-            ;
             accumulator(elapsed.count());
         }
-        std::cout << "Inference latency (seconds), Mean: " << bacc::mean(accumulator) << std::endl;
-        std::cout << "Inference latency (seconds), Var : " << bacc::moment<2>(accumulator)
-                  << std::endl;
+
+        VIAM_SDK_LOG(info) << "Inference latency (seconds), Mean: " << bacc::mean(accumulator);
+        VIAM_SDK_LOG(info) << "Inference latency (seconds), Var : " << bacc::moment<2>(accumulator);
 
         return EXIT_SUCCESS;
     }
 } catch (const std::exception& ex) {
     std::cerr << argv[0] << ": "
-              << "Failed: a std::exception was thrown: `" << ex.what() << "``" << std::endl;
+              << "Failed: a std::exception was thrown: `" << ex.what() << "``\n";
     return EXIT_FAILURE;
 } catch (...) {
     std::cerr << argv[0] << ": "
-              << "Failed: an unknown exception was thrown" << std::endl;
+              << "Failed: an unknown exception was thrown\n";
     return EXIT_FAILURE;
 }
diff --git a/src/viam/sdk/log/logging.cpp b/src/viam/sdk/log/logging.cpp
index 024a622ca..0c8c7ec2a 100644
--- a/src/viam/sdk/log/logging.cpp
+++ b/src/viam/sdk/log/logging.cpp
@@ -91,6 +91,11 @@ LogManager& LogManager::get() {
     return result;
 }
 
+void LogManager::set_global_resource_name(std::string name) {
+    sdk_logger_.channel(std::move(name));
+    VIAM_SDK_LOG(debug) << "Overrode global resource name";
+}
+
 LogSource& LogManager::global_logger() {
     return sdk_logger_;
 }
diff --git a/src/viam/sdk/log/logging.hpp b/src/viam/sdk/log/logging.hpp
index 93bdaa044..11004a302 100644
--- a/src/viam/sdk/log/logging.hpp
+++ b/src/viam/sdk/log/logging.hpp
@@ -69,6 +69,9 @@ class LogManager {
     /// This is the only way to access the logger.
     static LogManager& get();
 
+    /// @brief Override the channel name of general log messages not originating from resources.
+    void set_global_resource_name(std::string);
+
     /// @brief Set the global logger severity.
     void set_global_log_level(log_level);
 
diff --git a/src/viam/sdk/tests/test_log.cpp b/src/viam/sdk/tests/test_log.cpp
index d550cb8d4..da52b3733 100644
--- a/src/viam/sdk/tests/test_log.cpp
+++ b/src/viam/sdk/tests/test_log.cpp
@@ -29,6 +29,23 @@ BOOST_AUTO_TEST_CASE(test_cout_logging) {
     }
 }
 
+BOOST_AUTO_TEST_CASE(test_global_name) {
+    cout_redirect redirect;
+
+    sdk::LogManager::get().set_global_resource_name("My Channel");
+
+    VIAM_SDK_LOG(info) << "after";
+
+    const std::string rec = redirect.os.str();
+    redirect.release();
+
+    for (const char* s : {"My Channel", "after"}) {
+        BOOST_CHECK(rec.find(s) != std::string::npos);
+    }
+
+    BOOST_CHECK(rec.find(sdk::global_resource_name()) == std::string::npos);
+}
+
 BOOST_AUTO_TEST_CASE(test_global_filter) {
     cout_redirect redirect;
 

From 9297062abf2c0c452c2219c2a9946783f8e5a948 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Mon, 7 Apr 2025 11:07:08 -0400
Subject: [PATCH 81/86] silence const warnings

---
 src/viam/sdk/log/logging.cpp | 4 ++--
 src/viam/sdk/log/logging.hpp | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/viam/sdk/log/logging.cpp b/src/viam/sdk/log/logging.cpp
index 0c8c7ec2a..910636e80 100644
--- a/src/viam/sdk/log/logging.cpp
+++ b/src/viam/sdk/log/logging.cpp
@@ -91,8 +91,8 @@ LogManager& LogManager::get() {
     return result;
 }
 
-void LogManager::set_global_resource_name(std::string name) {
-    sdk_logger_.channel(std::move(name));
+void LogManager::set_global_resource_name(const std::string& name) {
+    sdk_logger_.channel(name);
     VIAM_SDK_LOG(debug) << "Overrode global resource name";
 }
 
diff --git a/src/viam/sdk/log/logging.hpp b/src/viam/sdk/log/logging.hpp
index 11004a302..9103b2033 100644
--- a/src/viam/sdk/log/logging.hpp
+++ b/src/viam/sdk/log/logging.hpp
@@ -70,7 +70,7 @@ class LogManager {
     static LogManager& get();
 
     /// @brief Override the channel name of general log messages not originating from resources.
-    void set_global_resource_name(std::string);
+    void set_global_resource_name(const std::string& name);
 
     /// @brief Set the global logger severity.
     void set_global_log_level(log_level);

From b1aa44b430a7ce1fe49073617de24960e62b2163 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 16 Apr 2025 11:09:00 -0400
Subject: [PATCH 82/86] add remark on logging enums

---
 src/viam/sdk/log/logging.hpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/viam/sdk/log/logging.hpp b/src/viam/sdk/log/logging.hpp
index 9103b2033..18b33200d 100644
--- a/src/viam/sdk/log/logging.hpp
+++ b/src/viam/sdk/log/logging.hpp
@@ -24,6 +24,9 @@ namespace sdk {
 
 /// @brief Severity levels for the logger.
 /// @ingroup Log
+/// @remark Our enums are usually prefixed with k_, but these values are meant to be used without
+/// namespace qualification in the log macros, eg `VIAM_SDK_LOG(info)` instead of
+/// `VIAM_SDK_LOG(k_info)`.
 enum class log_level : std::int8_t {
     trace = -2,
     debug = -1,

From 6018b945b5688d40f2e693234f35ab6420c9b32a Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 16 Apr 2025 11:15:48 -0400
Subject: [PATCH 83/86] add explanatory comment

---
 src/viam/sdk/log/private/log_backend.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/viam/sdk/log/private/log_backend.cpp b/src/viam/sdk/log/private/log_backend.cpp
index 6e917adc5..00287c797 100644
--- a/src/viam/sdk/log/private/log_backend.cpp
+++ b/src/viam/sdk/log/private/log_backend.cpp
@@ -7,6 +7,8 @@ namespace viam {
 namespace sdk {
 namespace impl {
 
+// The following code was copy-pasted from the boost mailing list
+// https://lists.boost.org/boost-commit/2009/04/15209.php
 time_pt ptime_convert(const boost::posix_time::ptime& from) {
     namespace posix_time = boost::posix_time;
 

From bc5f1652a4edb53bbb623004a93e546a5ed82f6c Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 16 Apr 2025 11:51:30 -0400
Subject: [PATCH 84/86] format file/line into rdk log message

---
 src/viam/sdk/log/private/log_backend.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/viam/sdk/log/private/log_backend.cpp b/src/viam/sdk/log/private/log_backend.cpp
index 00287c797..cde30a506 100644
--- a/src/viam/sdk/log/private/log_backend.cpp
+++ b/src/viam/sdk/log/private/log_backend.cpp
@@ -20,9 +20,14 @@ time_pt ptime_convert(const boost::posix_time::ptime& from) {
 }
 
 void LogBackend::consume(const boost::log::record_view& rec) const {
+    std::ostringstream os;
+
+    os << "[" << *rec[attr_file_type{}] << ":" << rec[attr_line_type{}] << "] "
+       << *rec[boost::log::expressions::smessage];
+
     parent->log(*rec[attr_channel_type{}],
                 to_string(*rec[attr_sev_type{}]),
-                *rec[boost::log::expressions::smessage],
+                os.str(),
                 ptime_convert(*rec[attr_time_type{}]));
 }
 

From 82f710479be4f4cec93c029e8526773607ce0185 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 16 Apr 2025 11:58:58 -0400
Subject: [PATCH 85/86] document use of boost log

---
 README.md | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/README.md b/README.md
index 90eb9419c..e754f6bc6 100644
--- a/README.md
+++ b/README.md
@@ -88,6 +88,15 @@ above) should resolve them. And please file a bug report! We will
 endeavor to be as responsive as possible, and resolve issues as
 quickly as possible.
 
+## A note on logging
+
+Users should only interact with logging via the macros, classes, and functions in
+[`viam/sdk/log/logging.hpp`](src/viam/sdk/log/logging.hpp). Logging is 
+implemented using Boost.Log, but this is an implementation detail subject
+to change without warning. In particular, using Boost.Log macros such as
+`BOOST_LOG_TRIVIAL` or `BOOST_LOG_SEV` is undefined behavior which will likely 
+fail to output log messages.
+
 ## License
 Copyright 2022 Viam Inc.
 

From 2fa08609a13c4a09d86407089992a7528988f910 Mon Sep 17 00:00:00 2001
From: Lia Stratopoulos <167905060+lia-viam@users.noreply.github.com>
Date: Wed, 16 Apr 2025 11:59:07 -0400
Subject: [PATCH 86/86] remove period

---
 src/viam/sdk/log/logging.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/viam/sdk/log/logging.hpp b/src/viam/sdk/log/logging.hpp
index 18b33200d..aa86c40d2 100644
--- a/src/viam/sdk/log/logging.hpp
+++ b/src/viam/sdk/log/logging.hpp
@@ -20,7 +20,7 @@
 namespace viam {
 namespace sdk {
 
-/// @defgroup Log Classes related to logging.
+/// @defgroup Log Classes related to logging
 
 /// @brief Severity levels for the logger.
 /// @ingroup Log