【NDK】封装日志库

共 22596字,需浏览 46分钟

 ·

2022-12-02 18:35

点击关注,与你共同成长!




【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 == nullreturn;
        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"44);
        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);
    }
}




【C++】STL梳理

【Android】NDK开发Crash分析

【C++】PK游戏(玩转多态)


以上,便是今天的分享,希望大家喜欢,觉得内容不错的,欢迎「分享」「」或者点击「在看」支持,谢谢各位。

浏览 41
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报