* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it
* under the terms of CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* \file sk_log.h
* \brief Super Kernel Log Module (Merged File Logger)
*
* Features:
* 1. Supports on/off control, passthrough to dlog_* when disabled
* 2. Creates directory structure: sk_meta/{Pid}/{ModelRI} when enabled
* 3. Provides five log levels: trace/debug/info/warning/error
* 4. Supports segmentation for long logs
* 5. RAII-style log context manager
* 6. Thread-safe file handle management
*/
#ifndef __SK_LOG_H__
#define __SK_LOG_H__
#include <csignal>
#include <cstdarg>
#include <cstdio>
#include <string>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <atomic>
#include <fstream>
#include <sstream>
#include <cstring>
#include <chrono>
#include <iomanip>
#include <thread>
#include <unistd.h>
#include <sys/stat.h>
#include <cstdlib>
#include "dlog_pub.h"
typedef void* aclmdlRI;
#define ASCENDC_MODULE_NAME static_cast<int32_t>(ASCENDCKERNEL)
#define SK_DLOGD(format, ...) \
do { \
dlog_debug(ASCENDC_MODULE_NAME, "[SK][%s] " format "\n", __FUNCTION__, \
##__VA_ARGS__); \
} while (0)
#define SK_DLOGW(format, ...) \
do { \
dlog_warn(ASCENDC_MODULE_NAME, "[SK][%s] " format "\n", __FUNCTION__, \
##__VA_ARGS__); \
} while (0)
#define SK_DLOGI(format, ...) \
do { \
dlog_info(ASCENDC_MODULE_NAME, "[SK][%s] " format "\n", __FUNCTION__, \
##__VA_ARGS__); \
} while (0)
#define SK_DLOGE(format, ...) \
do { \
dlog_error(ASCENDC_MODULE_NAME, "[SK][%s] " format "\n", __FUNCTION__, \
##__VA_ARGS__); \
} while (0)
#define CHECK_ACL(x) \
do { \
aclError __ret = x; \
if (__ret != ACL_ERROR_NONE) { \
SK_DLOGE("aclError: %d", __ret); \
} \
} while (0)
#define REPORT_ERROR_MESSAGE(...) \
do { \
ReportErrorMessage(__VA_ARGS__); \
} while (0)
#define SK_ASSERT_RETVAL(cond, ret) \
do { \
if (!(cond)) { \
SK_DLOGE_WITH_REPORT("Assert %s failed", #cond); \
return (ret); \
} \
} while (0)
#define SK_DLOGE_WITH_REPORT(format, ...) \
do { \
dlog_error(ASCENDC_MODULE_NAME, "[SK][%s] " format "\n", __FUNCTION__, \
##__VA_ARGS__); \
REPORT_ERROR_MESSAGE(format, ##__VA_ARGS__); \
} while (0)
constexpr const char* GetFileName(const char* path)
{
const char* file = path;
while (*path != '\0') {
if (*path++ == '/') {
file = path;
}
}
return file;
}
void ReportErrorMessageInner(const std::string& code, const char* fmt, ...);
const std::string& GetCurrentModelLabel();
template <typename... Arguments>
void ReportErrorMessage(const char* fmt, Arguments&&... args)
{
std::string errorCode = "EZ9999";
return ReportErrorMessageInner(errorCode, fmt, std::forward<Arguments>(args)...);
}
namespace sk {
namespace logger {
enum class LogLevel : uint8_t {
TRACE = 0,
DEBUG = 1,
INFO = 2,
WARNING = 3,
ERROR = 4,
OFF = 5
};
inline const char* LogLevelToString(LogLevel level) {
switch (level) {
case LogLevel::TRACE: return "TRACE";
case LogLevel::DEBUG: return "DEBUG";
case LogLevel::INFO: return "INFO";
case LogLevel::WARNING: return "WARN";
case LogLevel::ERROR: return "ERROR";
default: return "UNKNOWN";
}
}
struct LoggerConfig {
bool enabled = false;
LogLevel minLevel = LogLevel::INFO;
std::string baseDir = "sk_meta";
std::string modelLabel;
size_t maxFileSize = 100 * 1024 * 1024;
size_t maxLineLength = 4096;
bool enableTimestamp = true;
bool enablePidTid = true;
};
struct FileHandleInfo {
std::string filePath;
std::ofstream fileStream;
size_t currentSize = 0;
size_t writeCount = 0;
std::chrono::system_clock::time_point createTime;
FileHandleInfo() = default;
FileHandleInfo(const FileHandleInfo&) = delete;
FileHandleInfo& operator=(const FileHandleInfo&) = delete;
FileHandleInfo(FileHandleInfo&& other) noexcept
: filePath(std::move(other.filePath))
, fileStream(std::move(other.fileStream))
, currentSize(other.currentSize)
, writeCount(other.writeCount)
, createTime(other.createTime) {}
FileHandleInfo& operator=(FileHandleInfo&& other) noexcept {
if (this != &other) {
if (fileStream.is_open()) {
fileStream.close();
}
filePath = std::move(other.filePath);
fileStream = std::move(other.fileStream);
currentSize = other.currentSize;
writeCount = other.writeCount;
createTime = other.createTime;
}
return *this;
}
~FileHandleInfo() {
if (fileStream.is_open()) {
fileStream.flush();
fileStream.close();
}
}
};
class FileHandleManager {
public:
static FileHandleManager& Instance() {
static FileHandleManager instance;
return instance;
}
bool RegisterFile(const std::string& name, const std::string& path);
bool SwitchToFile(const std::string& name);
void SwitchToDefault();
bool Write(const std::string& name, const std::string& content);
bool WriteToCurrent(const std::string& content);
std::string GetCurrentHandle() const;
size_t GetFileSize(const std::string& name);
void CloseFile(const std::string& name);
bool InitializeDefault(const std::string& baseDir, pid_t pid, const std::string& modelLabel);
private:
FileHandleManager();
~FileHandleManager();
FileHandleManager(const FileHandleManager&) = delete;
FileHandleManager& operator=(const FileHandleManager&) = delete;
private:
std::unordered_map<std::string, FileHandleInfo> handles_;
mutable std::mutex mutex_;
static thread_local std::string currentHandle_;
};
class LogContextGuard {
public:
explicit LogContextGuard(const std::string& fileName, const std::string& filePath);
~LogContextGuard();
LogContextGuard(const LogContextGuard&) = delete;
LogContextGuard& operator=(const LogContextGuard&) = delete;
LogContextGuard(LogContextGuard&& other) noexcept;
LogContextGuard& operator=(LogContextGuard&& other) noexcept;
bool IsActive() const { return active_; }
private:
std::string previousHandle_;
bool active_;
};
class FileLogger {
public:
static FileLogger& Instance();
bool Initialize(const LoggerConfig& config);
template<typename... Args>
void WriteLogIfEnabled(LogLevel level, const char* funcName,
const char* fileName, int lineNum,
const char* format, Args&&... args) {
if (config_.enabled && level >= config_.minLevel) {
std::string message = FormatMessage(level, funcName, fileName, lineNum, format, std::forward<Args>(args)...);
WriteLog(message);
}
}
static void SetCurrentModelLabel(const std::string& modelLabel) {
currentThreadModelLabel_ = modelLabel;
}
static const std::string& GetCurrentThreadModelLabel() {
return currentThreadModelLabel_;
}
bool RegisterLogFile(const std::string& name, const std::string& subPath = "");
bool SwitchToFile(const std::string& name);
void SwitchToDefault();
std::unique_ptr<LogContextGuard> CreateContext(const std::string& fileName,
const std::string& modelLabel);
void SetEnabled(bool enabled);
bool IsEnabled() const;
void SetMinLevel(LogLevel level);
void SetModelLabel(const std::string& modelLabel);
bool IsInitialized() const;
std::string GetCurrentModelLabel() const;
private:
FileLogger() = default;
~FileLogger() = default;
FileLogger(const FileLogger&) = delete;
FileLogger& operator=(const FileLogger&) = delete;
std::string FormatMessage(LogLevel level, const char* funcName,
const char* fileName, int lineNum,
const char* format, ...);
void WriteLog(const std::string& message);
std::string GetEffectiveModelLabel() const;
private:
LoggerConfig config_;
std::atomic<bool> initialized_{false};
pid_t pid_{0};
mutable std::mutex mutex_;
static thread_local std::string currentThreadModelLabel_;
std::string currentModelLabel_;
};
}
}
#define SK_LOGT(format, ...) \
do { \
SK_DLOGD(format, ##__VA_ARGS__); \
sk::logger::FileLogger::Instance().WriteLogIfEnabled( \
sk::logger::LogLevel::TRACE, __FUNCTION__, __FILE__, __LINE__, format, ##__VA_ARGS__); \
} while (0)
#define SK_LOGD(format, ...) \
do { \
SK_DLOGD(format, ##__VA_ARGS__); \
sk::logger::FileLogger::Instance().WriteLogIfEnabled( \
sk::logger::LogLevel::DEBUG, __FUNCTION__, __FILE__, __LINE__, format, ##__VA_ARGS__); \
} while (0)
#define SK_LOGI(format, ...) \
do { \
SK_DLOGI(format, ##__VA_ARGS__); \
sk::logger::FileLogger::Instance().WriteLogIfEnabled( \
sk::logger::LogLevel::INFO, __FUNCTION__, __FILE__, __LINE__, format, ##__VA_ARGS__); \
} while (0)
#define SK_LOGW(format, ...) \
do { \
SK_DLOGW(format, ##__VA_ARGS__); \
sk::logger::FileLogger::Instance().WriteLogIfEnabled( \
sk::logger::LogLevel::WARNING, __FUNCTION__, __FILE__, __LINE__, format, ##__VA_ARGS__); \
} while (0)
#define SK_LOGE(format, ...) \
do { \
SK_DLOGE(format, ##__VA_ARGS__); \
sk::logger::FileLogger::Instance().WriteLogIfEnabled( \
sk::logger::LogLevel::ERROR, __FUNCTION__, __FILE__, __LINE__, format, ##__VA_ARGS__); \
} while (0)
#define SK_LOG_CONTEXT(fileName, modelLabel) \
auto _sk_log_context_guard = sk::logger::FileLogger::Instance().CreateContext(fileName, modelLabel)
#define SK_LOG_CONTEXT_SIMPLE(fileName) \
auto _sk_log_context_guard = sk::logger::FileLogger::Instance().CreateContext( \
fileName, sk::logger::FileLogger::Instance().GetCurrentModelLabel())
* @brief Initialize logger with frozen model label
* @param modelLabel Frozen model label, e.g. model_48_1
*
* Reads environment variable ASCEND_OP_COMPILE_SAVE_KERNEL_META to enable/disable file logging.
* - ASCEND_OP_COMPILE_SAVE_KERNEL_META=1: Enable file logging
* - ASCEND_OP_COMPILE_SAVE_KERNEL_META=0 or unset: Disable file logging
*
* @example
* InitSkLogger(modelLabel);
* // Creates directory: sk_meta/{pid}/model_305419896/
*/
inline void InitSkLogger(const std::string& modelLabel) {
sk::logger::FileLogger::SetCurrentModelLabel(modelLabel);
const char* envVar = std::getenv("ASCEND_OP_COMPILE_SAVE_KERNEL_META");
bool enabled = false;
if (envVar != nullptr) {
std::string value(envVar);
value.erase(0, value.find_first_not_of(" \t\n\r\f\v"));
value.erase(value.find_last_not_of(" \t\n\r\f\v") + 1);
enabled = (value == "1");
if (enabled) {
SK_DLOGI("File logger enabled by environment variable ASCEND_OP_COMPILE_SAVE_KERNEL_META=1");
} else {
SK_DLOGI("File logger disabled by environment variable ASCEND_OP_COMPILE_SAVE_KERNEL_META=%s", value.c_str());
}
} else {
SK_DLOGI("File logger disabled (environment variable ASCEND_OP_COMPILE_SAVE_KERNEL_META not set)");
}
sk::logger::LoggerConfig config;
config.enabled = enabled;
config.modelLabel = modelLabel;
config.minLevel = sk::logger::LogLevel::DEBUG;
config.baseDir = "sk_meta";
if (!sk::logger::FileLogger::Instance().Initialize(config)) {
SK_DLOGW("Failed to initialize file logger, falling back to dlog only");
sk::logger::FileLogger::Instance().SetEnabled(false);
}
}
bool InitializeSkFileLogger(bool enabled, const std::string& modelLabel = "",
sk::logger::LogLevel minLevel = sk::logger::LogLevel::INFO);
#define INIT_SK_FILE_LOGGER(modelLabel) \
InitializeSkFileLogger(true, modelLabel, sk::logger::LogLevel::DEBUG)
#define INIT_SK_FILE_LOGGER_MINIMAL(modelLabel) \
InitializeSkFileLogger(true, modelLabel, sk::logger::LogLevel::INFO)
#define DISABLE_SK_FILE_LOGGER() \
sk::logger::FileLogger::Instance().SetEnabled(false)
#endif