【NDK】封装日志库

0x1 需求

  • 供c++、Java调用
  • 控制台输出
  • 文件输出(文件大小)
  • 设置日志等级

0x2 C++

0x21 LogUtils.h

//
// Created by 后端码匠 on 2022/11/30.
//#ifndef NDKPRACTICE_LOGUTILS_H
#define NDKPRACTICE_LOGUTILS_H#include <stdio.h>
#include <android/log.h>
#include <errno.h>#define  LOG_TAG    "km_media_log"#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 LOG_TEXT_MAX_LENGTH        (1024)  //  单条日志大小
#define LOG_FILE_MAX_SIZE    (1024*1024*3) //  文件最大为3MBenum {LOG_LEVEL_NONE = 0,LOG_LEVEL_ERR = 1,LOG_LEVEL_WARNING = 2,LOG_LEVEL_INFO = 3,LOG_LEVEL_DEBUG = 4
};#ifdef  __cplusplus
extern "C" {#endif/*** 初始化日志选项* @param pFile* @param filename* @param logLevel* @param printScreen* @return*/
int LogInit(const char *pFile, const char *filename, int logLevel, int printScreen);/*** 日志处理* @param level* @param strFormat* @param ...*/
void WriteTextLog(int level, const char *strFormat, ...);/*** 向文件中写入日志* @param level* @param log*/
void WriteTextLogBottom(int level, const char *log);/*** 关闭日志库*/
void LogClose();#ifdef __cplusplus
}
#endif#endif //NDKPRACTICE_LOGUTILS_H

0x22 LogUtils.cpp

//
// Created by 后端码匠 on 2022/11/30.
//#include <cstdio>
#include <cstdarg>
#include <ctime>
#include <sys/stat.h>
#include <cstring>
#include <sys/types.h>
#include <cassert>
#include <string>
#include <sstream>
#include <vector>
#include <iostream>
#include <algorithm>
#include <sys/time.h>
#include <cstring>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>#include "LogUtils.h"char LOG_FILE_NAME[100] = "km_media_log.txt";   //日志默认名称
// 日志级别
int g_log_file_level = LOG_LEVEL_NONE;
int g_log_screen_level = LOG_LEVEL_NONE;long g_RollingPtr = 0;// 文件路径
static std::string g_logFilePath;int LogInit(const char *pFile, const char *filename, int logLevel, int printScreen) {g_RollingPtr = 0;g_log_file_level = logLevel;g_log_screen_level = printScreen;if (filename != nullptr) {strcpy(LOG_FILE_NAME, filename);}if (pFile != nullptr) {g_logFilePath = std::string(pFile) + "/" + LOG_FILE_NAME;} else {g_logFilePath = LOG_FILE_NAME;}return 0;
}char g_log_info[LOG_TEXT_MAX_LENGTH + 100];void WriteTextLog(int level, const char *strFormat, ...) {if (level > g_log_file_level && level > g_log_screen_level) {return;}time_t now;char timeStr[20];char temBuf[LOG_TEXT_MAX_LENGTH];time(&now);strftime(timeStr, sizeof(timeStr), "%Y-%m-%d %H:%M:%S", localtime(&now));va_list args;va_start(args, strFormat);vsnprintf(temBuf, sizeof(temBuf) - 1, strFormat, args);va_end(args);switch (level) {case LOG_LEVEL_DEBUG:LOGD("%s", g_log_info);sprintf(g_log_info, "%s [DEBUG] %s\n", timeStr, temBuf);break;case LOG_LEVEL_INFO:LOGI("%s", g_log_info);sprintf(g_log_info, "%s [INFO] %s\n", timeStr, temBuf);break;case LOG_LEVEL_WARNING:LOGW("%s", g_log_info);sprintf(g_log_info, "%s [WARN] %s\n", timeStr, temBuf);break;case LOG_LEVEL_ERR:LOGE("%s", g_log_info);sprintf(g_log_info, "%s [ERROR] %s\n", timeStr, temBuf);break;default:LOGI("%s", g_log_info);sprintf(g_log_info, "%s [NONE] %s\n", timeStr, temBuf);break;}if (level <= g_log_file_level && !g_logFilePath.empty()) {WriteTextLogBottom(level, g_log_info);}
}void WriteTextLogBottom(int level, const char *log) {if (level <= g_log_file_level) {FILE *fp;struct stat info{};if (stat(g_logFilePath.c_str(), &info) != 0) {g_RollingPtr = 0;fp = fopen(g_logFilePath.c_str(), "we"); // create fileif (fp == nullptr) {LOGE("%s, fopen(w) %s fail, err:%d", __func__, g_logFilePath.c_str(), errno);return;}fprintf(fp, "%s, stat fail create logfile, errno:%d\n", __func__, errno);fprintf(fp, "%s", log);fclose(fp);return;}if (info.st_size >= LOG_FILE_MAX_SIZE) // loop write{// 这里使用复写的方式,保证日志文件不会超过 LOG_FILE_MAX_SIZEfp = fopen(g_logFilePath.c_str(), "r+");if (nullptr == fp) {LOGE("%s, fopen(r+) %s fail, size:%ld, err:%d", __func__, g_logFilePath.c_str(),info.st_size, errno);return;}if (fseek(fp, g_RollingPtr, SEEK_SET) < 0) {fclose(fp);return;}g_RollingPtr += strlen(log);if (g_RollingPtr > info.st_size) {g_RollingPtr = 0;}} else {fp = fopen(g_logFilePath.c_str(), "a");if (fp == nullptr) {LOGE("%s, fopen(a) %s fail, size:%ld, err:%d", __func__, g_logFilePath.c_str(),info.st_size, errno);return;}}fprintf(fp, "%s", log);fclose(fp);}
}void LogClose() {g_log_file_level = LOG_LEVEL_NONE;g_log_screen_level = LOG_LEVEL_NONE;
}

0x3 Java

0x31 LogUtils

//
// Created by 后端码匠 on 2022/11/30.
//package cn.com.codingce.ndkpractice.utils;import android.content.Context;
import android.os.Environment;
import android.util.Log;import java.io.File;public class LogUtils {private static Context globalAplicationContext = null;private static String PATH_LOGCAT = null;public enum LogLevel {LOG_LEVEL_NONE,LOG_LEVEL_ERR,LOG_LEVEL_WARNING,LOG_LEVEL_INFO,LOG_LEVEL_DEBUG}public static void init() {if (globalAplicationContext == null) return;boolean obtainSDcardAccess = false;try {obtainSDcardAccess = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);} catch (Exception e) {}if (obtainSDcardAccess) {// 优先保存到SD卡中final File externalFilesDir = globalAplicationContext.getExternalFilesDir(null);if (externalFilesDir != null) {PATH_LOGCAT = externalFilesDir.getAbsolutePath() + File.separator + "kmsdk";} else {// 如果SD卡不存在,就保存到本应用的目录下PATH_LOGCAT = globalAplicationContext.getFilesDir().getAbsolutePath()+ File.separator + "kmsdk";}} else {// 如果SD卡不存在,就保存到本应用的目录下PATH_LOGCAT = globalAplicationContext.getFilesDir().getAbsolutePath()+ File.separator + "kmsdk";}File file = new File(PATH_LOGCAT);if (!file.exists()) {file.mkdirs();}logInit(PATH_LOGCAT, "km_media_log.txt", 4, 4);Log.e("LogUtils", "cur file dir is:" + file.toString());}public synchronized static void setApplicationContext(Context aplicationContext) {globalAplicationContext = aplicationContext.getApplicationContext();}// 日志类初始化public static native void logInit(String logFilePath, String logName, int logfileLevel, int logScreenLevel);public static native void logJni(int logLevel, String content);public static native void logClose();}

0x32 Native

//
// Created by 后端码匠 on 2022/11/30.
//#include <jni.h>
#include <string>
#include "LogUtils.h"#define diagnosis_assert(...) assert(__VA_ARGS__)int ret = -1;
static void nativeLogUtilsRegisterNatives(JNIEnv *jniEnv);JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {JNIEnv *jniEnv{nullptr};if (vm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6) != JNI_OK) {diagnosis_assert(!"JNI version error!");return JNI_EVERSION;}nativeLogUtilsRegisterNatives(jniEnv);return JNI_VERSION_1_6;
}static void localLogInit(JNIEnv *env, jclass clazz,jstring logFilePath, jstring logName,jint logfile_level, jint log_screen_level) {if (ret != 0) {const char *path = env->GetStringUTFChars(logFilePath, JNI_FALSE);const char *name = env->GetStringUTFChars(logName, JNI_FALSE);int fileLevel = logfile_level;int screenLevel = log_screen_level;ret = LogInit(path, name, fileLevel, screenLevel);env->ReleaseStringUTFChars(logFilePath, path);env->ReleaseStringUTFChars(logName, name);}
}static void logJni(JNIEnv *env, jclass clazz, jint _level,jstring _str) {if (ret != 0) {LOGE("log error! LogInit need");return;}const char *str = env->GetStringUTFChars(_str, JNI_FALSE);WriteTextLog(_level, str);env->ReleaseStringUTFChars(_str, str);
}static void logClose(JNIEnv *env, jclass clazz) {LogClose();ret = -1;
}static JNINativeMethod nativeUtilsMethods[] = {{"logInit",  "(Ljava/lang/String;Ljava/lang/String;II)V", (void *) localLogInit},{"logJni",   "(ILjava/lang/String;)V",                    (void *) logJni},{"logClose", "()V",                                       (void *) logClose},
};static void nativeLogUtilsRegisterNatives(JNIEnv *jniEnv) {if (jniEnv == nullptr) {return;}jclass clazz = nullptr;do {clazz = jniEnv->FindClass("cn/com/codingce/ndkpractice/utils/LogUtils");if (clazz == nullptr) {diagnosis_assert(!"FindClass LogUtils error!");break;}if (jniEnv->RegisterNatives(clazz, nativeUtilsMethods,std::extent<decltype(nativeUtilsMethods)>::value) != 0) {diagnosis_assert(!"RegisterNatives error!");break;}} while (false);if (jniEnv->ExceptionCheck() == JNI_TRUE) {jniEnv->ExceptionClear();}if (clazz != nullptr) {jniEnv->DeleteLocalRef(clazz);}
}

【Android】NDK封装日志库相关推荐

  1. Android NDK 编译 三方库记录 及 jni库封装问题

    因工作需求, 要将原先的c++库跨平台编译,在Android上运行, 其依赖了几个第三方库, 也需要一起编译, 在此做个记录 所需工具(centos 系统上完成) 1. cmake 3.15.6 2. ...

  2. Android NDK 减少 so 库体积方法总结

    1. 背景 基于亚马逊 AVS Device SDK 改造的全链路语音 SDK 最终编译的动态库有几十个,单架构动态库大小有几十兆,之前在Iot设备中勉强跑着,但是这个体积对于手机应用来说是致命的,各 ...

  3. Android NDK下STD库share static方式加载 (so库混乱调用问题)

    结论 -DANDROID_STL=c++_shared 有该选项 share方式加载STL,打包的so库不含有STD代码 没该选项 static方式加载STL. // 打so库的build.gradl ...

  4. android jni不适用ndk,Android NDK编译之undefined reference to 'JNI_CreateJavaVM'

    利用Android NDK编译动态库,在C文件中调用了两个JNI函数:JNI_GetDefaultJavaVMInitArgs和JNI_CreateJavaVM.编译的时候始终报以下错误: XXX: ...

  5. 【Android NDK 开发】NDK C/C++ 代码崩溃调试 - Tombstone 报错信息日志文件分析 ( 使用 addr2line 命令行工具查找动态库中的报错代码位置 )

    文章目录 一.从 Tombstone 报错日志中查找报错动态库 二.addr2line 命令行工具使用 64 位动态库使用的 aarch64-linux-android-addr2line.exe 工 ...

  6. 【Android NDK 开发】Android Studio 使用 CMake 导入动态库 ( 构建脚本路径配置 | 指定动态库查找路径 | 链接动态库 )

    文章目录 I . CMake 引入动态库与静态库区别 II . Android Studio 中 CMake 引入动态库流程 III . 指定动态库查找路径 IV . 链接函数库 V . 完整代码示例 ...

  7. 【Android NDK 开发】Android Studio 使用 CMake 导入静态库 ( CMake 简介 | 构建脚本路径配置 | 引入静态库 | 指定静态库路径 | 链接动态库 )

    文章目录 I . CMake 简介 II . Android Studio 中 CMake 引入静态库流程 III . 指定 CMake 最小版本号 IV . 导入函数库 ( 静态库 / 动态库 ) ...

  8. 【Android NDK 开发】Android.mk 配置静态库 ( Android Studio 配置静态库 | 配置动态库与静态库区别 | 动态库与静态库打包对比 )

    文章目录 I . Android Studio 中使用 Android.mk 配置静态库 总结 II . 第三方动态库来源 III . 配置 Android.mk 构建脚本路径 IV . 预编译 第三 ...

  9. 【Android NDK 开发】Android.mk 配置动态库 ( Android Studio 配置动态库 | 动态库加载版本限制 | 本章仅做参考推荐使用 CMake 配置动态库 )

    文章目录 I . Android Studio 中使用 Android.mk 配置动态库 总结 II . 第三方动态库来源 III . 配置 Android.mk 构建脚本路径 IV . 预编译 第三 ...

  10. 【Android NDK 开发】NDK 交叉编译 ( Ubuntu 中交叉编译动态库 | Android Studio 中配置使用第三方动态库 )

    文章目录 I . 动态库 与 静态库 II . 编译动态库 III. Android Studio 使用第三方动态库 IV . Android Studio 关键代码 V . 博客资源 I . 动态库 ...

最新文章

  1. 8)排序②排序算法之选择排序[1]直接选择排序
  2. @所有人,不服来战!你都认不出的这些汉字,机器能识别吗?
  3. python连接sqlite数据库的代码_【Python 连接SQLite数据库文件】
  4. Python自然语言处理学习笔记(32):4.4 函数:结构化编程的基础
  5. WebStorm 代码文字发虚模糊 - 解决篇
  6. “use strict” 严格模式使用(前端基础系列)
  7. 有标号的DAG计数 II
  8. 安卓系统曝root权限安全漏洞 已有软件利用
  9. jquery获取设置元素宽高位置height()、width()、offset()、position()、scrollTop()、scrollLeft()
  10. 下载了linux版redis怎么用,Linux下redis的安装与使用图文教程
  11. pycharm ubuntu 安装_pycharm使用远程python解释器
  12. L2:Abbott隐式格式有限差分法解一维明渠非恒定流
  13. 关于音频采样率,音频帧率,每次采集多少字节的理解
  14. 集合20210801
  15. 植物2 IOS 怎么实名认证_植物大战僵尸2未来世界22天困难怎么过关 植物阵容推荐...
  16. FPGA实现开根号,仿真通过,算一次需要34个时钟周期
  17. 数据库设计之网上书店系统
  18. Xilinx Srio详解IP核使用
  19. 海康硬盘录像机报警输出配置设置
  20. 【效能工具】记录常用效能工具

热门文章

  1. jib插件的简单使用
  2. 哪个大学开python课_2017春Python语言程序设计(天津大学仁爱学院)
  3. 修复iPhone系统白苹果问题
  4. 「Codeforces 643D」Bearish Fanpages
  5. android自定义View之气球碰撞效果
  6. 数据库点滴之T-SQL面试语句,练练手
  7. 对象赋值-深拷贝-浅拷贝
  8. hashcat的使用及相关
  9. Python:使用nltk统计词频并绘制统计图
  10. C语言算法训练学做菜,Java实现 蓝桥杯VIP 算法训练 学做菜