DLT 示例应用
要在应用程序中使用 DLT,它必须链接到 DLT 库。在系统上安装 DLT 守护程序后,将有一个名为 libdlt.so 的共享库,它为应用程序提供接口以获取与 DLT 守护程序的连接。在使用共享 dlt 库构建程序之前,必须在构建环境中设置库路径和包含路径。默认情况下,头文件“dlt.h”位于标准包含目录中名为“dlt/”的目录中。
此示例通过使用最少的代码示例概述了应用程序内部的 DLT 使用情况。
#include <dlt/dlt.h>
DLT_DECLARE_CONTEXT(ctx); /* declare context */
int main()
{
DLT_REGISTER_APP("TAPP", "Test Application for Logging");
DLT_REGISTER_CONTEXT(ctx, "TES1", "Test Context for Logging");
/* … */
DLT_LOG(ctx, DLT_LOG_ERROR, DLT_CSTRING("This is an error"));
/* … */
DLT_UNREGISTER_CONTEXT(ctx);
DLT_UNREGISTER_APP();
return 0;
}
DLT 非常易于使用。开发人员必须做的第一件事是包含 dlt 头文件。可以使用下一行中显示的宏静态声明 DLT 上下文。首先,必须在主函数内注册一个 DLT 应用程序。为此,必须指定应用程序标识符 APID 和应用程序描述。之后,可以指定一个或多个 DLT 上下文。要以详细模式记录消息,可以使用 DLT_LOG 宏。作为参数,必须指定日志上下文、日志级别和参数变量列表。 DLT 要求使用 DLT 类型宏对每个参数进行强类型化。在本例中,DLT_CSTRING 用于指定一个常量字符串。在应用程序清理时,所有 DLT 上下文以及 DLT 应用程序都必须注销。
cmake 中如何加入 DLT
要将 DLT 加入 CMake,推荐的方法是使用作为安装一部分生成的 CMake 配置文件。
你可以这样:
find_package(automotive-dlt REQUIRED)
...
target_link_libraries(myapp PRIVATE Genivi::DLT)
这让您的项目自动获得 libdlt 所需的所有必要编译和链接标志,包括包含目录。
生成的 CMake 配置文件遵循“Modern CMake”约定,并且只导出一个 IMPORTED CMake 目标;它不设置任何变量,除了可用于将 DLT 视为可选依赖项的automotive-dlt_FOUND 变量。
生成的 CMake 配置文件(在调用 find_package(automotive-dlt) 时隐式使用)默认仅将顶级目录添加到编译器的头文件搜索路径中;这要求用户的 #include 指令以常规形式编写,例如<dlt/dlt.h>。如果您还希望能够使用旧形式 <dlt.h>(出于向后兼容性原因,pkg-config 模块始终允许使用),您可以使用 CMake 选项 -DWITH_LEGACY_INCLUDE_PATH=On 配置 DLT,以便达到目的。
DLT使用 pkg-config
除了上面详述的 CMake 集成之外,还可以通过 pkg-config 使用 DLT。这也可以通过 CMake 的 PkgConfig 模块来完成。
PkgConfig 与“Modern CMake”的使用
在这里,您也让 PkgConfig 模块创建目标;然而,目标的名称由 PkgConfig 模块确定:
find_package(PkgConfig)
pkg_check_modules(DLT REQUIRED IMPORTED_TARGET automotive-dlt)
根据“Modern CMake”,不需要再添加的变量,而只有要添加到链接库的 CMake 目标:
target_link_libraries(myapp PRIVATE PkgConfig::DLT)
PkgConfig 与“Legacy CMake”(<3.0)的使用
在这里,您让 PkgConfig 模块只创建变量,而不创建目标:
find_package(PkgConfig)
pkg_check_modules(DLT REQUIRED automotive-dlt)
到 INCLUDE_DIRECTORIES(或者,从 CMake 2.8.11 开始,TARGET_INCLUDE_DIRECTORIES),添加
${DLT_INCLUDE_DIRS}
TARGET_LINK_LIBRARIES:
${DLT_LINK_LIBRARIES} (preferred, for CMake >= 3.12)
${DLT_LIBRARIES} (otherwise)
${DLT_LIBRARIES} 的内容不包括库的路径(例如 -L/path/to/lib),因此如果库驻留在不在链接器默认搜索路径上的位置,您要么必须添加LINK_DIRECTORIES 的路径:
link_directories(${DLT_LIBRARY_DIRS})
或者,不使用 ${DLT_LIBRARIES},而是使用 ${DLT_LDFLAGS},它结合了 ${DLT_LIBRARIES} 和 ${DLT_LIBRARY_DIRS}:
target_link_libraries(myapp ${DLT_LDFLAGS})
局限性
在 Android 上,应避免在 DLT 应用程序中定义 SIGUSR1,因为 DLT 库会阻止 SIGUSR1 在退出时终止管家线程。
测试工程源码
下面是我的工程:
dlt_test/
├── CMakeLists.txt
├── dlt_test.c
└── LICENSE
dlt_test.c
#include <dlt/dlt.h>
DLT_DECLARE_CONTEXT(ctx); /* declare context */
int main()
{
DLT_REGISTER_APP("TAPP", "Test Application for Logging");
// DLT_REGISTER_CONTEXT(ctx, "TES1", "Test Context for Logging");
// 向守护进程注册新的上下文,初始日志级别为DLT_LOG_VERBOSE
DLT_REGISTER_CONTEXT_LL_TS(ctx, "TES1", " First context ", DLT_LOG_VERBOSE, DLT_TRACE_STATUS_OFF);
// dlt_register_context_ll_ts(&ctx, "TES1", " First context ", DLT_LOG_VERBOSE, DLT_TRACE_STATUS_OFF);
/* … */
sleep(3);
DLT_LOG(ctx, DLT_LOG_VERBOSE , DLT_CSTRING("This is an error"));
/* … */
sleep(3);
DLT_UNREGISTER_CONTEXT(ctx);
DLT_UNREGISTER_APP();
return 0;
}
CMakeLists.txt
# for dlt_test
cmake_minimum_required (VERSION 3.0)
# The version number.
set (rtser_VERSION_MAJOR 0)
set (rtser_VERSION_MINOR 1)
set (rtser_VERSION_PATCH 0)
######################### Project settings #####################################
project(dlt_test)
#打印make详细信息
set(CMAKE_VERBOSE_MAKEFILE on)
#设置编译级别
add_definitions (-Wall -g)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0")
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -pthread")
#include( FindPkgConfig )
######## config inc&src&linklib settings and build #############################
find_package(PkgConfig)
pkg_check_modules(DLT REQUIRED IMPORTED_TARGET automotive-dlt)
include_directories(
)
link_directories(
)
file(GLOB SOURCES
"*.c"
)
add_executable(dlt_test
${SOURCES}
)
target_link_libraries(dlt_test PRIVATE PkgConfig::DLT)
#安装位置
set(CMAKE_INSTALL_PREFIX ${PROJECT_BINARY_DIR})
# build a CPack driven installer package
include (InstallRequiredSystemLibraries)
set (CPACK_RESOURCE_FILE_LICENSE
"${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set (CPACK_PACKAGE_VERSION_MAJOR "${rtser_VERSION_MAJOR}")
set (CPACK_PACKAGE_VERSION_MINOR "${rtser_VERSION_MINOR}")
set (CPACK_PACKAGE_VERSION_PATCH "${rtser_VERSION_PATCH}")
include (CPack)
######## Install targets ########
install(TARGETS dlt_test
RUNTIME DESTINATION /usr/bin/
)
记录的一般规则
需要在关键位置打印log,例如错误处理,不要乱用log,因为打印log需要消耗资源;
避免高频输出;
合并多条消息,请始终考虑每条日志消息都会产生一定的开销。所有必要的信息总是被组合在一起。此类log通常使用正则表达式 - 让工作更轻松!
不要使用 ASCII art;
不要使用 ASCII 创建图表;
避免在循环中跟踪;
日志级别的使用
DLT 中提供以下日志级别:
DLT_LOG_FATAL 致命的系统错误,应该很少见
DLT_LOG_ERROR 影响正确功能的错误
DLT_LOG_WARN 无法确保正确行为时发出警告
DLT_LOG_INFO 信息,提供高层次的理解
DLT_LOG_DEBUG 程序员详细调试信息
DLT_LOG_VERBOSE 程序员的详细调试信息
请注意默认日志级别设置为 INFO;这意味着记录在 INFO、WARN、ERROR 和 FATAL 中的消息将被记录。提示:可以通过设置环境变量来更改默认日志级别(请参阅 DLT 库 - 运行时配置)。
DLT API 使用
注册申请
重要的提示:因为 DLT是非异步线程安全函数,所以在子线程中不能使用。
DLT_REGISTER_APP 是异步的。建立 IPC 通道可能需要几毫秒的时间。因此,如果您在注册后立即登录,可能会丢失消息。在应用程序初始化期间,必须通过调用 DLT_REGISTER_APP() 尽早注册 DLT 应用程序。每个应用程序只允许调用一次 DLT_REGISTER_APP()。必须指定应用程序 ID(最多四个字符)并且在 ECU 中必须是唯一的。在这个例子中使用了“MAPP”。并且还可以指定应用程序的描述,这里是“用于日志记录的测试应用程序”。
int main(int argc, const char* argv[])
{
DLT_REGISTER_APP("MAPP","Test Application for Logging");
}
获取应用程序 ID
要获取应用程序 ID 值,请求分配一个至少 4 字节长度的字符数组并输入到函数调用中。
应用程序 ID 将存储在此输入字符数组中。
MACRO
DLT_GET_APPID(appid);
Function
dlt_get_appid(appid);
定义和注册所有日志上下文
可以根据需要定义尽可能多的上下文。这些上下文可以在不同的 C 或 CPP 文件中声明为上下文。但是每个上下文只允许声明一次。因此,必须为每个上下文使用唯一的变量名称。
DLT_DECLARE_CONTEXT(myContext1);
DLT_DECLARE_CONTEXT(myContext2);
DLT_DECLARE_CONTEXT(myContext3);
如果应使用来自另一个 C 或 CPP 文件的上下文,则可以通过调用来导入这些上下文:
DLT_IMPORT_CONTEXT(myContext1);
DLT_IMPORT_CONTEXT(myContext2);
DLT_IMPORT_CONTEXT(myContext3);
在注册应用程序并声明上下文后,需要在应用程序初始化期间尽早注册上下文。 DLT_REGISTER_CONTEXT() 不应在 DLT_REGISTER_APP() 之前调用。
在注册每个上下文期间,必须提供一个上下文 ID(最多四个字符)。在这个例子中使用了“TESX”。还可以提供上下文的描述;这里是“用于日志记录的测试上下文 X”。还可以使用宏 DLT_REGISTER_CONTEXT_LL_TS 使用预定义的日志级别和跟踪状态注册上下文。使用此方法注册第三个上下文。
int main(int argc, const char* argv[])
{
DLT_REGISTER_APP("MAPP","Test Application for Logging");
DLT_REGISTER_CONTEXT(myContext1,"TES1","Test Context 1 for Logging");
DLT_REGISTER_CONTEXT(myContext2,"TES2","Test Context 2 for Logging");
DLT_REGISTER_CONTEXT_LL_TS(myContext3, "TES3","Test Context 3 for Logging",
DLT_LOG_DEBUG, DLT_TRACE_STATUS_OFF);
}
注意:请注意,在 DLT 守护程序和应用程序之间的日志级别同步完成之前,可能需要一秒钟的时间。
注销上下文和应用程序
在终止应用程序注册的上下文之前,最后需要取消注册应用程序。
int main(int argc, const char* argv[])
{
/* business logic */
DLT_UNREGISTER_CONTEXT(myContext1);
DLT_UNREGISTER_CONTEXT(myContext2);
DLT_UNREGISTER_CONTEXT(myContext3);
DLT_UNREGISTER_APP();
return 0;
}
log命令
DLT 提供的函数允许使用任意数量的参数灵活构建消息。支持 Verbose 和 Non-Verbose 消息,具有不同的 API。使用这些函数发送消息需要多个函数调用,以启动消息构造、添加参数和发送消息。
下表显示了使用常量字符串和整数进行日志记录的所有 4 种类型的示例。
Verbose与非Verbose API
以下部分显示了所有 4 种日志类型的示例,例如一个字符串和一个整数。
MACRO
Verbose
DLT_LOG(ctx, DLT_LOG_INFO, DLT_STRING("ID: "), DLT_UINT32(123));
Non-Verbose
DLT_LOG_ID(ctx, DLT_LOG_INFO, 42 /* unique message ID */, DLT_STRING("ID: "),
DLT_UINT32(123));
Function
Verbose
if (dlt_user_log_write_start(&ctx, &ctxdata, DLT_LOG_INFO) > 0) {
dlt_user_log_write_string(&myctxdata, "ID: ");
dlt_user_log_write_uint32(&myctxdata, 123);
dlt_user_log_write_finish(&myctxdata);
}
Non-Verbose
if (dlt_user_log_write_start_id(&ctx, &ctxdata, DLT_LOG_INFO, 42) > 0) {
dlt_user_log_write_string(&myctxdata, "ID: ");
dlt_user_log_write_uint32(&myctxdata, 123);
dlt_user_log_write_finish(&myctxdata);
}
记录参数
可以使用以下参数类型。可以将多个参数添加到单个日志消息中。所有日志参数的大小加在一起不应超过 1390 字节,包括 DLT 消息头。
类型 说明
DLT_STRING(TEXT) 字符串
DLT_STRING_ATTR(TEXT,NAME) 字符串(带属性)
DLT_SIZED_STRING(TEXT,LENGTH) 已知长度的字符串
DLT_SIZED_STRING_ATTR(TEXT,LENGTH,NAME) 已知长度的字符串(带属性)
DLT_CSTRING(TEXT) 常量字符串(不以非详细模式发送)
DLT_CSTRING_ATTR(TEXT,NAME) 常量字符串(带属性;不以非详细模式发送)
DLT_SIZED_CSTRING(TEXT,LENGTH) 已知长度的常量字符串(不以非详细模式发送)
DLT_SIZED_CSTRING_ATTR(TEXT,LENGTH,NAME) 已知长度的常量字符串(带属性;不以非详细模式发送)
DLT_UTF8(TEXT) utf8 编码的字符串
DLT_UTF8_ATTR(TEXT,NAME) Utf8 编码字符串(带属性)
DLT_SIZED_UTF8(TEXT,LENGTH) 已知长度的 Utf8 编码字符串
DLT_SIZED_UTF8_ATTR(TEXT,LENGTH,NAME) 已知长度的 utf8 编码字符串(带属性)
DLT_RAW(BUF,LENGTH) 原始缓冲区
DLT_RAW_ATTR(BUF,LENGTH,NAME) 原始缓冲区(带属性)
DLT_INT(VAR) 整数变量,取决于平台
DLT_INT_ATTR(VAR,NAME,UNIT) 整数变量,取决于平台(带属性)
DLT_INT8(VAR) 整数 8 位变量
DLT_INT8_ATTR(VAR,NAME,UNIT) 整数 8 位变量(带属性)
DLT_INT16(VAR) 整数 16 位变量
DLT_INT16_ATTR(VAR,NAME,UNIT) 整数 16 位变量(带属性)
DLT_INT32(VAR) 整数 32 位变量
DLT_INT32_ATTR(VAR,NAME,UNIT) 整数 32 位变量(带属性)
DLT_INT64(VAR) 整数 64 位变量
DLT_INT64_ATTR(VAR,NAME,UNIT) 整数 64 位变量(带属性)
DLT_UINT(VAR) 无符号整数变量
DLT_UINT_ATTR(VAR,NAME,UNIT) 无符号整数变量(带属性)
DLT_UINT8(VAR) 无符号 8 位整数变量
DLT_UINT8_ATTR(VAR,NAME,UNIT) 无符号 8 位整数变量(带属性)
DLT_UINT16(VAR) 无符号 16 位整数变量
DLT_UINT16_ATTR(VAR,NAME,UNIT) 无符号 16 位整数变量(带属性)
DLT_UINT32(VAR) 无符号 32 位整数变量
DLT_UINT32_ATTR(VAR,NAME,UNIT) 无符号 32 位整数变量(带属性)
DLT_UINT64(VAR) 无符号 64 位整数变量
DLT_UINT64_ATTR(VAR,NAME,UNIT) 无符号 64 位整数变量(带属性)
DLT_BOOL(VAR) 布尔变量
DLT_BOOL_ATTR(VAR,NAME) 布尔变量(带属性)
DLT_FLOAT32(VAR) 浮点型 32 位变量
DLT_FLOAT32_ATTR(VAR,NAME,UNIT) 浮点型 32 位变量(带属性)
DLT_FLOAT64(VAR) Float 64 位变量
DLT_FLOAT64_ATTR(VAR,NAME,UNIT) Float 64 位变量(带属性)
DLT_HEX8(UINT_VAR) 8 位十六进制值
DLT_HEX16(UINT_VAR) 16 位十六进制值
DLT_HEX32(UINT_VAR) 32 位十六进制值
DLT_HEX64(UINT_VAR) 64 位十六进制值
DLT_BIN8(UINT_VAR) 8 位二进制值
DLT_BIN16(UINT_VAR 16 位二进制值
DLT_PTR(PTR_VAR) 用于打印指针的架构独立宏