log_manager

1 阅读5分钟

/*

  • Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2024-2024. All rights reserved.
  • Description: Log storage manager implementation based on littlefs. */

#include "log_manager.h" #include "littlefs_adapt.h" #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <stdarg.h> #include <sys/stat.h>

// ==================== Static Variables ====================

static log_manager_t g_log_mgr = {0};

// ==================== Static Helper Functions ====================

/**

  • @brief Generate timestamp-based filename with format: YYYYMMDD_HHMMSS

  • Uses local time, caller should ensure time is set correctly. */ static void get_timestamp_string(char *buf, size_t len) { if (buf == NULL || len < 16) { return; }

    time_t now = time(NULL); struct tm *timeinfo = localtime(&now);

    snprintf(buf, len, "%04d%02d%02d_%02d%02d%02d", timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); }

/**

  • @brief Check if file should be rotated (exceeded size limit) */ static int should_rotate_file(unsigned int current_size) { return current_size >= LOG_MAX_FILE_SIZE; }

/**

  • @brief Generate unique filename with timestamp and sequence */ static void generate_unique_filename(char *filename, size_t max_len) { char timestamp[32]; get_timestamp_string(timestamp, sizeof(timestamp));

    // Primary filename format: /logs/YYYYMMDD_HHMMSS.log snprintf(filename, max_len, "%s/%s.log", LOG_DIR_PATH, timestamp); }

/**

  • @brief Scan log directory and build file list */ static int scan_log_directory(void) { // Note: littlefs_adapt doesn't provide directory listing API // This is a placeholder for when directory API is added // For now, files are tracked in memory during runtime

    g_log_mgr.file_count = 0; memset(g_log_mgr.file_list, 0, sizeof(g_log_mgr.file_list));

    return 0; }

/**

  • @brief Update file metadata cache */ static int update_file_metadata(const char *filename) { if (!filename || strlen(filename) == 0) { return -1; }

    unsigned int size = 0;

    // Get file size if (fs_adapt_stat(filename, &size) != 0) { return -1; }

    // Add to file list if not already present and within limit if (g_log_mgr.file_count < LOG_MAX_FILES) { strncpy(g_log_mgr.file_list[g_log_mgr.file_count].filename, filename, LOG_MAX_FILENAME_SIZE - 1); g_log_mgr.file_list[g_log_mgr.file_count].size = size; g_log_mgr.file_list[g_log_mgr.file_count].last_modified = time(NULL); g_log_mgr.file_count++; }

    return 0; }

/**

  • @brief Rotate to new log file when current exceeds limit */ static int rotate_log_file(void) { // Close current file if open if (g_log_mgr.current_fd >= 0) { fs_adapt_sync(g_log_mgr.current_fd); fs_adapt_close(g_log_mgr.current_fd); g_log_mgr.current_fd = -1; }

    // Generate new filename generate_unique_filename(g_log_mgr.current_filename, LOG_MAX_FILENAME_SIZE);

    // Open new file g_log_mgr.current_fd = fs_adapt_open(g_log_mgr.current_filename, O_RDWR | O_CREAT | O_TRUNC); if (g_log_mgr.current_fd < 0) { return -1; }

    g_log_mgr.current_file_size = 0;

    // Update file metadata update_file_metadata(g_log_mgr.current_filename);

    return 0; }

// ==================== Public API Implementation ====================

int log_manager_init(void) { // Mount littlefs fs_adapt_mount();

// Create log directory
int ret = fs_adapt_mkdir(LOG_DIR_PATH);
if (ret < 0 && ret != LFS_ERR_EXIST) {
    return -1;
}

// Initialize manager structure
g_log_mgr.initialized = 1;
g_log_mgr.current_fd = -1;
g_log_mgr.current_file_size = 0;
g_log_mgr.file_count = 0;

// Scan existing files
scan_log_directory();

// Create initial log file
if (rotate_log_file() != 0) {
    g_log_mgr.initialized = 0;
    return -1;
}

return 0;

}

int log_manager_cleanup(void) { if (!g_log_mgr.initialized) { return -1; }

// Close current file
if (g_log_mgr.current_fd >= 0) {
    fs_adapt_sync(g_log_mgr.current_fd);
    fs_adapt_close(g_log_mgr.current_fd);
    g_log_mgr.current_fd = -1;
}

// Unmount filesystem
fs_adapt_unmount();

g_log_mgr.initialized = 0;

return 0;

}

char* log_manager_generate_filename(char *filename, size_t max_len) { if (filename == NULL || max_len < 32) { return NULL; }

generate_unique_filename(filename, max_len);
return filename;

}

int log_manager_write(const char *log_message) { if (!g_log_mgr.initialized || !log_message) { return -1; }

unsigned int msg_len = strlen(log_message);

// Check if current file needs rotation
if (should_rotate_file(g_log_mgr.current_file_size + msg_len + 1)) {
    if (rotate_log_file() != 0) {
        return -1;
    }
}

// Write the log message
int ret = fs_adapt_write(g_log_mgr.current_fd, log_message, msg_len);
if (ret <= 0) {
    return -1;
}

// Write newline
ret = fs_adapt_write(g_log_mgr.current_fd, "\n", 1);
if (ret <= 0) {
    return -1;
}

// Sync to storage
fs_adapt_sync(g_log_mgr.current_fd);

// Update file size
g_log_mgr.current_file_size += msg_len + 1;

return msg_len + 1;

}

int log_manager_printf(const char *format, ...) { if (!g_log_mgr.initialized || !format) { return -1; }

char buffer[512];
va_list args;

va_start(args, format);
int ret = vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);

if (ret < 0) {
    return -1;
}

return log_manager_write(buffer);

}

int log_manager_write_level(const char *level, const char *message) { if (!level || !message) { return -1; }

char buffer[512];
time_t now = time(NULL);
struct tm *timeinfo = localtime(&now);

snprintf(buffer, sizeof(buffer), 
         "[%02d:%02d:%02d] [%-5s] %s",
         timeinfo->tm_hour,
         timeinfo->tm_min,
         timeinfo->tm_sec,
         level,
         message);

return log_manager_write(buffer);

}

int log_manager_read(const char *filename, char *buffer, size_t max_len) { if (!filename || !buffer || max_len == 0) { return -1; }

char filepath[LOG_MAX_FILENAME_SIZE];
snprintf(filepath, sizeof(filepath), "%s/%s", LOG_DIR_PATH, filename);

// Open file in read-only mode
int fd = fs_adapt_open(filepath, O_RDONLY);
if (fd < 0) {
    return 0;  // File not found
}

// Read file content
int bytes_read = fs_adapt_read(fd, buffer, max_len - 1);

// Close file
fs_adapt_close(fd);

if (bytes_read > 0) {
    buffer[bytes_read] = '\0';
    return bytes_read;
}

return bytes_read;

}

int log_manager_read_offset(const char *filename, char *buffer, size_t max_len, unsigned int offset) { if (!filename || !buffer || max_len == 0) { return -1; }

char filepath[LOG_MAX_FILENAME_SIZE];
snprintf(filepath, sizeof(filepath), "%s/%s", LOG_DIR_PATH, filename);

// Open file
int fd = fs_adapt_open(filepath, O_RDONLY);
if (fd < 0) {
    return 0;
}

// Seek to offset
int ret = fs_adapt_seek(fd, (int)offset, LFS_SEEK_SET);
if (ret < 0) {
    fs_adapt_close(fd);
    return -1;
}

// Read from offset
int bytes_read = fs_adapt_read(fd, buffer, max_len - 1);

// Close file
fs_adapt_close(fd);

if (bytes_read > 0) {
    buffer[bytes_read] = '\0';
    return bytes_read;
}

return bytes_read;

}

int log_manager_get_file_size(const char *filename, unsigned int *file_size) { if (!filename || !file_size) { return -1; }

char filepath[LOG_MAX_FILENAME_SIZE];
snprintf(filepath, sizeof(filepath), "%s/%s", LOG_DIR_PATH, filename);

return fs_adapt_stat(filepath, file_size);

}

int log_manager_delete_file(const char *filename) { if (!filename) { return -1; }

char filepath[LOG_MAX_FILENAME_SIZE];
snprintf(filepath, sizeof(filepath), "%s/%s", LOG_DIR_PATH, filename);

int ret = fs_adapt_delete(filepath);
if (ret == 0) {
    // Remove from file list
    for (int i = 0; i < g_log_mgr.file_count; i++) {
        if (strcmp(g_log_mgr.file_list[i].filename, filename) == 0) {
            // Shift remaining items
            for (int j = i; j < g_log_mgr.file_count - 1; j++) {
                memcpy(&g_log_mgr.file_list[j], 
                       &g_log_mgr.file_list[j + 1], 
                       sizeof(log_file_info_t));
            }
            g_log_mgr.file_count--;
            break;
        }
    }
}

return ret == 0 ? 0 : -1;

}

int log_manager_list_files(log_file_info_t *files, int max_count, int *actual_count) { if (!files || max_count <= 0) { return -1; }

int count = g_log_mgr.file_count < max_count ? g_log_mgr.file_count : max_count;

memcpy(files, g_log_mgr.file_list, count * sizeof(log_file_info_t));

if (actual_count != NULL) {
    *actual_count = count;
}

return 0;

}

int log_manager_cleanup_old_files(int max_files) { if (max_files <= 0 || g_log_mgr.file_count <= max_files) { return 0; }

int files_to_delete = g_log_mgr.file_count - max_files;
int deleted = 0;

// Simple approach: delete oldest files by index
for (int i = 0; i < files_to_delete && i < g_log_mgr.file_count; i++) {
    if (log_manager_delete_file(g_log_mgr.file_list[i].filename) == 0) {
        deleted++;
    }
}

return deleted;

}

int log_manager_get_total_size(unsigned int *total_size) { if (!total_size) { return -1; }

*total_size = 0;

for (int i = 0; i < g_log_mgr.file_count; i++) {
    *total_size += g_log_mgr.file_list[i].size;
}

return 0;

}

int log_manager_get_current_file_info(log_file_info_t *current_file) { if (!current_file) { return -1; }

if (strlen(g_log_mgr.current_filename) == 0) {
    return -1;
}

strncpy(current_file->filename, g_log_mgr.current_filename, LOG_MAX_FILENAME_SIZE - 1);
current_file->size = g_log_mgr.current_file_size;
current_file->last_modified = time(NULL);

return 0;

}

int log_manager_clear_all(void) { // Delete all known log files int deleted = 0; int original_count = g_log_mgr.file_count;

for (int i = 0; i < original_count; i++) {
    if (log_manager_delete_file(g_log_mgr.file_list[0].filename) == 0) {
        deleted++;
    }
}

// Rotate to create new empty log file
if (rotate_log_file() != 0) {
    return -1;
}

return deleted;

}

int log_manager_get_flash_info(unsigned int *start_addr, unsigned int *total_size, unsigned int *free_size) { if (!start_addr || !total_size || !free_size) { return -1; }

// Access littlefs block information from littlefs_adapt module
// These are defined in littlefs_adapt.c
// extern lfs_block_info_t g_lfs_block_info
// where g_lfs_block_info.start_block and g_lfs_block_info.max_block are available

// Note: We need to access the global variables from littlefs_adapt.c
// The block size is 4KB (4096 bytes) as defined in littlefs_adapt.c
#define LFS_BLOCK_SIZE 4096

// This is a workaround - we retrieve block info by using partition API
// If partition info is available
partition_information_t part_info;
errcode_t ret = uapi_partition_get_info(CONFIG_LFS_PARTITION_ID, &part_info);

if (ret != ERRCODE_SUCC) {
    return -1;
}

// Get partition address and size
*start_addr = part_info.part_info.addr_info.addr;
*total_size = part_info.part_info.addr_info.size;

// Calculate free space = total size - used log files size
unsigned int used_size = 0;
if (log_manager_get_total_size(&used_size) != 0) {
    used_size = 0;  // If we can't get it, assume 0
}

if (*total_size >= used_size) {
    *free_size = *total_size - used_size;
} else {
    *free_size = 0;
}

return 0;

}