【NDK】封装日志库

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【NDK】封装日志库

7e8835d9408a9f4bd641f219306dd888.jpg

【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);
    }
}


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
4月前
|
前端开发 C语言 开发者
领导被我的花式console.log吸引了!直接写入公司公共库!
【8月更文挑战第23天】领导被我的花式console.log吸引了!直接写入公司公共库!
45 2
领导被我的花式console.log吸引了!直接写入公司公共库!
|
4月前
|
JSON 中间件 Go
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
本文详细介绍了如何在Go项目中集成并配置Zap日志库。首先通过`go get -u go.uber.org/zap`命令安装Zap,接着展示了`Logger`与`Sugared Logger`两种日志记录器的基本用法。随后深入探讨了Zap的高级配置,包括如何将日志输出至文件、调整时间格式、记录调用者信息以及日志分割等。最后,文章演示了如何在gin框架中集成Zap,通过自定义中间件实现了日志记录和异常恢复功能。通过这些步骤,读者可以掌握Zap在实际项目中的应用与定制方法
158 1
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
|
3月前
|
存储 运维 监控
超级好用的C++实用库之日志类
超级好用的C++实用库之日志类
50 0
|
4月前
|
Linux API
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
|
4月前
|
存储 JSON 前端开发
一文搞懂 Go 1.21 的日志标准库 - slog
一文搞懂 Go 1.21 的日志标准库 - slog
161 2
|
4月前
|
JSON Go API
一文搞懂 Golang 高性能日志库 - Zap
一文搞懂 Golang 高性能日志库 - Zap
338 2
|
4月前
|
存储 安全 Python
[python]使用标准库logging实现多进程安全的日志模块
[python]使用标准库logging实现多进程安全的日志模块
|
5月前
|
测试技术 UED 存储
SLS Prometheus存储问题之在使用内置降采样时,SLS自动选择适配的指标库该如何解决
SLS Prometheus存储问题之在使用内置降采样时,SLS自动选择适配的指标库该如何解决
|
4月前
|
存储 JSON Go
一文搞懂 Golang 高性能日志库 Zerolog
一文搞懂 Golang 高性能日志库 Zerolog
460 0
|
6月前
|
C++
spdlog 日志库部分源码说明——日志格式设定,DIY你自己喜欢的调试信息,你能调试的远比你想象的还要丰富
spdlog 日志库部分源码说明——日志格式设定,DIY你自己喜欢的调试信息,你能调试的远比你想象的还要丰富
358 6