Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions diagnostic/build-2b54872c.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
{
"generated_at": "2026-06-21T10:12:02.735403+00:00",
"commit": "2b54872c",
"diagnostic_logd": "diagnostic/build-2b54872c.logd",
"diagnostic_logd_error": null,
"chunked": false,
"chunk_size_bytes": null,
"password": "eaa19d08ed6752bc7db4",
"decrypt_command": "encryptly unpack diagnostic/build-2b54872c.logd <outdir> --password eaa19d08ed6752bc7db4",
"total_modules": 10,
"passed": 2,
"failed": 8,
"modules": [
{
"name": "backend",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null,
"output": "Command not found: [Errno 2] No such file or directory: 'cargo'"
},
{
"name": "frontend",
"status": "PASS",
"elapsed_seconds": 3.823,
"artifact": "/Users/qingfeng/Desktop/\u81ea\u52a8\u642c\u7816/TentOfTrials-gautam-logger/frontend/dist",
"output": "> tent-frontend@0.0.0 build\n> tsc -b && vite build\n\nvite v6.4.3 building for production...\ntransforming...\n\u2713 100 modules transformed.\nrendering chunks...\ncomputing gzip size...\ndist/index.html 0.62 kB \u2502 gzip: 0.34 kB\ndist/assets/state-BkjSKDbY.js 8.91 kB \u2502 gzip: 3.55 kB \u2502 map: 57.15 kB\ndist/assets/vendor-CREcWLHI.js 48.93 kB \u2502 gzip: 17.22 kB \u2502 map: 481.27 kB\ndist/assets/index-CyxcoTyU.js 231.32 kB \u2502 gzip: 72.02 kB \u2502 map: 1,044.42 kB\n\u2713 built in 527ms"
},
{
"name": "market",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null,
"output": "Command not found: [Errno 2] No such file or directory: 'go'"
},
{
"name": "frailbox",
"status": "FAIL",
"elapsed_seconds": 0.032,
"artifact": null,
"output": "gcc -Wall -Wextra -Wpedantic -std=c2x -O2 -g -D_FORTIFY_SOURCE=3 -fstack-protector-strong -fPIE -Iinclude -MMD -MP -c src/arena.c -o build/src/arena.o\nsrc/arena.c:17:23: error: use of undeclared identifier 'MAP_HUGETLB'\n 17 | mmap_flags |= MAP_HUGETLB;\n | ^\nsrc/arena.c:179:17: warning: comparison of distinct pointer types ('const void *' and 'char *') [-Wcompare-distinct-pointer-types]\n 179 | ptr < (char *)region->start + region->size) {\n | ~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n1 warning and 1 error generated.\nmake: *** [build/src/arena.o] Error 1"
},
{
"name": "engine",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null,
"output": "Command not found: [Errno 2] No such file or directory: 'cmake'"
},
{
"name": "compliance",
"status": "FAIL",
"elapsed_seconds": 0.009,
"artifact": null,
"output": "The operation couldn\u2019t be completed. Unable to locate a Java Runtime.\nPlease visit http://www.java.com for information on installing Java."
},
{
"name": "v2-market-stream",
"status": "PASS",
"elapsed_seconds": 0.051,
"artifact": null,
"output": "Syntax OK"
},
{
"name": "nfc-scanner",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null,
"output": "Command not found: [Errno 2] No such file or directory: 'luac'"
},
{
"name": "openapi-haskell",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null,
"output": "Command not found: [Errno 2] No such file or directory: 'ghc'"
},
{
"name": "openapi-tools",
"status": "FAIL",
"elapsed_seconds": 0,
"artifact": null,
"output": "Command not found: [Errno 2] No such file or directory: 'luac'"
}
],
"pr_note": "Include the encrypted diagnostic logd artifact(s): diagnostic/build-2b54872c.logd. The encrypted .logd is the required diagnostic content for PR review; this JSON file is metadata. Maintainers may ask you to remove these diagnostic artifacts before merging."
}
Binary file added diagnostic/build-2b54872c.logd
Binary file not shown.
13 changes: 13 additions & 0 deletions docs/OPERATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,19 @@ FROM pg_stat_activity
WHERE state = 'idle' AND age > interval '1 hour';
```

## Frailbox Legacy Logger Error Handling

The frailbox legacy logger writes to `stderr` unless `LOG_FILE` is set. If the
configured file cannot be opened during startup, the logger reports the path and
system error to `stderr`, then continues using `stderr` so startup logging is not
lost.

If a configured log file later fails during write or flush, the logger reports
the failed operation, closes the bad stream, and falls back to `stderr` for the
current and future log messages. Shutdown also reports flush or close failures
instead of ignoring them. These fallbacks preserve existing logging behavior for
healthy files while making common filesystem failures visible to operators.

## Capacity Planning

### Resource Utilization
Expand Down
9 changes: 7 additions & 2 deletions frailbox/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ DEPS = $(OBJS:.o=.d)

TARGET = frailbox

.PHONY: all clean
.PHONY: all clean logger-error-test

all: $(TARGET)

Expand All @@ -37,7 +37,12 @@ distclean: clean
test: $(TARGET)
./$(TARGET) --sandbox-type seccomp --memory-limit 64 --verbose

logger-error-test:
@mkdir -p $(BUILDDIR)/tests
$(CC) $(CFLAGS) -I$(INCDIR) tests/test_logger_error_handling.c src/logger.c -o $(BUILDDIR)/tests/test_logger_error_handling -lpthread
./$(BUILDDIR)/tests/test_logger_error_handling

valgrind: $(TARGET)
valgrind --leak-check=full --show-leak-kinds=all ./$(TARGET)

.PHONY: all clean distclean test valgrind
.PHONY: all clean distclean test logger-error-test valgrind
103 changes: 92 additions & 11 deletions frailbox/src/logger.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ static int g_log_level = DEFAULT_LOG_LEVEL;
*/
static FILE *g_log_file = NULL;

/**
* Path of the configured log file. Kept only for diagnostics when the
* logger has to fall back to stderr after an I/O failure.
*/
static char g_log_file_path[512] = "";

/**
* Whether to include timestamps in log output.
* This can be disabled for performance-critical logging paths.
Expand Down Expand Up @@ -325,6 +331,47 @@ static void ring_buffer_push(const char *message)
pthread_mutex_unlock(&g_ring_buffer.ring_mutex);
}

static void set_log_file_path(const char *path)
{
if (path == NULL || path[0] == '\0') {
g_log_file_path[0] = '\0';
return;
}

strncpy(g_log_file_path, path, sizeof(g_log_file_path) - 1);
g_log_file_path[sizeof(g_log_file_path) - 1] = '\0';
}

static void report_log_file_error(const char *operation, const char *path, int err)
{
const char *display_path = (path != NULL && path[0] != '\0') ? path : "<stderr>";
const char *message = (err != 0) ? strerror(err) : "unknown error";

fprintf(stderr, "Legacy logger %s failed for '%s': %s\n",
operation, display_path, message);
}

static void fallback_to_stderr(FILE *failed_file, const char *operation, int err)
{
char failed_path[sizeof(g_log_file_path)];
strncpy(failed_path, g_log_file_path, sizeof(failed_path) - 1);
failed_path[sizeof(failed_path) - 1] = '\0';

report_log_file_error(operation, failed_path, err);
fprintf(stderr, "Legacy logger falling back to stderr.\n");

if (failed_file != NULL && failed_file != stderr) {
clearerr(failed_file);
if (fclose(failed_file) != 0) {
int close_err = errno != 0 ? errno : EIO;
report_log_file_error("close after fallback", failed_path, close_err);
}
}

g_log_file = stderr;
set_log_file_path(NULL);
}

/* ------------------------------------------------------------------ */
/* PUBLIC API */
/* ------------------------------------------------------------------ */
Expand Down Expand Up @@ -377,15 +424,19 @@ int log_init(void)

const char *env_log_file = getenv("LOG_FILE");
if (env_log_file != NULL && strlen(env_log_file) > 0) {
set_log_file_path(env_log_file);
g_log_file = fopen(env_log_file, "a");
if (g_log_file == NULL) {
fprintf(stderr, "Failed to open log file '%s': %s\n",
env_log_file, strerror(errno));
int open_err = errno != 0 ? errno : EIO;
report_log_file_error("open", env_log_file, open_err);
fprintf(stderr, "Legacy logger falling back to stderr.\n");
/* Fall back to stderr */
g_log_file = stderr;
set_log_file_path(NULL);
}
} else {
g_log_file = stderr;
set_log_file_path(NULL);
}

const char *env_module = getenv("LOG_MODULE");
Expand Down Expand Up @@ -525,13 +576,30 @@ void log_message(int level, const char *file, int line, const char *fmt, ...)
buffer[total_len + 1] = '\0';
}

/* Write to output */
if (g_log_file != NULL) {
fputs(buffer, g_log_file);
fflush(g_log_file);
} else {
fputs(buffer, stderr);
fflush(stderr);
/* Write to output. A configured file can become unwritable after init
* (disk full, revoked permissions, stale mount), so downgrade to stderr
* instead of silently dropping future logs. */
FILE *target = g_log_file != NULL ? g_log_file : stderr;
if (fputs(buffer, target) == EOF) {
int write_err = errno != 0 ? errno : EIO;
if (target != stderr) {
fallback_to_stderr(target, "write", write_err);
fputs(buffer, stderr);
fflush(stderr);
} else {
report_log_file_error("write", NULL, write_err);
clearerr(stderr);
}
} else if (fflush(target) == EOF) {
int flush_err = errno != 0 ? errno : EIO;
if (target != stderr) {
fallback_to_stderr(target, "flush", flush_err);
fputs(buffer, stderr);
fflush(stderr);
} else {
report_log_file_error("flush", NULL, flush_err);
clearerr(stderr);
}
}

pthread_mutex_unlock(&log_mutex);
Expand All @@ -551,12 +619,25 @@ void log_shutdown(void)
pthread_mutex_lock(&log_mutex);

if (g_log_file != NULL && g_log_file != stderr) {
fflush(g_log_file);
fclose(g_log_file);
char closing_path[sizeof(g_log_file_path)];
strncpy(closing_path, g_log_file_path, sizeof(closing_path) - 1);
closing_path[sizeof(closing_path) - 1] = '\0';

if (fflush(g_log_file) == EOF) {
int flush_err = errno != 0 ? errno : EIO;
report_log_file_error("shutdown flush", closing_path, flush_err);
clearerr(g_log_file);
}

if (fclose(g_log_file) != 0) {
int close_err = errno != 0 ? errno : EIO;
report_log_file_error("shutdown close", closing_path, close_err);
}
g_log_file = NULL;
}

g_log_level = LOG_LEVEL_NONE;
set_log_file_path(NULL);

pthread_mutex_unlock(&log_mutex);

Expand Down
Loading