12#include <nlohmann/json.hpp>
35 std::lock_guard<std::mutex> lock(write_mutex_);
36 if (file_.is_open()) {
50 logger->info(
"Audit logging disabled");
53 std::filesystem::create_directories(config_.
log_dir);
54 auto path = config_.
log_dir /
"audit.jsonl";
55 current_file_size_ = std::filesystem::exists(path)
56 ? std::filesystem::file_size(path) : 0;
57 file_.open(path, std::ios::app);
58 if (!file_.is_open()) {
59 logger->error(
"Failed to open audit log: {}", path.string());
62 logger->info(
"Audit log opened: {}", path.string());
73 if (!config_.
enabled || !file_.is_open()) {
78 line[
"timestamp"] = utc_timestamp();
80 line[
"sequence"] =
static_cast<int64_t
>(sequence_.fetch_add(1));
82 std::string serialized = line.dump();
83 write_line(serialized);
84 auto seq = entry_count_.fetch_add(1);
85 logger->info(
"Audit: tool='{}', caller='{}', seq={}",
95void AuditLogger::write_line(
const std::string& line) {
96 std::lock_guard<std::mutex> lock(write_mutex_);
98 file_ << line <<
'\n';
99 current_file_size_ += line.size() + 1;
115 std::lock_guard<std::mutex> lock(write_mutex_);
116 if (file_.is_open()) {
129 return entry_count_.load();
139 return config_.
log_dir /
"audit.jsonl";
148std::string AuditLogger::utc_timestamp() {
149 auto now = std::chrono::system_clock::now();
150 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
151 now.time_since_epoch()) % 1000;
152 auto time = std::chrono::system_clock::to_time_t(now);
154 gmtime_r(&time, &utc);
155 std::ostringstream ss;
156 ss << std::put_time(&utc,
"%Y-%m-%dT%H:%M:%S")
157 <<
'.' << std::setfill(
'0') << std::setw(3) << ms.count() <<
'Z';
166void AuditLogger::rotate_if_needed() {
181void AuditLogger::rotate_files() {
183 auto base = config_.
log_dir /
"audit.jsonl";
185 for (
size_t i = config_.
max_files; i >= 1; --i) {
186 auto src = base.string() +
"." + std::to_string(i);
187 auto dst = base.string() +
"." + std::to_string(i + 1);
189 std::filesystem::remove(src);
190 }
else if (std::filesystem::exists(src)) {
191 std::filesystem::rename(src, dst);
194 std::filesystem::rename(base, base.string() +
".1");
195 file_.open(base, std::ios::app);
196 current_file_size_ = 0;
197 logger->info(
"Audit log rotated");
212 const char* context_json,
213 char** modified_json,
215 *modified_json =
nullptr;
217 if (ctx ==
nullptr || ctx->logger ==
nullptr) {
221 auto j = nlohmann::json::parse(context_json);
223 entry.
tool_name = j.value(
"tool_name",
"");
225 ? j[
"args"].dump() :
"{}";
227 entry.
elapsed_ms = j.value(
"elapsed_ms", 0.0);
229 ? j[
"directives"].dump() :
"[]";
231 ctx->logger->record(entry);
232 }
catch (
const std::exception& e) {
233 logger->error(
"Audit hook callback failed: {}", e.what());
253 ?
"error" :
"success";
AuditHookContext — bridges engine state to audit hook callback.
JSONL audit logger for MCP tool calls.
static int hook_callback(entropic_hook_point_t hook_point, const char *context_json, char **modified_json, void *user_data)
Hook callback for POST_TOOL_CALL integration.
AuditLogger(const AuditLogConfig &config)
Construct with configuration.
bool initialize()
Open the log file and prepare for writing.
void flush()
Force flush buffered entries to disk.
std::filesystem::path log_path() const
Get the file path of the current audit log.
void record(const AuditEntry &entry)
Record a tool call audit entry.
size_t entry_count() const
Get the number of entries recorded this session.
~AuditLogger()
Destructor — flushes and closes the log file.
entropic_hook_point_t
Hook points in the engine lifecycle.
spdlog initialization and logger access.
ENTROPIC_EXPORT std::shared_ptr< spdlog::logger > get(const std::string &name)
Get or create a named logger.
Activate model on GPU (WARM → ACTIVE).
void populate_from_hook_context(AuditEntry &entry, const AuditHookContext &ctx)
Populate AuditEntry fields from AuditHookContext state.
nlohmann::json audit_entry_to_json(const AuditEntry &entry)
Serialize AuditEntry fields to a JSON object.
A single audit log entry for one MCP tool call.
int delegation_depth
Current delegation depth (0 = lead)
std::string result_content
Tool result text (full, never truncated)
double elapsed_ms
Tool execution duration in milliseconds.
std::string parent_conversation_id
Parent conversation ID (empty for lead)
int iteration
Engine loop iteration number.
std::string params_json
Tool parameters as JSON string (never truncated)
std::string tool_name
Fully-qualified tool name (e.g., "filesystem.write_file")
std::string result_status
"success", "error", or "timeout"
std::string caller_id
Identity/tier name (e.g., "eng", "qa", "lead")
std::string directives_json
Directives array as JSON string ("[]" if none)
Context passed to AuditLogger hook via user_data pointer.
const int * iteration
Pointer to current iteration.
const int * delegation_depth
Pointer to current depth.
const std::string * caller_id
Pointer to current identity name.
const std::string * parent_conversation_id
Pointer to parent conv ID.
Audit log configuration within StorageConfig.
size_t max_file_size
Rotation size in bytes (0 = unlimited)
size_t flush_interval_entries
Flush every N entries (0 = every entry)
std::string session_id
UUID for this session.
std::filesystem::path log_dir
Directory for audit log files.
bool enabled
Master toggle for audit logging.
size_t max_files
Max rotated files to keep.