PX4 MAVSDK – C++ Programming [Episode 10] Custom Logging and Integration Testing (gtest)

Hello! This is Aiden from the Marketing Team. In our previous sessions, we explored various methodologies for actual drone control, including Offboard mode.

However, when developing real-world robotics software in university labs or corporate environments, you quickly realize that debugging and verifying system stability often takes much longer than writing the initial code. Due to the nature of drones, a minor logic error can lead to a crash—resulting in catastrophic hardware damage.

Therefore, in this 10th installment, we will take a deep dive into two essential technologies for maximizing the reliability of autonomous flight systems: Custom Logging and Automated Integration Testing using the Google C++ Testing Framework (gtest).


1. Understanding the MAVSDK Logging System

To understand what data is flowing inside the drone during flight, or to identify communication latency and packet loss, you must monitor the logs. The MAVSDK core and plugins are designed to continuously output useful log messages during operation. By default, these messages are directed to standard output (stdout), appearing directly on your console.

Log messages are categorized into four levels based on their importance:

  • Debug: Detailed messages for reporting internal progress or status. (Note: These are hidden in Release builds.)
  • Info: Informational messages regarding general progress and status.
  • Warn: Warning messages triggered when the vehicle does not behave as expected, such as a rejected command.
  • Err: Reports critical errors related to SDK operation, such as communication link issues or retry failures.


Custom Logging (Custom Logging Callback)

While flooding the console with logs is great for real-time debugging, it is inconvenient when you need to analyze research data post-flight or transmit logs to a server. MAVSDK provides the ability to override the logging system’s behavior by registering a custom callback function.

Using the mavsdk::log::subscribe function, you can save log messages to external files or filter out less important messages (e.g., Debug).

[C++ Example: Custom Logging]

C++
#include <iostream>
#include <fstream>
#include <mavsdk/mavsdk.h>
#include <mavsdk/log_callback.h>

using namespace mavsdk;

int main() {
    // Open a file stream to save logs
    std::ofstream log_file("drone_flight_log.txt", std::ios::app);

    // Register a custom log callback (using a lambda function)
    mavsdk::log::subscribe([&log_file](mavsdk::log::Level level, 
                                       const std::string& message, 
                                       const std::string& file, 
                                       int line) {
        // 1. Record all logs to a file (Custom logic)
        if (log_file.is_open()) {
            log_file << "[" << file << ":" << line << "] " << message << std::endl;
        }

        // 2. Filter console output to show only Warn and Err
        if (level == mavsdk::log::Level::Warn || level == mavsdk::log::Level::Err) {
            std::cerr << "⚠️ [CRITICAL]: " << message << '\n';
        }
    });

    std::cout << "Custom logging has started.\n";

    // Drone connection and control logic omitted...

    return 0;
}

Applying this code executes the lambda function registered as the callback. Calling subscribe a second time will overwrite the previous callback. To return to the default logging behavior (stdout), simply call subscribe with nullptr to unsubscribe.

🚨 Note for Researchers: Multi-threaded Logging

Callbacks can be invoked randomly from multiple internal threads. To prevent performance degradation, MAVSDK does not handle synchronization (such as mutex locks) for the log stream. If your external file logging system is not thread-safe, you must implement synchronization yourself using std::mutex to prevent data corruption. Additionally, keep the code inside the callback lightweight; heavy tasks can cause communication latency.


Internal Logging API for Plugins and Testing

If you are modifying the MAVSDK core or writing new integration tests and need console logging, you can use the internal macro APIs provided by MAVSDK. These APIs automatically append timestamps, log levels, filenames, and line numbers to your messages.

C++
#include "integration_test_helper.h"
#include "log.h"

void my_test_function() {
    LogDebug() << "This is a debug message for monitoring progress.";
    LogInfo() << "This is an informational message.";
    LogWarn() << "The vehicle is not behaving as commanded!";
    LogErr() << "Communication link lost.";
}

Output Example: [04:55:08|Error] This is an error message (test_file.cpp:26)

Furthermore, if you wish to track all raw packet data for MAVLink messages, you can set the #define MESSAGE_DEBUGGING 1 macro at the top of src/core/system.cpp and rebuild (only active in Debug builds).


Automated Reliability Verification: Google Test (gtest) Integration

After writing hundreds of lines of autonomous flight code, you cannot realistically power up the drone and go outside for a test every single time. The MAVSDK C++ library natively integrates the Google C++ Testing Framework (gtest), the world’s most widely used testing framework, to verify code integrity.

Whenever new code is committed to the library, these tests are executed; the code can only be merged into the main branch once all tests pass.

1) Unit Tests

Unit tests verify logical errors in individual functions, such as communication modules or distance calculation algorithms. After building the project, you can run all unit tests with the following command: build/default/src/core/core_tests

C++
build/default/src/core/core_tests

2) Integration Tests and SITL Integration

While unit tests verify logic, integration tests verify the organic operation of the entire system—for example, “Does the virtual drone actually increase altitude when a takeoff command is issued?” Because running integration tests on physical hardware can be dangerous due to unpredictable behavior, they must be executed in a SITL (Software In The Loop) simulator environment.

[SITL-based Integration Test Procedure] To run integration tests, the PX4 Gazebo simulation must be running in the background. Open two terminals and follow these steps:

  1. Terminal 1 (Manual Simulator Execution): Use the HEADLESS=1 option if you want to run the simulator in the background without a 3D GUI (highly useful for remote servers or CI/CD pipelines).
  2. Terminal 2 (Integration Test Execution): Run the compiled integration test runner.

3) Test Filtering and Debugging Tips

As the number of test cases grows, you may want to test specific features only. You can use gtest’s powerful filtering functionality:

  • Check entire test list: --gtest_list_tests
  • Run a specific test (e.g., telemetry health check): --gtest_filter=TelemetryTest.Health

For researchers implementing new avoidance algorithms for a thesis, automating unit tests for your plugin and integration tests with SITL is crucial. This allows you to prove in just one minute that your existing functions remain intact even after large-scale refactoring.


In this 10th episode, we explored how to build a custom logging system that serves as the “eyes” of autonomous flight software and the gtest-based automated testing workflow that ensures code reliability.

Maintaining thread-safety in logging, keeping callbacks lightweight, and establishing an automated SITL-based testing framework before actual flight are the decisive differences between hobbyist coding and professional R&D.

With this, we have covered almost all practical elements of MAVSDK C++ programming, from environment setup to control APIs, logging, and testing.

In the next [Episode 11: MAVSDK vs. MAVROS vs. uXRCE-DDS Comparison], we will perform a deep dive into the three technologies researchers struggle with most when choosing a drone communication framework, using academic data (latency, resource utilization).


Author: Aiden, Marketing Team at QUAD Drone Research Lab

Date: 2026.03.20

Similar Posts

  • |

    드론 자재 선정 – 제어·통신부 : 911 FALCON 프로젝트

    안녕하세요, 쿼드(QUAD) 드론연구소의 이근찬 선임연구원입니다. 지난 편에서는 911 FALCON 드론의 전원부(Power System) 설계와 자재 선정 과정을 정리해 보았습니다. 추력부에서 요구되는 전력 조건을 기준으로 배터리 용량을 결정하고, 비행제어기와 ESC에 안정적으로 전원을 공급하기 위한 파워모듈과 PDB를 선정한 뒤, 각 장치에 전원이 어떤 경로로 분배되는지까지 전체 전원 구조를 함께 정리했습니다. 이번 편에서는 그 다음 단계로, 드론의 비행과 운용을…

  • PX4 MAVSDK – C++ Programming [10편] 사용자 정의 로깅 및 통합 테스트(gtest)

    안녕하세요! 마케팅팀 에이든(Aiden)입니다. 지난 연재들에서는 오프보드(Offboard) 모드를 비롯하여 드론을 실제로 제어하는 다양한 방법론을 다루었습니다. 하지만 대학 연구실이나 기업에서 실제 로보틱스 소프트웨어를 개발하다 보면, 코드를 작성하는 시간보다 버그를 잡고 시스템의 안정성을 검증하는 시간이 훨씬 더 오래 걸린다는 사실을 깨닫게 됩니다. 드론의 특성상 사소한 논리 오류 하나가 추락이라는 치명적인 하드웨어 손상으로 이어질 수 있기 때문입니다. 그래서 이번…

  • PX4 MAVSDK – C++ Programming [9편] 정밀한 드론 제어: 오프보드(Offboard) 모드

    안녕하세요! 마케팅팀의 에이든(Aiden)입니다. 지난 연재들을 통해 우리는 드론의 센서 데이터를 수신하고(Telemetry), 기본적인 이착륙을 수행하며(Action), 다수의 웨이포인트를 순회하는 자율 비행(Mission)까지 알아보았습니다. 하지만 카메라(Vision) 기반의 장애물 회피, 인공지능(AI) 표적 추적, 다수 기체의 군집 비행a(Swarm)과 같은 첨단 로보틱스 연구를 수행하려면, 정해진 경로를 따라가는 것만으로는 부족합니다. 드론의 두뇌 역할을 하는 컴패니언 컴퓨터(Companion Computer)가 밀리초(ms) 단위로 상황을 판단하고, 기체의 속도와…

  • PX4 MAVSDK – C++ Programming [3편] MAVSDK C++ 설치 및 SITL 시뮬레이션 환경 구축

    안녕하세요! 마케팅팀 에이든(Aiden)입니다. 지난 2편에서는 MAVSDK C++을 다루기 위해 필수적인 C++의 핵심 문법(포인터, 참조, 람다 함수, 템플릿 등)을 함께 살펴보았습니다. 이론적인 배경지식을 탄탄히 다졌으니, 이제 본격적으로 코드가 실행될 ‘놀이터’를 만들어 볼 차례입니다. 연구실이나 실험실에서 개발한 드론 제어 코드를 실제 기체에 바로 올려서 테스트하는 것은 매우 위험합니다. 작은 오타 하나, 로직의 사소한 오류 하나가 수백만 원짜리…

  • |

    PX4 MAVSDK – C++ Programming [6편] Action API를 이용한 기본 비행 제어

    안녕하세요! 마케팅 팀의 에이든(Aiden)입니다. 지난 5편에서는 드론의 신경망이라고 할 수 있는 Telemetry 플러그인을 통해 기체의 현재 상태와 GPS 위치를 비동기 콜백으로 수신하는 방법을 알아보았습니다. 센서 데이터를 성공적으로 읽어왔다면, 이제 드디어 기체에 물리적인 움직임을 명령할 차례입니다! 자율 비행 로보틱스 연구에 있어서 드론을 원하는 시점에 정확히 이륙(Takeoff)시키고 안전하게 착륙(Land)시키는 것은 모든 알고리즘의 시작과 끝을 담당하는 가장 핵심적인…

  • PX4 MAVSDK – C++ Programming [4편] C++ 앱 빌드 및 통신 연결

    안녕하세요! 마케팅팀 에이든(Aiden)입니다. 지난 3편에서는 안전한 자율 비행 테스트를 위한 SITL 가상 환경을 구축하고, MAVSDK에서 제공하는 기본 예제를 실행해 보며 드론이 가상 공간에서 이륙하는 모습을 확인했습니다. 하지만 연구자나 개발자라면 남이 만들어둔 예제를 실행하는 것에 만족할 수 없겠죠? 이번 4편에서는 빈 폴더에서부터 시작해 나만의 C++ 프로젝트를 설정(Build)하고, 드론 시스템과 통신을 연결(Connection)하는 방법을 처음부터 끝까지 친절하게 안내해…

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다