From 8f1f04e9b5545dd967b78d44ceb414bc8f315ae7 Mon Sep 17 00:00:00 2001 From: Mika Date: Mon, 8 Dec 2025 09:42:01 +0000 Subject: [PATCH] Add trace_agg_logger/main.c --- trace_agg_logger/main.c | 161 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 trace_agg_logger/main.c diff --git a/trace_agg_logger/main.c b/trace_agg_logger/main.c new file mode 100644 index 0000000..e4b3eee --- /dev/null +++ b/trace_agg_logger/main.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include + +/* + * trace_agg_logger - kleines CLI-Tool zur Protokollierung von + * Aggregationsvorgaengen rund um trace_agg.py. + * + * Idee: + * - Wird von trace_agg.py (oder einem Test-Wrapper) als separates + * Kommandozeilenprogramm aufgerufen. + * - Nimmt eine frei formulierbare Lognachricht entgegen + * (z. B. JSON oder Key-Value-Paare) und schreibt diese + * zeilenbasiert in eine Logdatei. + * - Jede Zeile wird um einen einfachen Zeitstempel und ein Level + * ergaenzt. + * + * Nutzung (Beispiele): + * - ./trace_agg_logger "Aggregation started" + * - ./trace_agg_logger --level DEBUG "Filtered 496 clocksource events" + * - LOG_FILE=custom.log ./trace_agg_logger "custom target file" + * + * Rueckgabewerte: + * - 0 bei Erfolg + * - != 0 bei Fehler (z. B. Datei nicht schreibbar, fehlende Message) + * + * Diese Implementierung ist bewusst simpel gehalten, damit sie in + * Unit-/Integration-Tests leicht aufrufbar ist. + */ + +#define DEFAULT_LOG_FILE "trace_agg.log" +#define MAX_LEVEL_LEN 16 + +static void print_usage(const char *prog) +{ + fprintf(stderr, + "Usage: %s [--level LEVEL] message...\n" + "\n" + "Options:\n" + " --level LEVEL Log-Level, z.B. INFO, DEBUG, ERROR (Default: INFO)\n" + "\n" + "Die eigentliche Nachricht wird aus allen verbleibenden\n" + "Argumenten nach --level zusammengesetzt. Die Ausgabe erfolgt\n" + "zeilenweise in eine Logdatei. Der Pfad kann per Umgebungs-\n" + "variable LOG_FILE gesetzt werden (Default: %s).\n", + prog, DEFAULT_LOG_FILE); +} + +static const char *get_log_file_path(void) +{ + const char *path = getenv("LOG_FILE"); + if (path == NULL || path[0] == '\0') { + return DEFAULT_LOG_FILE; + } + return path; +} + +static void current_timestamp(char *buf, size_t buflen) +{ + /* Erzeugt einen einfachen Zeitstempel im Format YYYY-MM-DD HH:MM:SS */ + time_t now = time(NULL); + struct tm tm_now; + + if (now == (time_t)-1) { + /* Fallback: leerer Zeitstempel bei Fehler */ + if (buflen > 0) { + buf[0] = '\0'; + } + return; + } + + /* Lokale Zeit in tm umwandeln; thread-sichere Variante */ +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(__APPLE__) + localtime_r(&now, &tm_now); +#else + struct tm *tmp = localtime(&now); + if (!tmp) { + if (buflen > 0) { + buf[0] = '\0'; + } + return; + } + tm_now = *tmp; +#endif + + strftime(buf, buflen, "%Y-%m-%d %H:%M:%S", &tm_now); +} + +int main(int argc, char *argv[]) +{ + const char *level = "INFO"; + int msg_index = 1; /* Index des ersten Message-Arguments */ + int i; + + if (argc < 2) { + print_usage(argv[0]); + return 1; + } + + /* Einfache Argument-Parsing-Logik fuer --level */ + if (argc >= 4 && strcmp(argv[1], "--level") == 0) { + level = argv[2]; + msg_index = 3; + } + + if (msg_index >= argc) { + fprintf(stderr, "Error: missing log message.\n"); + print_usage(argv[0]); + return 1; + } + + /* Nachricht aus den verbleibenden Argumenten zusammenbauen */ + size_t total_len = 0; + for (i = msg_index; i < argc; ++i) { + total_len += strlen(argv[i]); + if (i + 1 < argc) { + total_len += 1; /* Leerzeichen zwischen Tokens */ + } + } + + char *message = (char *)malloc(total_len + 1); + if (!message) { + fprintf(stderr, "Error: failed to allocate memory for log message.\n"); + return 1; + } + + message[0] = '\0'; + for (i = msg_index; i < argc; ++i) { + strcat(message, argv[i]); + if (i + 1 < argc) { + strcat(message, " "); + } + } + + const char *log_path = get_log_file_path(); + FILE *fp = fopen(log_path, "a"); + if (!fp) { + fprintf(stderr, "Error: cannot open log file '%s': %s\n", + log_path, strerror(errno)); + free(message); + return 1; + } + + char ts[32]; + current_timestamp(ts, sizeof(ts)); + + /* Ausgabeformat: [TIMESTAMP] [LEVEL] message */ + if (fprintf(fp, "[%s] [%s] %s\n", ts, level, message) < 0) { + fprintf(stderr, "Error: failed to write to log file '%s'.\n", log_path); + fclose(fp); + free(message); + return 1; + } + + fclose(fp); + free(message); + + return 0; +}