161 lines
4.3 KiB
C
161 lines
4.3 KiB
C
#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;
|
|
}
|