【NDK】封装日志库
点击关注,与你共同成长!
【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) // 文件最大为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 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);
}
}
以上,便是今天的分享,希望大家喜欢,觉得内容不错的,欢迎「分享」「赞」或者点击「在看」支持,谢谢各位。
评论