Add trace_agg_logger/main.c
This commit is contained in:
parent
514186c8e2
commit
8f1f04e9b5
1 changed files with 161 additions and 0 deletions
161
trace_agg_logger/main.c
Normal file
161
trace_agg_logger/main.c
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Loading…
Reference in a new issue