Browse Source

feature:logkit的native层开发

#Suyghur 4 years ago
parent
commit
479aa5efe0

+ 7 - 1
demo/build.gradle

@@ -17,6 +17,11 @@ android {
         targetSdkVersion TARGET_SDK_VERSION
         versionCode 1
         versionName "1.0"
+
+        ndk {
+            // 设置支持的SO库架构
+            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
+        }
     }
 
     signingConfigs {
@@ -67,5 +72,6 @@ android {
 }
 
 dependencies {
-    implementation project(':library_caps')
+//    implementation project(':library_caps')
+    implementation project(':library_logkit')
 }

+ 1 - 2
demo/src/main/java/com/suyghur/dolin/simple/DemoApplication.kt

@@ -2,7 +2,6 @@ package com.suyghur.dolin.simple
 
 import android.app.Application
 import android.content.Context
-import com.suyghur.dolin.Zege
 
 /**
  * @author #Suyghur.
@@ -16,6 +15,6 @@ class DemoApplication : Application() {
 
     override fun onCreate() {
         super.onCreate()
-        Zege.getDefault().initialize(this)
+//        Zege.getDefault().initialize(this)
     }
 }

+ 2 - 2
library_caps/src/main/cpp/clz_prepare.cpp

@@ -4,7 +4,7 @@
 
 #include <terminator.h>
 #include <logger.h>
-#include "libs/clz_prepare.h"
+#include "includes/clz_prepare.h"
 
 
 void JNICALL clzPrepare(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread, jclass clz) {
@@ -15,5 +15,5 @@ void JNICALL clzPrepare(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread, jclass
     char *clz_signature = nullptr;
     jvmti_env->GetClassSignature(clz, &clz_signature, nullptr);
 
-    char *stack_info=createStackInfo()
+//    char *stack_info=createStackInfo()
 }

+ 1 - 1
library_caps/src/main/cpp/terminator.cpp

@@ -2,7 +2,7 @@
 // Created by #Suyghur, on 4/6/21.
 //
 
-#include "libs/terminator.h"
+#include "includes/terminator.h"
 
 static volatile bool terminated = false;
 

+ 2 - 2
library_caps/src/main/cpp/zege.cpp

@@ -3,9 +3,9 @@
 //
 
 #include <jni.h>
-#include "libs/jvmti.h"
+#include "includes/jvmti.h"
 #include <string.h>
-#include "libs/logger.h"
+#include "includes/logger.h"
 #include "clz_prepare.h"
 
 static jvmtiEnv *localJvmtiEnv;

+ 2 - 2
library_caps/src/main/cpp/zege_drive.cpp

@@ -3,7 +3,7 @@
 //
 
 #include <logger.h>
-#include "libs/zege_drive.h"
+#include "includes/zege_drive.h"
 
 
 jvmtiEnv *getJvmtiEnv(JavaVM *vm) {
@@ -72,7 +72,7 @@ char *createStackInfo(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread, int stac
         }
 
         if (result == nullptr) {
-            asprintf(&result, "%s%s%s", clz_signature, "^^^", method_name)
+            asprintf(&result, "%s%s%s", clz_signature, "^^^", method_name);
         } else {
             char *stack = nullptr;
             asprintf(&stack, "%s%s%s%s%s", result, ",,,", clz_signature, "^^^", method_name);

+ 0 - 4
library_log/src/main/cpp/logkit.cpp

@@ -1,4 +0,0 @@
-//
-// Created by #Suyghur, on 4/7/21.
-//
-

+ 0 - 9
library_log/src/main/java/com/suyghur/dolin/log/LogKit.kt

@@ -1,9 +0,0 @@
-package com.suyghur.dolin.log
-
-/**
- * @author #Suyghur.
- * Created on 4/7/21
- */
-object LogKit {
-
-}

+ 0 - 0
library_log/.gitignore → library_logkit/.gitignore


+ 9 - 4
library_log/CMakeLists.txt → library_logkit/CMakeLists.txt

@@ -10,10 +10,15 @@ cmake_minimum_required(VERSION 3.4.1)
 # You can define multiple libraries, and CMake builds them for you.
 # Gradle automatically packages shared libraries with your APK.
 
-#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
-aux_source_directory(src/main/cpp/ DIR_LIB_SOURCE)
-add_library(zap SHARED ${DIR_LIB_SOURCE})
-include_directories(src/main/cpp/includes)
+aux_source_directory(src/main/cpp/ DIR_SOURCE)
+aux_source_directory(src/main/cpp/libs DIR_LIB_SOURCE)
+include_directories(src/main/cpp/libs)
+add_library(
+        logkit
+        SHARED
+        ${DIR_SOURCE}
+        ${DIR_LIB_SOURCE}
+)
 
 # Searches for a specified prebuilt library and stores the path as a
 # variable. Because CMake includes system libraries in the search path by

+ 0 - 0
library_log/build.gradle → library_logkit/build.gradle


+ 0 - 0
library_log/consumer-rules.pro → library_logkit/consumer-rules.pro


+ 0 - 0
library_log/proguard-rules.pro → library_logkit/proguard-rules.pro


+ 0 - 0
library_log/src/main/AndroidManifest.xml → library_logkit/src/main/AndroidManifest.xml


+ 162 - 0
library_logkit/src/main/cpp/libs/buffer.cpp

@@ -0,0 +1,162 @@
+//
+// Created by #Suyghur, on 4/7/21.
+//
+
+#include "buffer.h"
+
+Buffer::Buffer(char *ptr, size_t buffer_size) : buffer_ptr(ptr), buffer_size(buffer_size), buffer_header(buffer_ptr, buffer_size) {}
+
+Buffer::~Buffer() {
+    release();
+}
+
+void Buffer::initData(char *log_path, size_t log_path_len, bool compress) {
+    std::lock_guard<std::recursive_mutex> lck_release(log_mtx);
+    memset(buffer_ptr, '\0', buffer_size);
+
+    buffer_header::Header header{};
+    header.magic = kMagicHeader;
+    header.log_path_len = log_path_len;
+    header.log_path = log_path;
+    header.log_len = 0;
+    header.compress = compress;
+
+    buffer_header.initHeader(header);
+    initCompress(compress);
+
+    data_ptr = (char *) buffer_header.getPtr();
+    write_ptr = (char *) buffer_header.getWritePtr();
+
+    openLogFile(log_path);
+}
+
+void Buffer::setLength(size_t len) {
+    buffer_header.setLogLen(len);
+}
+
+size_t Buffer::getLength() {
+    return write_ptr - data_ptr;
+}
+
+size_t Buffer::append(const char *log, size_t len) {
+    std::lock_guard<std::recursive_mutex> lck_append(log_mtx);
+    if (getLength() == 0) {
+        initCompress(compress);
+    }
+
+    size_t free_size = emptySize();
+    size_t write_size;
+    if (compress) {
+        zStream.avail_in = (uInt) len;
+        zStream.next_in = (Bytef *) log;
+
+        zStream.avail_out = (uInt) free_size;
+        zStream.next_out = (Bytef *) write_ptr;
+
+        if (Z_OK != deflate(&zStream, Z_SYNC_FLUSH)) {
+            return 0;
+        }
+
+        write_size = free_size - zStream.avail_out;
+    } else {
+        write_size = len <= free_size ? len : free_size;
+        memcpy(write_ptr, log, write_size);
+    }
+    write_ptr += write_size;
+    setLength(getLength());
+    return write_size;
+}
+
+void Buffer::release() {
+    std::lock_guard<std::recursive_mutex> lck_release(log_mtx);
+    if (compress && Z_NULL != zStream.state) {
+        deflateEnd(&zStream);
+    }
+    if (map_buffer) {
+        munmap(buffer_ptr, buffer_size);
+    } else {
+        delete[]buffer_ptr;
+    }
+    if (log_file_ptr != nullptr) {
+        fclose(log_file_ptr);
+    }
+}
+
+size_t Buffer::emptySize() {
+    return buffer_size - (write_ptr - buffer_ptr);
+}
+
+char *Buffer::getLogPath() {
+    return buffer_header.getLogPath();
+}
+
+void Buffer::setFileFlush(FileFlush *flush) {
+    file_flush_ptr = flush;
+}
+
+void Buffer::callFileFlush() {
+    callFileFlush(file_flush_ptr);
+}
+
+void Buffer::callFileFlush(FileFlush *flush) {
+    callFileFlush(file_flush_ptr, nullptr);
+}
+
+void Buffer::callFileFlush(FileFlush *flush, Buffer *buffer) {
+    if (flush == nullptr) {
+        if (buffer != nullptr) {
+            delete buffer;
+        }
+        return;
+    }
+
+    std::lock_guard<std::recursive_mutex> lck_flush(log_mtx);
+    if (getLength() > 0) {
+        if (compress && Z_NULL != zStream.state) {
+            deflateEnd(&zStream);
+        }
+        auto *buffer_flush = new BufferFlush(log_file_ptr);
+        buffer_flush->write(data_ptr, getLength());
+        buffer_flush->releaseThiz(buffer);
+        clear();
+        flush->asyncFlush(buffer_flush);
+    } else {
+        delete buffer;
+    }
+}
+
+void Buffer::changeLogPath(char *path) {
+    if (log_file_ptr != nullptr) {
+        callFileFlush();
+    }
+    initData(path, strlen(path), compress);
+}
+
+void Buffer::clear() {
+    std::lock_guard<std::recursive_mutex> lck_clear(log_mtx);
+    write_ptr = data_ptr;
+    memset(write_ptr, '\0', emptySize());
+    setLength(getLength());
+}
+
+bool Buffer::initCompress(bool is_compress) {
+    compress = is_compress;
+    if (compress) {
+        zStream.zalloc = Z_NULL;
+        zStream.zfree = Z_NULL;
+        zStream.opaque = Z_NULL;
+        return Z_OK == deflateInit2(&zStream, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+    }
+    return false;
+}
+
+bool Buffer::openLogFile(const char *path) {
+    if (path != nullptr) {
+        FILE *file = fopen(path, "ab+");
+        if (file != nullptr) {
+            log_file_ptr = file;
+            return true;
+        }
+    }
+    return false;
+}

+ 74 - 0
library_logkit/src/main/cpp/libs/buffer.h

@@ -0,0 +1,74 @@
+//
+// Created by #Suyghur, on 4/7/21.
+//
+
+#ifndef DOLIN_BUFFER_H
+#define DOLIN_BUFFER_H
+
+#include <string>
+#include <mutex>
+#include <zlib.h>
+#include <sys/mman.h>
+#include "buffer_header.h"
+#include "file_flush.h"
+
+using namespace buffer_header;
+
+class Buffer {
+public:
+
+    bool map_buffer = true;
+
+    Buffer(char *ptr, size_t buffer_size);
+
+    ~Buffer();
+
+    void initData(char *log_path, size_t log_path_len, bool compress);
+
+    size_t getLength();
+
+    size_t append(const char *log, size_t len);
+
+    void release();
+
+    size_t emptySize();
+
+    char *getLogPath();
+
+    void setFileFlush(FileFlush *flush);
+
+    void callFileFlush();
+
+    void callFileFlush(FileFlush *flush);
+
+    void callFileFlush(FileFlush *flush, Buffer *buffer);
+
+    void changeLogPath(char *path);
+
+private:
+    FILE *log_file_ptr = nullptr;
+    FileFlush *file_flush_ptr = nullptr;
+    char *const buffer_ptr = nullptr;
+    char *data_ptr = nullptr;
+    char *write_ptr = nullptr;
+
+    size_t buffer_size = 0;
+    std::recursive_mutex log_mtx;
+
+    BufferHeader buffer_header;
+    z_stream zStream{};
+    bool compress = false;
+
+    void clear();
+
+    void setLength(size_t len);
+
+    bool initCompress(bool compress);
+
+    bool openLogFile(const char *path);
+
+
+};
+
+
+#endif //DOLIN_BUFFER_H

+ 76 - 0
library_logkit/src/main/cpp/libs/buffer_flush.cpp

@@ -0,0 +1,76 @@
+//
+// Created by #Suyghur, on 4/7/21.
+//
+
+#include <cmath>
+#include <cstring>
+#include "buffer_flush.h"
+
+BufferFlush::BufferFlush(FILE *log_file, size_t size) : capacity(size), log_file_ptr(log_file) {}
+
+BufferFlush::~BufferFlush() {
+    if (data_ptr != nullptr) {
+        delete[] data_ptr;
+    }
+    if (release_ptr != nullptr) {
+        delete release_ptr;
+    }
+
+}
+
+/**
+ * 填充缓存
+ */
+void BufferFlush::write(void *data, size_t len) {
+    if (data_ptr == nullptr) {
+        capacity = (size_t) fmax(capacity, len);
+        data_ptr = new char[capacity]{0};
+        write_ptr = data_ptr;
+    }
+
+    size_t empty_size = emptySize();
+    if (len < empty_size) {
+        memcpy(write_ptr, data, len);
+        write_ptr += len;
+    } else {
+        size_t now_len = getLength();
+        size_t new_capacity = now_len + len;
+        char *data_tmp = new char[new_capacity]{0};
+        memcpy(data_tmp, data_ptr, now_len);
+        memcpy(data_tmp + now_len, data, len);
+        char *old_data = data_ptr;
+        data_ptr = data_tmp;
+        write_ptr = data_ptr + new_capacity;
+        delete[]old_data;
+    }
+}
+
+void BufferFlush::reset() {
+    if (data_ptr != nullptr) {
+        memset(data_ptr, 0, capacity);
+        write_ptr = data_ptr;
+    }
+}
+
+size_t BufferFlush::getLength() {
+    if (data_ptr != nullptr && write_ptr != nullptr) {
+        return write_ptr - data_ptr;
+    }
+    return 0;
+}
+
+void *BufferFlush::getPtr() {
+    return data_ptr;
+}
+
+FILE *BufferFlush::getLogFile() {
+    return log_file_ptr;
+}
+
+void BufferFlush::releaseThiz(void *buffer) {
+    this->release_ptr = buffer;
+}
+
+size_t BufferFlush::emptySize() {
+    return capacity - getLength();
+}

+ 41 - 0
library_logkit/src/main/cpp/libs/buffer_flush.h

@@ -0,0 +1,41 @@
+//
+// Created by #Suyghur, on 4/7/21.
+//
+
+#ifndef DOLIN_BUFFER_FLUSH_H
+#define DOLIN_BUFFER_FLUSH_H
+
+
+#include <cstdio>
+
+class BufferFlush {
+
+public:
+    BufferFlush(FILE *log_file, size_t size = 128);
+
+    ~BufferFlush();
+
+    void write(void *data, size_t len);
+
+    void reset();
+
+    size_t getLength();
+
+    void *getPtr();
+
+    FILE *getLogFile();
+
+    void releaseThiz(void *buffer);
+
+private:
+    FILE *log_file_ptr = nullptr;
+    void *release_ptr = nullptr;
+    char *data_ptr = nullptr;
+    char *write_ptr = nullptr;
+    size_t capacity;
+
+    size_t emptySize();
+};
+
+
+#endif //DOLIN_BUFFER_FLUSH_H

+ 139 - 0
library_logkit/src/main/cpp/libs/buffer_header.cpp

@@ -0,0 +1,139 @@
+//
+// Created by #Suyghur, on 4/7/21.
+//
+
+#include "buffer_header.h"
+
+buffer_header::BufferHeader::BufferHeader(void *data, size_t size) : data_ptr((char *) data), data_size(size) {}
+
+buffer_header::BufferHeader::~BufferHeader() = default;
+
+void buffer_header::BufferHeader::initHeader(buffer_header::Header &header) {
+    if ((sizeof(char) + sizeof(size_t) + sizeof(size_t) + header.log_path_len) > data_size) {
+        return;
+    }
+    memcpy(data_ptr, &header.magic, sizeof(char));
+    memcpy(data_ptr + sizeof(char), &header.log_len, sizeof(size_t));
+    memcpy(data_ptr + sizeof(char) + sizeof(size_t), &header.log_path_len, sizeof(size_t));
+    memcpy(data_ptr + sizeof(char) + sizeof(size_t) + sizeof(size_t), header.log_path, header.log_path_len);
+    char compress = 0;
+    if (header.compress) {
+        compress = 1;
+    }
+    memcpy(data_ptr + sizeof(char) + sizeof(size_t) + sizeof(size_t) + header.log_path_len, &compress, sizeof(char));
+}
+
+/**
+ * 获取原始的锚点
+ */
+void *buffer_header::BufferHeader::getOriginPtr() {
+    return data_ptr;
+}
+
+/**
+ * 获取当前锚点
+ */
+void *buffer_header::BufferHeader::getPtr() {
+    return data_ptr + getHeaderLen();
+}
+
+/**
+ * 获取写入的锚点
+ */
+void *buffer_header::BufferHeader::getWritePtr() {
+    return data_ptr + getHeaderLen() + getLogLen();
+}
+
+buffer_header::Header *buffer_header::BufferHeader::getHeader() {
+    auto *header = new Header();
+    if (isAvailable()) {
+        header->magic = kMagicHeader;
+
+        size_t log_len = 0;
+        memcpy(&log_len, data_ptr + sizeof(char), sizeof(size_t));
+        header->log_len = log_len;
+
+        size_t log_path_len = 0;
+        memcpy(&log_path_len, data_ptr + sizeof(char) + sizeof(size_t), sizeof(size_t));
+        header->log_path_len = log_path_len;
+
+        char *log_path = new char[log_path_len + 1];
+        memset(log_path, 0, log_path_len + 1);
+        memcpy(log_path, data_ptr + sizeof(char) + sizeof(size_t) + sizeof(size_t), log_path_len);
+        header->log_path = log_path;
+
+        char compress = (data_ptr + sizeof(char) + sizeof(size_t) + sizeof(size_t) + log_path_len)[0];
+        header->compress = compress == 1;
+    }
+    return header;
+}
+
+size_t buffer_header::BufferHeader::getHeaderLen() {
+    if (isAvailable()) {
+        return calculateHeaderLen(getLogPathLen());
+    }
+    return 0;
+}
+
+void buffer_header::BufferHeader::setLogLen(size_t len) {
+    if (isAvailable()) {
+        memcpy(data_ptr + sizeof(char), &len, sizeof(size_t));
+    }
+}
+
+size_t buffer_header::BufferHeader::getLogLen() {
+    if (isAvailable()) {
+        size_t len = 0;
+        memcpy(&len, data_ptr + sizeof(char), sizeof(size_t));
+        //log长度总是大于0并小于buffer_size减去header长度的
+        if (len > 0 && len < (data_size - getHeaderLen())) {
+            return len;
+        }
+    }
+    return 0;
+}
+
+size_t buffer_header::BufferHeader::getLogPathLen() {
+    if (isAvailable()) {
+        size_t len = 0;
+        memcpy(&len, data_ptr + sizeof(char) + sizeof(size_t), sizeof(size_t));
+        //log path 的长度不能大于整个buffer减去header中其他数据的长度
+        if (len > 0 && len < data_size - calculateHeaderLen(0)) {
+            return len;
+        }
+    }
+    return 0;
+}
+
+char *buffer_header::BufferHeader::getLogPath() {
+    if (isAvailable()) {
+        size_t log_path_len = getLogPathLen();
+        if (log_path_len > 0) {
+            char *log_path = new char[log_path_len + 1];
+            memset(log_path, 0, log_path_len + 1);
+            memcpy(log_path, data_ptr + sizeof(char) + sizeof(size_t) + sizeof(size_t), log_path_len);
+            return log_path;
+        }
+    }
+    return nullptr;
+}
+
+
+bool buffer_header::BufferHeader::isCompress() {
+    if (isAvailable()) {
+        return ((data_ptr + sizeof(char) + sizeof(size_t) + sizeof(size_t) + getLogPathLen())[0]) == 1;
+    }
+    return false;
+}
+
+bool buffer_header::BufferHeader::isAvailable() {
+    return data_ptr[0] == kMagicHeader;
+}
+
+size_t buffer_header::BufferHeader::calculateHeaderLen(size_t path_len) {
+    return sizeof(char) + sizeof(size_t) + sizeof(size_t) + path_len + sizeof(char);
+}
+
+
+
+

+ 60 - 0
library_logkit/src/main/cpp/libs/buffer_header.h

@@ -0,0 +1,60 @@
+//
+// Created by #Suyghur, on 4/7/21.
+//
+
+#ifndef DOLIN_BUFFER_HEADER_H
+#define DOLIN_BUFFER_HEADER_H
+
+#include <string>
+
+namespace buffer_header {
+    static const char kMagicHeader = '\x11';
+
+    struct Header {
+        char magic;
+        size_t log_len;
+        size_t log_path_len;
+        char *log_path;
+        bool compress;
+    };
+
+    class BufferHeader {
+    public:
+        BufferHeader(void *data, size_t size);
+
+        ~BufferHeader();
+
+        void initHeader(Header &header);
+
+        void *getOriginPtr();
+
+        void *getPtr();
+
+        void *getWritePtr();
+
+        Header *getHeader();
+
+        size_t getHeaderLen();
+
+        void setLogLen(size_t len);
+
+        size_t getLogLen();
+
+        size_t getLogPathLen();
+
+        char *getLogPath();
+
+        bool isCompress();
+
+        bool isAvailable();
+
+        static size_t calculateHeaderLen(size_t path_len);
+
+    private:
+        char *data_ptr;
+        size_t data_size;
+    };
+}
+
+
+#endif //DOLIN_BUFFER_HEADER_H

+ 59 - 0
library_logkit/src/main/cpp/libs/file_flush.cpp

@@ -0,0 +1,59 @@
+//
+// Created by #Suyghur, on 4/7/21.
+//
+
+#include "file_flush.h"
+
+FileFlush::FileFlush() {
+    async_thread = std::thread(&FileFlush::asyncLogThread, this);
+}
+
+FileFlush::~FileFlush() {
+    stopFlush();
+}
+
+bool FileFlush::asyncFlush(BufferFlush *buffer) {
+    std::unique_lock<std::mutex> lck_async_flush(async_mtx);
+    if (exit) {
+        delete buffer;
+        return false;
+    }
+    async_buffer.push_back(buffer);
+    async_condition.notify_all();
+    return true;
+}
+
+void FileFlush::stopFlush() {
+    exit = true;
+    async_condition.notify_all();
+    async_thread.join();
+}
+
+void FileFlush::asyncLogThread() {
+    while (true) {
+        std::unique_lock<std::mutex> lck_async_log_thread(async_mtx);
+        while (!async_buffer.empty()) {
+            BufferFlush *data = async_buffer.back();
+            async_buffer.pop_back();
+            flush(data);
+        }
+        if (exit) {
+            return;
+        }
+        async_condition.wait(lck_async_log_thread);
+    }
+}
+
+/**
+ * 写文件
+ */
+ssize_t FileFlush::flush(BufferFlush *buffer) {
+    ssize_t written = 0;
+    FILE *log_file = buffer->getLogFile();
+    if (log_file != nullptr && buffer->getLength() > 0) {
+        written = fwrite(buffer->getPtr(), buffer->getLength(), 1, log_file);
+        fflush(log_file);
+    }
+    delete buffer;
+    return written;
+}

+ 36 - 0
library_logkit/src/main/cpp/libs/file_flush.h

@@ -0,0 +1,36 @@
+//
+// Created by #Suyghur, on 4/7/21.
+//
+
+#ifndef DOLIN_FILE_FLUSH_H
+#define DOLIN_FILE_FLUSH_H
+
+
+#include "buffer_flush.h"
+#include <vector>
+#include <thread>
+
+class FileFlush {
+public:
+    FileFlush();
+
+    ~FileFlush();
+
+    bool asyncFlush(BufferFlush *buffer);
+
+    void stopFlush();
+
+private:
+    void asyncLogThread();
+
+    static ssize_t flush(BufferFlush *buffer);
+
+    bool exit = false;
+    std::vector<BufferFlush *> async_buffer;
+    std::thread async_thread;
+    std::condition_variable async_condition;
+    std::mutex async_mtx;
+};
+
+
+#endif //DOLIN_FILE_FLUSH_H

+ 141 - 0
library_logkit/src/main/cpp/logkit.cpp

@@ -0,0 +1,141 @@
+//
+// Created by #Suyghur, on 4/7/21.
+//
+#include <jni.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sstream>
+#include "libs/buffer.h"
+#include "libs/file_flush.h"
+#include "libs/buffer_header.h"
+
+static FileFlush *p_file_flush = nullptr;
+
+static void writeDirty2File(int buffer_fd) {
+    struct stat file_stat{};
+    if (fstat(buffer_fd, &file_stat) >= 0) {
+        auto buffer_size = static_cast<size_t>(file_stat.st_size);
+        //buffer size必须大于文件头长度,否则下标溢出
+        if (buffer_size > buffer_header::BufferHeader::calculateHeaderLen(0)) {
+            char *buffer_ptr_tmp = (char *) mmap(0, buffer_size, PROT_WRITE | PROT_READ, MAP_SHARED, buffer_fd, 0);
+            if (buffer_ptr_tmp != MAP_FAILED) {
+                auto *tmp = new Buffer(buffer_ptr_tmp, buffer_size);
+                size_t data_size = tmp->getLength();
+                if (data_size > 0) {
+                    tmp->callFileFlush(p_file_flush, tmp);
+                } else {
+                    delete tmp;
+                }
+            }
+        }
+    }
+}
+
+static char *openMMap(int buffer_fd, size_t buffer_size) {
+    char *map_ptr = nullptr;
+    if (buffer_fd != -1) {
+        //写脏数据
+        writeDirty2File(buffer_fd);
+        //根据buffer size 调整 buffer 大小
+        ftruncate(buffer_fd, static_cast<int >(buffer_size));
+        lseek(buffer_fd, 0, SEEK_SET);
+        map_ptr = (char *) mmap(nullptr, buffer_size, PROT_WRITE | PROT_READ, MAP_SHARED, buffer_fd, 0);
+        if (map_ptr == MAP_FAILED) {
+            map_ptr = nullptr;
+        }
+    }
+    return map_ptr;
+}
+
+
+static jlong initNative(JNIEnv *env, jobject thiz, jstring buffer_path, jint capacity,
+                        jstring log_path, jboolean compress) {
+    const char *_buffer_path = env->GetStringUTFChars(buffer_path, JNI_FALSE);
+    const char *_log_path = env->GetStringUTFChars(log_path, JNI_FALSE);
+    auto buffer_size = static_cast<size_t>(capacity);
+    int buffer_fd = open(_buffer_path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+    //buffer ["路径的长度","路径","内容"], 参考mmkv格式
+    if (p_file_flush == nullptr) {
+        p_file_flush = new FileFlush();
+    }
+    //加上头信息占用的大小
+    buffer_size = buffer_size + BufferHeader::calculateHeaderLen(strlen(_log_path));
+    char *buffer_ptr = openMMap(buffer_fd, buffer_size);
+
+    bool map_buffer = true;
+    //如果打开 mmap 失败,则降级使用内存缓存
+    if (buffer_ptr == nullptr) {
+        buffer_ptr = new char[buffer_size];
+        map_buffer = false;
+    }
+
+    auto *buffer = new Buffer(buffer_ptr, buffer_size);
+    buffer->callFileFlush(p_file_flush);
+    //将 buffer 中的数据清空,并写入日志文件信息
+    buffer->initData((char *) _log_path, strlen(_log_path), compress);
+    buffer->map_buffer = map_buffer;
+
+    env->ReleaseStringUTFChars(buffer_path, _buffer_path);
+    env->ReleaseStringUTFChars(log_path, _log_path);
+    return reinterpret_cast<long >(buffer);
+}
+
+static void writeNative(JNIEnv *env, jobject thiz, jlong ptr, jstring msg) {
+    const char *_msg = env->GetStringUTFChars(msg, JNI_FALSE);
+    jsize msg_len = env->GetStringUTFLength(msg);
+    auto *buffer = reinterpret_cast<Buffer *>(ptr);
+    //缓存写满时异步刷新
+    if (msg_len >= buffer->emptySize()) {
+        if (p_file_flush != nullptr) {
+            buffer->callFileFlush(p_file_flush);
+        }
+    }
+    buffer->append(_msg, (size_t) msg_len);
+    env->ReleaseStringUTFChars(msg, _msg);
+}
+
+static void asyncFlushNative(JNIEnv *env, jobject thiz, jlong ptr) {
+    auto *buffer = reinterpret_cast<Buffer *>(ptr);
+    if (p_file_flush != nullptr) {
+        buffer->callFileFlush(p_file_flush);
+    }
+}
+
+static void changeLogPathNative(JNIEnv *env, jobject thiz, jlong ptr, jstring path) {
+    const char *log_path = env->GetStringUTFChars(path, JNI_FALSE);
+    auto *buffer = reinterpret_cast<Buffer *>(ptr);
+    buffer->changeLogPath(const_cast<charf *>(log_path));
+    env->ReleaseStringUTFChars(path, log_path);
+}
+
+static void releaseNative(JNIEnv *env, jobject thiz, jlong ptr) {
+    auto *buffer = reinterpret_cast<Buffer *>(ptr);
+    buffer->callFileFlush(p_file_flush, buffer);
+    if (p_file_flush != nullptr) {
+        delete p_file_flush;
+    }
+    p_file_flush = nullptr;
+}
+
+
+static JNINativeMethod gMethods[] = {
+        {"initNative",          "(Ljava/lang/String;ILjava/lang/String;Z)J", (void *) initNative},
+        {"writeNative",         "(JLjava/lang/String;)V",                    (void *) writeNative},
+        {"asyncFlushNative",    "(J)V",                                      (void *) asyncFlushNative},
+        {"changeLogPathNative", "(JLjava/lang/String;)V",                    (void *) changeLogPathNative},
+        {"releaseNative",       "(J)V",                                      (void *) releaseNative},
+
+};
+
+extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv *env = nullptr;
+    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
+        return JNI_ERR;
+    }
+    jclass clz = env->FindClass("com/suyghur/dolin/logkit/record/RecordImpl");
+    if (env->RegisterNatives(clz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
+        return JNI_ERR;
+    }
+    return JNI_VERSION_1_6;
+}

+ 22 - 0
library_logkit/src/main/java/com/suyghur/dolin/logkit/LogKit.kt

@@ -0,0 +1,22 @@
+package com.suyghur.dolin.logkit
+
+/**
+ * @author #Suyghur.
+ * Created on 4/7/21
+ */
+class LogKit {
+
+//    private var ptr = 0L
+
+//    init {
+//        System.loadLibrary("logkit")
+//        try {
+//            ptr = initNative()
+//        } catch (e: Exception) {
+//            e.printStackTrace()
+//        }
+//    }
+
+
+//    private external fun initNative(bufferPath: String, capacity: Int, logPath: String, compress: Boolean): Long
+}

+ 16 - 0
library_logkit/src/main/java/com/suyghur/dolin/logkit/record/IRecord.kt

@@ -0,0 +1,16 @@
+package com.suyghur.dolin.logkit.record
+
+/**
+ * @author #Suyghur.
+ * Created on 4/7/21
+ */
+interface IRecord {
+
+    fun write(msg: String)
+
+    fun asyncFlush()
+
+    fun changeLogPath(path:String)
+
+    fun release()
+}

+ 73 - 0
library_logkit/src/main/java/com/suyghur/dolin/logkit/record/RecordImpl.kt

@@ -0,0 +1,73 @@
+package com.suyghur.dolin.logkit.record
+
+/**
+ * @author #Suyghur.
+ * Created on 4/7/21
+ */
+class RecordImpl(bufferPath: String, capacity: Int, logPath: String, compress: Boolean) : IRecord {
+
+    //句柄
+    private var ptr = 0L
+
+    init {
+        System.loadLibrary("logkit")
+        try {
+            ptr = initNative(bufferPath, capacity, logPath, compress)
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+    }
+
+    override fun write(msg: String) {
+        if (ptr != 0L) {
+            try {
+                writeNative(ptr, msg)
+            } catch (e: Exception) {
+                e.printStackTrace()
+            }
+        }
+    }
+
+    override fun asyncFlush() {
+        if (ptr != 0L) {
+            try {
+                asyncFlushNative(ptr)
+            } catch (e: Exception) {
+                e.printStackTrace()
+            }
+        }
+    }
+
+    override fun changeLogPath(path: String) {
+        if (ptr != 0L) {
+            try {
+                changeLogPathNative(ptr, path)
+            } catch (e: Exception) {
+                e.printStackTrace()
+            }
+        }
+    }
+
+    override fun release() {
+        if (ptr != 0L) {
+            try {
+                releaseNative(ptr)
+            } catch (e: Exception) {
+                e.printStackTrace()
+            }
+        }
+    }
+
+    private external fun writeNative(ptr: Long, msg: String)
+
+    private external fun asyncFlushNative(ptr: Long)
+
+    private external fun changeLogPathNative(ptr: Long, path: String)
+
+    private external fun releaseNative(ptr: Long)
+
+    companion object {
+        @JvmStatic
+        private external fun initNative(bufferPath: String, capacity: Int, logPath: String, compress: Boolean): Long
+    }
+}

+ 2 - 2
settings.gradle

@@ -1,4 +1,4 @@
 include ':demo'
-include ':library_caps'
-include ':library_log'
+//include ':library_caps'
+include ':library_logkit'
 rootProject.name = 'Dolin'