在 Android 系统中,C/C++ 代码的日志输出与我们熟悉的 Java/Kotlin 的 Log 类不同,它使用的是一套源自 Linux 内核的、更底层的日志系统,这套系统通过一个名为 liblog 的库来实现,其中最核心的函数是 __android_log_write 和一系列封装好的宏。

核心函数:__android_log_write
这是最基础、最核心的日志写入函数。
函数原型
#include <android/log.h> int __android_log_write(int priority, const char* tag, const char* text);
参数说明
priority: 日志的优先级/级别,这是一个整数值,系统定义了几个宏来代表不同的级别。tag: 日志标签,这是一个字符串,用于标识日志的来源(通常是你的模块名或类名),方便在 logcat 中过滤。强烈建议使用一个简短且唯一的标签。text: 日志的具体内容,这是一个字符串。
日志优先级 (Priority)
Android 定义了以下日志级别,按重要性从高到低排序:
| 宏定义 | 值 | 含义 | 描述 |
|---|---|---|---|
ANDROID_LOG_FATAL |
6 | 致命 | 将导致进程终止的严重错误。 |
ANDROID_LOG_ERROR |
3 | 错误 | 普通错误。 |
ANDROID_LOG_WARN |
4 | 警告 | 潜在问题。 |
ANDROID_LOG_INFO |
2 | 信息 | 一般性信息。 |
ANDROID_LOG_DEBUG |
1 | 调试 | 调试信息,仅在调试版本中可用。 |
ANDROID_LOG_VERBOSE |
5 | 详细 | 冗余的信息,非常多。 |
ANDROID_LOG_SILENT |
0 | 静默 | 最低级别,什么都不会打印。 |
注意:
ANDROID_LOG_VERBOSE的值是 5,比WARN(4) 还高,这意味着它的日志级别更低,打印的内容更多,级别数值越小,日志越重要。
便捷宏:LOGV, LOGD, LOGI, LOGW, LOGE, LOGF
每次都写 __android_log_write 非常繁琐,Android 提供了一系列预定义的宏,它们会自动填充 tag 和优先级,让代码更简洁、更易读。

这些宏通常这样定义:
#define LOG_TAG "MyNativeModule" // 定义一个全局的日志标签 #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) #define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
__android_log_print是__android_log_write的一个增强版,它支持printf风格的格式化字符串,非常实用。
完整实践步骤
下面我们通过一个完整的例子,展示如何在 C/C++ 代码中打印日志,并将其集成到 Android Studio 项目中。
步骤 1: 创建一个 Native C 模块
- 在 Android Studio 中,右键你的模块 -> New -> Folder -> JNI Folder。
- 在
app/src/main/jni目录下,创建一个 C 源文件,native-log.c。
步骤 2: 编写 C 代码
在 native-log.c 中,包含头文件并使用宏来打印日志。

#include <jni.h>
#include <android/log.h>
// 定义一个全局的日志标签,方便统一管理
#define LOG_TAG "MyNativeLog"
// 定义日志宏
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
// 这是一个 JNI 方法,我们在这里演示日志打印
JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
// 使用不同的级别打印日志
LOGD("This is a DEBUG message from C.");
LOGD("The value of an integer is: %d", 42);
LOGE("This is an ERROR message! Something might be wrong.");
// 你也可以直接使用底层函数
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "This is an INFO message using the low-level function.");
return (*env)->NewStringUTF(env, "Hello from C with Logs!");
}
代码解释:
#include <android/log.h>: 包含日志功能头文件。#define LOG_TAG "MyNativeLog": 定义一个标签,所有后续的日志都会带上这个标签。LOGD(...)和LOGE(...): 定义了两个便捷宏,分别对应 DEBUG 和 ERROR 级别。__VA_ARGS__是一个可变参数宏,会把所有传入的参数原封不动地传递给后面的函数。__android_log_print: 用于格式化输出,就像printf一样。__android_log_write: 直接输出字符串。
步骤 3: 配置 CMakeLists.txt
为了让编译器找到 <android/log.h> 并链接 log 库,你需要修改 app/CMakeLists.txt 文件。
# 在你的 CMakeLists.txt 中添加以下内容
# 查找并添加 log 库
find_library(log-lib log)
# 添加你的 native 源文件
add_library(
native-lib # 库名
SHARED # 共享库
native-lib.cpp # 你的 C++ 源文件
native-log.c # 添加你的 C 源文件
)
# 链接 log 库到你的 native-lib
target_link_libraries(
native-lib
${log-lib}
)
关键点:
find_library(log-lib log): 查找 Android 系统提供的log库,并将其路径存入log-lib变量。add_library(...): 确保将你的.c文件(native-log.c)添加到编译列表中。target_link_libraries(...): 将log-lib链接到你的库上。这一步是必不可少的,否则链接时会报错。
步骤 4: 在 Java/Kotlin 代码中调用
在你的 MainActivity.java (或 .kt) 中,加载库并调用 JNI 方法。
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.lang.reflect.Method;
public class MainActivity extends AppCompatActivity {
// 加载库的名字要和 CMakeLists.txt 中定义的库名一致
static {
System.loadLibrary("native-lib");
}
// 声明一个 native 方法
public native String stringFromJNI();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
}
步骤 5: 查看日志 (logcat)
- 在 Android Studio 底部,打开 Logcat 窗口。
- 在过滤器中,选择你的设备或模拟器。
- 在 Log Tag 输入框中,输入你在 C 代码中定义的标签,即
MyNativeLog。 - 你还可以在 Show only selected application 的下拉菜单中选择你的 App,以过滤掉其他应用的日志。
现在运行你的 App,你就能在 Logcat 中看到类似下面这样的输出:
// D/MyNativeLog: This is a DEBUG message from C.
// D/MyNativeLog: The value of an integer is: 42
// E/MyNativeLog: This is an ERROR message! Something might be wrong.
// I/MyNativeLog: This is an INFO message using the low-level function.
高级技巧与注意事项
条件编译 (Debug vs Release)
在 Release 版本中,通常我们不希望打印大量的调试日志,可以通过宏来控制。
在 CMakeLists.txt 中,你可以根据构建类型定义宏:
# 在 CMakeLists.txt 顶部添加
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
endif()
# 在 add_library 之前添加
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_definitions(LOG_DEBUG=1)
else()
add_compile_definitions(LOG_DEBUG=0)
endif()
然后在你的 C 代码中这样使用:
#ifdef LOG_DEBUG
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#else
#define LOGD(...) // Do nothing
#endif
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
// 现在在 Debug 模式下 LOGD 会打印,在 Release 模式下会被忽略
LOGD("This will only be printed in Debug builds.");
LOGE("This will always be printed.");
日志缓冲区
在高频率日志打印的场景下(如游戏循环),频繁调用 JNI 和日志系统可能会影响性能,一个优化技巧是先将日志信息写入一个内存缓冲区,然后定期(例如每帧)一次性将缓冲区的内容全部打印出来。
崩溃日志
当 C/C++ 代码发生段错误 (Segmentation Fault) 时,Java 层的 try-catch 是无法捕获的,这时,日志系统会打印出包含堆栈信息的 FATAL 级别日志,这对于分析崩溃原因至关重要,你可以通过 Android Studio 的 Analyze -> Inspect Code 或其他工具来捕获和分析这类崩溃日志。
| 功能 | Java/Kotlin | C/C++ |
|---|---|---|
| 核心类/函数 | android.util.Log |
android/log.h |
| 打印方法 | Log.d(tag, msg) |
__android_log_print(...) 或 LOGD(...) |
| 日志级别 | VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT |
ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ..., ANDROID_LOG_FATAL |
| 字符串,作为第一个参数 | 宏定义 #define LOG_TAG "..." |
|
| 链接库 | 不需要 | 必须在 CMakeLists.txt 中链接 log 库 |
在 C/C++ 中使用日志,包含头文件、定义标签宏、使用便捷宏 和 在 CMake 中链接 log 库 是四个关键步骤,熟练使用 C 日志将极大地方便你进行 NDK 开发和调试。
