【NDK】封装日志库
00x1需求x1需求
- 供C++、Java调用
- 控制台输出
- 文件输出(文件大小)
- 设置日志等级
00x2 C++x2 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) // 文件最大为3MB enum { 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 file if (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_SIZE fp = 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 Java0x3 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) { Log.e("LogUtils", "Exception: " + e.getMessage()); } 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); } }