C/C++日志写入接口

RK3568平台开发系列讲解(Logger篇)日志写入接口驱动 我们知道在Logger驱动程序模块中,定义了log_main、log_events和log_radio 3个日志缓冲区,分别对应3个设备文件/dev/log/main、/dev/log/events和/dev/log/radio。在上述代码中,通过4个日志缓冲区的前面3个ID对应了这3个设备文件的文件描述符。在Android系统中,通过宏来使用C/C++层的日志接口,在文件system/core/include/android/log.h中定义了日志的级别,具体实现代码如下所示。接口一般是在应用层编写。 阅读详情

Logger写接口

C/C++日志写入接口

Android系统就提供了三组常用的C/C++宏来封装日志写入接口, 这些宏有的在程序的非调试版本中只是一个空定义, 因此, 可以避免在程序的发布版本中输出日志。

LOGV、 LOGD、 LOGI、 LOGW和LOGE, 它们用来写入类型为main的日志记录;

SLOGV、 SLOGD、 SLOGI、 SLOGW和SLOGE, 它们用来写入类型为system的日志记录;

LOG_EVENT_INT、 LOG_EVENT_LONG和LOG_EVENT_STRING, 它们用来写入类型为events的日志记录

目录 :

~/Android/system/core/include
    cutils
        log.h

区分程序是调试版本还是发布版本 :

// system\core\include\cutils\log.h

#ifndef LOG_NDEBUG
#ifdef NDEBUG
#define LOG_NDEBUG 1
#else
// 调试版本中定义为0
#define LOG_NDEBUG 0
#endif
#endif
// system\core\include\cutils\log.h

// 当前编译单元的默认日志记录标签
#ifndef LOG_TAG
// 没有日志记录标签
#define LOG_TAG NULL
#endif

LOGV 、 LOGD 、 LOGI 、 LOGW 和LOGE

// system\core\include\cutils\log.h

// 用来写入类型为main的日志记录
// 优先级分别为 VERBOSE、 DEBUG、 INFO、 WARN  ERROR
#ifndef LOGV
#if LOG_NDEBUG
#define LOGV(...)   ((void)0)
#else
// 调试版本
#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#endif
#endif

/*
 * Simplified macro to send a debug log message using the current LOG_TAG.
 */
#ifndef LOGD
#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif

/*
 * Simplified macro to send an info log message using the current LOG_TAG.
 */
#ifndef LOGI
#define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif

#ifndef LOGW
#define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif

#ifndef LOGE
#define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif

使用宏LOG来实现日志写入功能 :

// system\core\include\cutils\log.h

// 实现日志写入功能
#ifndef LOG

// priority加上前缀“ ANDROID_ ”
// ANDROID_# # priority的参数都是类型为android_LogPriority的枚举值
#define LOG(priority, tag, ...) \
    LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#endif

#ifndef LOG_PRI

// 向Logger日志驱动程序中写入日志记录
#define LOG_PRI(priority, tag, ...) \
    android_printLog(priority, tag, __VA_ARGS__)
#endif

// 向Logger日志驱动程序中写入日志记录
#define android_printLog(prio, tag, fmt...) \
    __android_log_print(prio, tag, fmt)

android_LogPriority的枚举值 :

// system\core\include\android\log.h

typedef enum android_LogPriority {
    ANDROID_LOG_UNKNOWN = 0,
    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
    ANDROID_LOG_VERBOSE,
    ANDROID_LOG_DEBUG,
    ANDROID_LOG_INFO,
    ANDROID_LOG_WARN,
    ANDROID_LOG_ERROR,
    ANDROID_LOG_FATAL,
    ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
} android_LogPriority;

SLOGV 、 SLOGD 、 SLOGI 、 SLOGW 和SLOGE :

// system\core\include\cutils\log.h

// 用来写入类型为system的日志记录
// 写入的日志记录的优先级分别为VERBOSE、 DEBUG、 INFO、 WARN和ERROR
#ifndef SLOGV
#if LOG_NDEBUG
#define SLOGV(...)   ((void)0)
#else
// 调试版本
// 向Logger日志驱动程序中写入日志记录
#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#endif
#endif

#ifndef SLOGD
#define SLOGD(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#endif

#ifndef SLOGI
#define SLOGI(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
#endif

#ifndef SLOGW
#define SLOGW(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
#endif

#ifndef SLOGE
#define SLOGE(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
#endif

LOG_EVENT_INT 、 LOG_EVENT_LONG 和LOG_EVENT_STRING :

// system\core\include\cutils\log.h

typedef enum 
{
    // 整数
    EVENT_TYPE_INT      = 0,
    // 长整数
    EVENT_TYPE_LONG     = 1,
    // 字符串
    EVENT_TYPE_STRING   = 2,
    // 列表
    EVENT_TYPE_LIST     = 3,
} AndroidEventLogType;

// 写入类型为events的日志记录
#define LOG_EVENT_INT(_tag, _value) {                                       \
        int intBuf = _value;                                                \
        // 往Logger日志驱动程序中写入日志记录
        (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf,            \
            sizeof(intBuf));                                                \
    }

#define LOG_EVENT_LONG(_tag, _value) {                                      \
        long long longBuf = _value;                                         \
        // 往Logger日志驱动程序中写入日志记录
        (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf,          \
            sizeof(longBuf));                                               \
    }

// 往Logger日志驱动程序中写入一条内容为字符串值的日志记录
#define LOG_EVENT_STRING(_tag, _value)                                      \
// 空定义
    ((void) 0)  /* not implemented -- must combine len with string */

// 向Logger日志驱动程序中写入日志记录
#define android_printLog(prio, tag, fmt...) \
    __android_log_print(prio, tag, fmt)

Java日志写入接口

Android系统在应用程序框架层中定义了三个Java日志写入接口,

android.util.Log、android.util.Slog和android.util.EventLog, 写入的日志记录类型分别为main、 system和events。

这三个Java日志写入接口是通过JNI方法来调用日志库liblog提供的函数来实现日志记录的写入功能的。

android.util.Log :

// frameworks\base\core\java\android\util\Log.java

public final class Log 
{

    // 优先级分别为VERBOSE、 DEBUG、 INFO、 WARN  ERROR
    /**
     * Priority constant for the println method; use Log.v.
     */
    public static final int VERBOSE = 2;

    /**
     * Priority constant for the println method; use Log.d.
     */
    public static final int DEBUG = 3;

    /**
     * Priority constant for the println method; use Log.i.
     */
    public static final int INFO = 4;

    /**
     * Priority constant for the println method; use Log.w.
     */
    public static final int WARN = 5;

    /**
     * Priority constant for the println method; use Log.e.
     */
    public static final int ERROR = 6;

    /**
     * Priority constant for the println method.
     */
    public static final int ASSERT = 7;

    //...

    public static int v(String tag, String msg) 
    {
        // 实现日志记录写入功能
        return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
    }

    public static int v(String tag, String msg, Throwable tr) {
        return println_native(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr));
    }

    public static int d(String tag, String msg) {
        return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
    }

    public static int d(String tag, String msg, Throwable tr) {
        return println_native(LOG_ID_MAIN, DEBUG, tag, msg + '\n' + getStackTraceString(tr));
    }

    public static int i(String tag, String msg) {
        return println_native(LOG_ID_MAIN, INFO, tag, msg);
    }

    public static int i(String tag, String msg, Throwable tr) {
        return println_native(LOG_ID_MAIN, INFO, tag, msg + '\n' + getStackTraceString(tr));
    }

    public static int w(String tag, String msg) {
        return println_native(LOG_ID_MAIN, WARN, tag, msg);
    }

    public static int w(String tag, String msg, Throwable tr) {
        return println_native(LOG_ID_MAIN, WARN, tag, msg + '\n' + getStackTraceString(tr));
    }

    public static int w(String tag, Throwable tr) {
        return println_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));
    }

    public static int e(String tag, String msg) {
        return println_native(LOG_ID_MAIN, ERROR, tag, msg);
    }

    public static int e(String tag, String msg, Throwable tr) {
        return println_native(LOG_ID_MAIN, ERROR, tag, msg + '\n' + getStackTraceString(tr));
    }

    //...

    /** @hide */ public static final int LOG_ID_MAIN = 0;
    /** @hide */ public static final int LOG_ID_RADIO = 1;
    /** @hide */ public static final int LOG_ID_EVENTS = 2;
    /** @hide */ public static final int LOG_ID_SYSTEM = 3;

    // 实现日志记录写入功能
    /** @hide */ public static native int println_native(int bufID,
            int priority, String tag, String msg);
}
// frameworks\base\core\jni\android_util_Log.cpp

static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
        jint bufID, jint priority, jstring tagObj, jstring msgObj)
{
    const char* tag = NULL;
    const char* msg = NULL;

    // 检查写入的日志记录的内容msgObj是否为null
    if (msgObj == NULL) 
    {
        jclass npeClazz;

        npeClazz = env->FindClass("java/lang/NullPointerException");
        assert(npeClazz != NULL);

        env->ThrowNew(npeClazz, "println needs a message");
        return -1;
    }

    // 0、 1、 2和3四个值表示的日志记录的类型分别为main、radio、 events和system

    // 检查写入的日志记录的类型值是否位于0和LOG_ID_MAX之间
    if (bufID < 0 || bufID >= LOG_ID_MAX) 
    {
        jclass npeClazz;

        npeClazz = env->FindClass("java/lang/NullPointerException");
        assert(npeClazz != NULL);

        env->ThrowNew(npeClazz, "bad bufID");
        return -1;
    }

    if (tagObj != NULL)
        tag = env->GetStringUTFChars(tagObj, NULL);
    msg = env->GetStringUTFChars(msgObj, NULL);

    // 往Logger日志驱动程序中写入日志记录
    int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);

    if (tag != NULL)
        env->ReleaseStringUTFChars(tagObj, tag);
    env->ReleaseStringUTFChars(msgObj, msg);

    return res;
}

android.util.Slog :

// frameworks\base\core\java\android\util\Slog.java

// 注释中有一个“@hide”关键字, 表示这是一个隐藏接口,
//只在系统内部使用, 应用程序一般不应该使用该接口来写入日志记录
/**
 * @hide
 */
public final class Slog 
{
    // 写入的日志记录的类型为system

    private Slog() {
    }

    // 成员函数有v、 d、 i、w和e
    // 优先级分别为VERBOSE、 DEBUG、 INFO、 WARN和ERROR

    public static int v(String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg);
    }

    public static int v(String tag, String msg, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag,
                msg + '\n' + Log.getStackTraceString(tr));
    }

    public static int d(String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg);
    }

    public static int d(String tag, String msg, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag,
                msg + '\n' + Log.getStackTraceString(tr));
    }

    public static int i(String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);
    }

    public static int i(String tag, String msg, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag,
                msg + '\n' + Log.getStackTraceString(tr));
    }

    public static int w(String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, msg);
    }

    public static int w(String tag, String msg, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag,
                msg + '\n' + Log.getStackTraceString(tr));
    }

    public static int w(String tag, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, Log.getStackTraceString(tr));
    }

    public static int e(String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag, msg);
    }

    public static int e(String tag, String msg, Throwable tr) {
        return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag,
                msg + '\n' + Log.getStackTraceString(tr));
    }

    public static int println(int priority, String tag, String msg) {
        return Log.println_native(Log.LOG_ID_SYSTEM, priority, tag, msg);
    }
}

android.util.EventLog :

// frameworks\base\core\java\android\util\EventLog.java

public class EventLog 
{
    //...
    // 向Logger日志驱动程序中写入类型为events的日志记录, 
    // 这些日志记录的内容分别为整数、 长整数、 字符串和列表。

    // 整数
    public static native int writeEvent(int tag, int value);

    // 长整数
    public static native int writeEvent(int tag, long value);

    // 字符串
    public static native int writeEvent(int tag, String str);

    // 列表
    public static native int writeEvent(int tag, Object... list);

    //...
}

写入整数和长整数类型日志记录的JNI方法writeEvent的实现 :

//frameworks\base\core\jni\android_util_EventLog.cpp

static jint android_util_EventLog_writeEvent_Integer(JNIEnv* env, jobject clazz,
                                                     jint tag, jint value)
{
    // 向Logger日志驱动程序中写入日志记录
    // EVENT_TYPE_INT : 要写入的日志记录的内容为一个整数
    return android_btWriteLog(tag, EVENT_TYPE_INT, &value, sizeof(value));
}

static jint android_util_EventLog_writeEvent_Long(JNIEnv* env, jobject clazz,
                                                  jint tag, jlong value)
{
    // 向Logger日志驱动程序中写入日志记录
    // EVENT_TYPE_LONG :  要写入的日志记录的内容为一个长整数
    return android_btWriteLog(tag, EVENT_TYPE_LONG, &value, sizeof(value));
}

整数的日志记录的内存布局 :

image-20200720151030266

长整数的日志记录的内存布局 :

image-20200720151039785

宏android_btWriteLog的定义 :

// system\core\include\cutils\log.h

// 往Logger日志驱动程序中写入日志记录
#define android_btWriteLog(tag, type, payload, len) \
    __android_log_btwrite(tag, type, payload, len)

写入字符串类型日志记录的JNI方法writeEvent的实现 :

static jint android_util_EventLog_writeEvent_String(JNIEnv* env, jobject clazz,
                                   jint tag, jstring value) 
{
    uint8_t buf[MAX_EVENT_PAYLOAD];

    // Don't throw NPE -- I feel like it's sort of mean for a logging function
    // to be all crashy if you pass in NULL -- but make the NULL value explicit.
    const char *str = value != NULL ? env->GetStringUTFChars(value, NULL) : "NULL";
    jint len = strlen(str);
    const int max = sizeof(buf) - sizeof(len) - 2;  // Type byte, final newline
    if (len > max) 
    {
        len = max;
    }

    // 日志记录内容的类型为字符串
    buf[0] = EVENT_TYPE_STRING;
    // 字符串的长度
    memcpy(&buf[1], &len, sizeof(len));
    // 字符串内容
    memcpy(&buf[1 + sizeof(len)], str, len);
    // ‘\n'来结束该日志记录
    buf[1 + sizeof(len) + len] = '\n';

    if (value != NULL) env->ReleaseStringUTFChars(value, str);

    // 将日志记录写入到Logger日志驱动程序
    return android_bWriteLog(tag, buf, 2 + sizeof(len) + len);
}

内容为字符串的日志记录的内存布局 :

image-20200720151051111

第一个字段记录日志记录内容的类型为字符串,

第二个字段描述该字符串的长度,

第三个字段保存的是字符串内容,

第四个字段使用特殊字符‘\n’来结束该日志记录

写入列表类型日志记录的JNI方法writeEvent的实现 :

static jint android_util_EventLog_writeEvent_Array(JNIEnv* env, jobject clazz,
                                  jint tag, jobjectArray value) 
{
    if (value == NULL) 
    {
        return android_util_EventLog_writeEvent_String(env, clazz, tag, NULL);
    }

    uint8_t buf[MAX_EVENT_PAYLOAD];
    const size_t max = sizeof(buf) - 1;  // leave room for final newline
    size_t pos = 2;  // Save room for type tag & array count

    // 一个列表中最多可以包含255个元素
    jsize copied = 0, num = env->GetArrayLength(value);
    for (; copied < num && copied < 255; ++copied) 
    {
        // 依次取出列表中的元素, 且根据它们的值类型来组织缓冲区buf的内存布局
        jobject item = env->GetObjectArrayElement(value, copied);

        if (item == NULL || env->IsInstanceOf(item, gStringClass)) 
        {
            // 值类型为字符串
            if (pos + 1 + sizeof(jint) > max) 
                break;
            const char *str = item != NULL ? env->GetStringUTFChars((jstring) item, NULL) : "NULL";
            jint len = strlen(str);
            if (pos + 1 + sizeof(len) + len > max) 
                len = max - pos - 1 - sizeof(len);

            // EVENT_TYPE_STRING值
            buf[pos++] = EVENT_TYPE_STRING;
            // 字符串长度值
            memcpy(&buf[pos], &len, sizeof(len));
            // 字符串的内容
            memcpy(&buf[pos + sizeof(len)], str, len);

            pos += sizeof(len) + len;
            if (item != NULL) 
                env->ReleaseStringUTFChars((jstring) item, str);
        } 
        else if (env->IsInstanceOf(item, gIntegerClass)) 
        {
            // 值类型为整数

            jint intVal = env->GetIntField(item, gIntegerValueID);
            if (pos + 1 + sizeof(intVal) > max) 
                break;

            // 第一个字节设置为一个EVENT_TYPE_INT值
            buf[pos++] = EVENT_TYPE_INT;
            // 整数值
            memcpy(&buf[pos], &intVal, sizeof(intVal));
            pos += sizeof(intVal);
        } 
        else if (env->IsInstanceOf(item, gLongClass)) 
        {
            // 值类型为长整数

            jlong longVal = env->GetLongField(item, gLongValueID);
            if (pos + 1 + sizeof(longVal) > max) 
                break;

            // buf的内容就为一个EVENT_TYPE_LONG值
            buf[pos++] = EVENT_TYPE_LONG;
            // 长整数值
            memcpy(&buf[pos], &longVal, sizeof(longVal));
            pos += sizeof(longVal);
        } 
        else 
        {
            // 抛出一个异常

            jniThrowException(env,
                    "java/lang/IllegalArgumentException",
                    "Invalid payload item type");
            return -1;
        }
        env->DeleteLocalRef(item);
    }

    // 列表类型的日志记录
    buf[0] = EVENT_TYPE_LIST;
    // 列表元素个数
    buf[1] = copied;
    // 日志记录的结束标志
    buf[pos++] = '\n';
    // 将日志记录写入到Logger日志驱动程序中
    return android_bWriteLog(tag, buf, pos);
}

内容为列表的日志记录的内存布局 :

image-20200720151100719

Android日志系统驱动程序Logger源代码分析 我们知道,在Android系统中,提供了一个轻量级的日志系统,这个日志系统是以驱动程序的形式实现在内核空间的,而在用户空间分别提供了Java接口和C/C++接口来使用这个日志系统,取决于你编写的是Android应用程序还是系统组件。在前面的文章浅谈Android系统开发中LOG的使用中,已经简要地介绍了在Android应用程序开发中Log的使用方法,在这一篇文章中,我们将更进一步地分析Logger 阅读详情

相关推荐

C/C++工程接入InfluxDB:接口选型、Line Protocol封装与批量写入实践

时序数据库是监控与物联网场景中最常见的数据存储底座,而InfluxDB凭借其HTTP REST API和简洁的Line Protocol文本协议,成为许多C/C++服务上报指标的首选目标。与MySQL等数据库不同,InfluxDB没有原生二进制行协议,所有客户端本质上都是围绕HTTP接口和协议字符串的拼接与发送展开。理解这一点,就能明白influxdb_c和C++11 header-only客户端在接口风格与资源管理上的差异:前者适合C工程和ABI稳定场景,后者以builder链式调用和RAII思想简化编码

weixin_31569671的博客 215

android 日志系统中LOG_NDEBUG宏的作用

log.h文件: /*  * Normally we strip ALOGV (VERBOSE messages) from release builds.  * You can modify this (for example with "#define LOG_NDEBUG 0"  * at the top of your source file) to change that beh

bob_fly1984的专栏 1万+

Unity 常见问题 之 打包编译的报错 Unable to sign the application; please provide passwords!

Unity 常见问题 之 打包编译的报错 Unable to sign the application; please provide passwords! 目录 Unity 常见问题 之 打包编译的报错 Unable to sign the application; please provide passwords! 一、简单介绍 二、问题描述 三、原因分析 四、可能的解决方法 方法一:取消 Keystore 设置即可 方法二:重新添加 Keystore 相关设置 一...

仙魁XAN 1万+

android 4.1 中的 log

Android4.1中,log有了一些打印,具体可以参考system/core/include/cutils/log.h 1. 下面是控制哪些log输出的宏 /*  * Normally we strip ALOGV (VERBOSE messages) from release builds.  * You can modify this (for example with "#defi

flyingqr的专栏 6255

别再死记硬背了!用Amesim HCD库亲手搭建单向阀、锥阀、球阀模型(附详细参数设置)

本文详细介绍了如何使用Amesim HCD库从零构建单向阀、锥阀和球阀的仿真模型,帮助工程师摆脱死记硬背,掌握物理建模的核心技巧。通过实战案例和参数设置指南,读者将学会如何优化阀门动态响应、验证流量特性以及处理旋转运动等独特挑战,提升液压系统设计效率。

weixin_29248611的博客 501

【数据库】银行/信贷业务测试工作中涉及的数据库表格

银行/信贷业务测试工作中涉及的数据库表格,金融信贷测试人必看!

孤之鹜的博客 1173

Android Log等级输出方法

以下是Android9.0 打开ALOGV/ALOGI/ALOGD日志输出的方法。#define LOG_NDEBUG 1//这里之前是0改为1就好了。

Jimmy的专栏 505

YooAsset 2.2.12四层加密架构:跨平台资源安全实战指南

AssetBundle加密是Unity热更新安全的核心环节,其本质是通过算法、密钥管理与生命周期协同实现资源全链路防护。传统方案常陷于‘打包后加密’的误区,导致内存开销大、跨平台兼容差、逆向防护弱。YooAsset 2.2.12提出打包层、传输层、加载层、运行层四层防护架构,将加密前置到资源序列化前,并依托BundleHeader实现动态算法协商与密钥分发,显著提升iOS/Android/WebGL三端一致性。该方案兼顾AES-128等通用算法与SM4等国密支持,同时通过IMemoryDecryptor实现

weixin_32428571的博客 416

打开Android C文件中的LOG

打开Android C文件中的LOG 可以一次打开LOGV,LOGI,LOGD,也可以分别打开: #undef NDEBUG       //打开LOGV/LOGI/LOGD #define LOG_NDEBUG   0  //打开LOGV #define LOG_NIDEBUG  0  //打开LOGI #define LOG_NDDEBUG 0 //打开LOGD

oman111的专栏 1101

DirectX修复工具使用技巧之二——手动修复C++创建失败的文件

最后更新:2021-2-25 随着V4.0正式版的发布,近来有部分用户来咨询如何解决C++文件创建失败的问题。在此我将以解决最常见的C++2015-2019文件创建失败为例,向大家演示一下在线修复的方法,其他C++或文件的方法大同小异。此次操作以Windows 7为例,其他系统相应参考即可。 首先,如果希望程序能手动在线修复创建失败的失败,请首先确定您使用的V4.0.2版或更高版本,老版本不支持此功能。查看程序版本的方式可以把鼠标放在DirectX Repair.e...

VBcom的专栏 6万+

OnlyOffice Docker部署踩坑实录:解决Nginx反向代理导致的editor.bin下载失败

本文详细解析了在Docker中部署OnlyOffice时,因Nginx反向代理配置不当导致editor.bin下载失败的常见问题。通过分析问题根源,提供了从Nginx代理配置、Docker网络设置到OnlyOffice容器内部调整的完整解决方案,并分享了实战调试技巧与性能优化建议,帮助用户彻底解决这一部署难题。

wine的专栏 524

c++生成so调用LOGI

前一阵为android程序写了一些c++生成的so文件,但是单文件没法单步调试,导致调用时出现问题后不好定位代码,于是想到在C++程序的关键点打LOGI标记,刚开始遇到些问题,后来终于解决了,现在把解决方法记录下来,方便自己,也方便别人。     首先,在C++或c的开始定义宏,如下: // 定义info信息 #define LOGI(...) __android_log_print(AND

wincehxd的博客 1525

【YOLOv12[基础]】热力图可视化实践 | 脚本升级 | 优化可视化效果 | 论文必备 | GradCAMPlusPlus, GradCAM, XGradCAM, EigenCAM等

热力图可视化实践 | 脚本升级 | 优化可视化效果 | 论文必备 GradCAMPlusPlus, GradCAM, XGradCAM, EigenCAM等

静谧、淡雅 895

C/C++常用接口: 添加写入日志到文件的功能

#include <stdio.h> #include <stdarg.h> #include <time.h> int write_log (FILE* pFile, const char *format, ...) { va_list arg; int done; va_start (arg, format); time_t time_lo...

Stay Hungry Stay Foolish 1600

基于深度学习的茶叶等级检测识别系统:YOLOv10 + UI界面 + 数据集

YOLOv10是YOLO系列的最新版本,继承了YOLO模型的优点,并对速度和精度进行了优化。YOLOv10在目标检测中使用卷积神经网络(CNN)作为基础,通过改进网络结构和优化训练策略,实现了在更低的延迟下进行高精度的目标检测。本文详细介绍了基于YOLOv10的茶叶等级检测与识别系统的设计与实现过程。我们从数据集准备、模型训练与推理、UI界面设计等多个方面进行了详细讲解,并给出了相应的代码实现。通过本系统,我们可以高效地自动检测茶叶的等级,并通过UI界面直观地展示结果。

YOLO 765

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Android日志系统分析之开篇

<!-- @page {margin:0.79in} p {margin-bottom:0.08in} --> 在android系统中, 提供了一个轻量级的日志系统。该日志系统基于在内核中实现的一个字符设备驱动 logger(misc字符设备驱动)。通过该字

山庄来客的专栏 2873

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

开源日志库log4cpp & muduo

c++开源日志库log4cpp、muduo

大菜菜的博客 832

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

为什么不要在android log中打印中文_Android日志系统分析

以前做项目的时候,使用xposed对使用过爱加密加固过的应用进行hook时,出现过完全打印不出日志的情况,当时完全不知道是什么原因导致的。看到看雪的这篇文章《解决爱加密加固之后使用xposed hook的时候log打印不出来的问题》,加上最近又在看一些系统内核的东西,所以有了这篇文章。· 本文篇幅较长,可以先马再阅读哦~Android提供的Logger日志系统是基于内核的Logger日志驱动程序实...

weixin_39847034的博客 382

Java native日志系统_Android Logger日志系统

文件夹前言该篇文章是我的读书和实践笔记。參考的是《Android系统源代码情景分析》。执行时库层日志库——liblogAndroid系统在执行时库层提供了一个用来和Logger日志驱动程序进行交互的日志库liblog。通过日志库liblog提供的接口。应用程序就能够方便地往Logger日志驱动程序中写入日志记录。位于执行时库层的C/C++日志写入接口和位于应用程序框架层的Java日志写入接口都是通...

weixin_33549415的博客 529

Android Logger日志系统

目录目录 前言 运行时库层日志库liblog 源码分析 CC日志写入接口 Java日志写入接口前言该篇文章是我的读书和实践笔记。参考的是《Android系统源代码情景分析》。运行时库层日志库——liblogAndroid系统在运行时库层提供了一个用来和Logger日志驱动程序进行交互的日志库liblog。通过日志库liblog提供的接口,应用程序就可以方便地往Logger日志驱动程序中写入日志记录。

小一的专栏 6770

在Android的c/c++代码中使用LOG

在Android中,Java代码通过android.util.Log输出Log信息,同样的本地c/c++代码也提供了相对应而且是更多的接口。Android直接在头文件(system/core/include/cutils/log.h)里定义了一些日志输出的宏,这些宏比android.util.Log提供了更多的日志输出接口。因此,使用这些宏,就可以进行和java代码中一样的日志输出。宏LOGD()

JAVAweb学习 824

安卓进阶六:Logger日志系统

本文是学习罗升阳大佬的《安卓系统源代码情景分析》的笔记和总结 一、日志驱动、运行时库层日志库、c/c++日志写入接口、java日志写入接口总结 二、Logcat总结 一、日志驱动、运行时库层日志库、c/c++日志写入接口、java日志写入接口总结 由这张图: 1、注意LOGV、LOGD这些宏操作即c/c++日志写入接口是在Runtime Library 层的,不是在硬件抽象层 2、注意通过c/c++日志接口、java日志接口将日志记录write到Logger驱动的目的是将它们保存起来,这

我的安卓之路 752

Android源码——Logger日志系统

Android的Logger日志系统是基于内核中的Logger日志驱动程序实现的。 日志保存在内核空间中 缓冲区保存日志 分类方法:日志的类型 + 日志的输出量 日志类型: main sysytem radio events 以上四种日志分别通过以下四个设备文件来访问: /dev/log/main /d...

weixin_30715523的博客 182
上一篇: 安卓硬件驱动开发
下一篇: Linux\_c输出
onnx
博客等级 码龄5年 1万+粉丝 416原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

onnx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值