From dca5d04ad4ba1cc5fabf51b213f6384c1cb90367 Mon Sep 17 00:00:00 2001 From: Agustin Ranieri Date: Fri, 16 Aug 2024 09:32:46 -0300 Subject: [PATCH] Logs: Quito macro y mejoro errores (#186) * Logs: Quito macro y mejoro errores * Docs: Documento error.h * Tabs! --- README.md | 1 + docs/navbar.config.json | 1 + src/commons/error.c | 10 +- src/commons/error.h | 169 +++++++++++++++++++++++++- src/commons/log.c | 114 +++++++++-------- src/commons/temporal.c | 21 +++- tests/integration-tests/logger/main.c | 13 +- 7 files changed, 254 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 5fd9c428..b1caaa56 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Provee los siguientes TADs: * Manejo de fechas y timestamps (commons/temporal.h) * Información de procesos (commons/process.h) * Impresión de dumps de memoria (commons/memory.h) +* Impresión de errores (commons/error.h) * Manejo simple de archivos de texto (commons/txt.h) ## Notas diff --git a/docs/navbar.config.json b/docs/navbar.config.json index 908ec10f..ab018075 100644 --- a/docs/navbar.config.json +++ b/docs/navbar.config.json @@ -19,6 +19,7 @@ }, { "text": "bitarray.h", "url": "bitarray_8h.html" }, { "text": "config.h", "url": "config_8h.html" }, + { "text": "error.h", "url": "error_8h.html" }, { "text": "log.h", "url": "log_8h.html" }, { "text": "memory.h", "url": "memory_8h.html" }, { "text": "process.h", "url": "process_8h.html" }, diff --git a/src/commons/error.c b/src/commons/error.c index 8127e508..a17bdb29 100644 --- a/src/commons/error.c +++ b/src/commons/error.c @@ -16,6 +16,7 @@ #include "error.h" +#include #include #include #include @@ -23,16 +24,11 @@ #include "string.h" - void error_show(char *message, ...) { va_list arguments; va_start(arguments, message); - - char *error_message = string_duplicate("[[ERROR]]"); - string_append(&error_message, message); - - vprintf(error_message, arguments); - + char *error_message = string_from_format("[[ERROR]] %s: %s\n", message, strerror(errno)); + vfprintf(stderr, error_message, arguments); free(error_message); va_end(arguments); } diff --git a/src/commons/error.h b/src/commons/error.h index f05c839d..e29146b8 100644 --- a/src/commons/error.h +++ b/src/commons/error.h @@ -16,14 +16,171 @@ #ifndef ERROR_H_ #define ERROR_H_ - /** @cond INCLUDE_INTERNALS */ + /** + * @file + * @brief `#include ` + */ /** - * @brief imprime un mensaje con el siguiente formato - * [[ERROR]] MESSAGE + * @brief imprime por `stderr` un mensaje con el siguiente formato + * `[[ERROR]] {{message}}: {{errno}}` + * + * @note [errno](https://man7.org/linux/man-pages/man3/errno.3.html) es un + * número entero que representa el valor del último error ocurrido en el hilo + * actual. Varias funciones de la biblioteca estándar de C setean `errno` y + * devuelven un valor especial para indicar que hubo un error. Ejemplo: + * @code + * if (bind(...) == -1) { + * error_show("No se pudo hacer bind"); + * abort(); + * } + * + * => [[ERROR]] No se pudo hacer bind: address already in use + * @endcode + * + * La función utiliza + * [strerror](https://man7.org/linux/man-pages/man3/strerror.3.html) para + * imprimir la descripción del error. + * Estos son todos los valores posibles de `errno` con sus respectivas + * descripciones: + * + * | number | hex | symbol | description | + * |:------:|:---:|--------|-------------| + * | 1 | 0x01 | EPERM | Operation not permitted | + * | 2 | 0x02 | ENOENT | No such file or directory | + * | 3 | 0x03 | ESRCH | No such process | + * | 4 | 0x04 | EINTR | Interrupted system call | + * | 5 | 0x05 | EIO | Input/output error | + * | 6 | 0x06 | ENXIO | No such device or address | + * | 7 | 0x07 | E2BIG | Argument list too long | + * | 8 | 0x08 | ENOEXEC | Exec format error | + * | 9 | 0x09 | EBADF | Bad file descriptor | + * | 10 | 0x0a | ECHILD | No child processes | + * | 11 | 0x0b | EAGAIN | Resource temporarily unavailable | + * | 11 | 0x0b | EWOULDBLOCK | (Same value as EAGAIN) Resource temporarily unavailable | + * | 12 | 0x0c | ENOMEM | Cannot allocate memory | + * | 13 | 0x0d | EACCES | Permission denied | + * | 14 | 0x0e | EFAULT | Bad address | + * | 15 | 0x0f | ENOTBLK | Block device required | + * | 16 | 0x10 | EBUSY | Device or resource busy | + * | 17 | 0x11 | EEXIST | File exists | + * | 18 | 0x12 | EXDEV | Invalid cross-device link | + * | 19 | 0x13 | ENODEV | No such device | + * | 20 | 0x14 | ENOTDIR | Not a directory | + * | 21 | 0x15 | EISDIR | Is a directory | + * | 22 | 0x16 | EINVAL | Invalid argument | + * | 23 | 0x17 | ENFILE | Too many open files in system | + * | 24 | 0x18 | EMFILE | Too many open files | + * | 25 | 0x19 | ENOTTY | Inappropriate ioctl for device | + * | 26 | 0x1a | ETXTBSY | Text file busy | + * | 27 | 0x1b | EFBIG | File too large | + * | 28 | 0x1c | ENOSPC | No space left on device | + * | 29 | 0x1d | ESPIPE | Illegal seek | + * | 30 | 0x1e | EROFS | Read-only file system | + * | 31 | 0x1f | EMLINK | Too many links | + * | 32 | 0x20 | EPIPE | Broken pipe | + * | 33 | 0x21 | EDOM | Numerical argument out of domain | + * | 34 | 0x22 | ERANGE | Numerical result out of range | + * | 35 | 0x23 | EDEADLK | Resource deadlock avoided | + * | 35 | 0x23 | EDEADLOCK | (Same value as EDEADLK) Resource deadlock avoided | + * | 36 | 0x24 | ENAMETOOLONG | File name too long | + * | 37 | 0x25 | ENOLCK | No locks available | + * | 38 | 0x26 | ENOSYS | Function not implemented | + * | 39 | 0x27 | ENOTEMPTY | Directory not empty | + * | 40 | 0x28 | ELOOP | Too many levels of symbolic links | + * | 42 | 0x2a | ENOMSG | No message of desired type | + * | 43 | 0x2b | EIDRM | Identifier removed | + * | 44 | 0x2c | ECHRNG | Channel number out of range | + * | 45 | 0x2d | EL2NSYNC | Level 2 not synchronized | + * | 46 | 0x2e | EL3HLT | Level 3 halted | + * | 47 | 0x2f | EL3RST | Level 3 reset | + * | 48 | 0x30 | ELNRNG | Link number out of range | + * | 49 | 0x31 | EUNATCH | Protocol driver not attached | + * | 50 | 0x32 | ENOCSI | No CSI structure available | + * | 51 | 0x33 | EL2HLT | Level 2 halted | + * | 52 | 0x34 | EBADE | Invalid exchange | + * | 53 | 0x35 | EBADR | Invalid request descriptor | + * | 54 | 0x36 | EXFULL | Exchange full | + * | 55 | 0x37 | ENOANO | No anode | + * | 56 | 0x38 | EBADRQC | Invalid request code | + * | 57 | 0x39 | EBADSLT | Invalid slot | + * | 59 | 0x3b | EBFONT | Bad font file format | + * | 60 | 0x3c | ENOSTR | Device not a stream | + * | 61 | 0x3d | ENODATA | No data available | + * | 62 | 0x3e | ETIME | Timer expired | + * | 63 | 0x3f | ENOSR | Out of streams resources | + * | 64 | 0x40 | ENONET | Machine is not on the network | + * | 65 | 0x41 | ENOPKG | Package not installed | + * | 66 | 0x42 | EREMOTE | Object is remote | + * | 67 | 0x43 | ENOLINK | Link has been severed | + * | 68 | 0x44 | EADV | Advertise error | + * | 69 | 0x45 | ESRMNT | Srmount error | + * | 70 | 0x46 | ECOMM | Communication error on send | + * | 71 | 0x47 | EPROTO | Protocol error | + * | 72 | 0x48 | EMULTIHOP | Multihop attempted | + * | 73 | 0x49 | EDOTDOT | RFS specific error | + * | 74 | 0x4a | EBADMSG | Bad message | + * | 75 | 0x4b | EOVERFLOW | Value too large for defined data type | + * | 76 | 0x4c | ENOTUNIQ | Name not unique on network | + * | 77 | 0x4d | EBADFD | File descriptor in bad state | + * | 78 | 0x4e | EREMCHG | Remote address changed | + * | 79 | 0x4f | ELIBACC | Can not access a needed shared library | + * | 80 | 0x50 | ELIBBAD | Accessing a corrupted shared library | + * | 81 | 0x51 | ELIBSCN | .lib section in a.out corrupted | + * | 82 | 0x52 | ELIBMAX | Attempting to link in too many shared libraries | + * | 83 | 0x53 | ELIBEXEC | Cannot exec a shared library directly | + * | 84 | 0x54 | EILSEQ | Invalid or incomplete multibyte or wide character | + * | 85 | 0x55 | ERESTART | Interrupted system call should be restarted | + * | 86 | 0x56 | ESTRPIPE | Streams pipe error | + * | 87 | 0x57 | EUSERS | Too many users | + * | 88 | 0x58 | ENOTSOCK | Socket operation on non-socket | + * | 89 | 0x59 | EDESTADDRREQ | Destination address required | + * | 90 | 0x5a | EMSGSIZE | Message too long | + * | 91 | 0x5b | EPROTOTYPE | Protocol wrong type for socket | + * | 92 | 0x5c | ENOPROTOOPT | Protocol not available | + * | 93 | 0x5d | EPROTONOSUPPORT | Protocol not supported | + * | 94 | 0x5e | ESOCKTNOSUPPORT | Socket type not supported | + * | 95 | 0x5f | EOPNOTSUPP | Operation not supported | + * | 95 | 0x5f | ENOTSUP | (Same value as EOPNOTSUPP) Operation not supported | + * | 96 | 0x60 | EPFNOSUPPORT | Protocol family not supported | + * | 97 | 0x61 | EAFNOSUPPORT | Address family not supported by protocol | + * | 98 | 0x62 | EADDRINUSE | Address already in use | + * | 99 | 0x63 | EADDRNOTAVAIL | Cannot assign requested address | + * | 100 | 0x64 | ENETDOWN | Network is down | + * | 101 | 0x65 | ENETUNREACH | Network is unreachable | + * | 102 | 0x66 | ENETRESET | Network dropped connection on reset | + * | 103 | 0x67 | ECONNABORTED | Software caused connection abort | + * | 104 | 0x68 | ECONNRESET | Connection reset by peer | + * | 105 | 0x69 | ENOBUFS | No buffer space available | + * | 106 | 0x6a | EISCONN | Transport endpoint is already connected | + * | 107 | 0x6b | ENOTCONN | Transport endpoint is not connected | + * | 108 | 0x6c | ESHUTDOWN | Cannot send after transport endpoint shutdown | + * | 109 | 0x6d | ETOOMANYREFS | Too many references: cannot splice | + * | 110 | 0x6e | ETIMEDOUT | Connection timed out | + * | 111 | 0x6f | ECONNREFUSED | Connection refused | + * | 112 | 0x70 | EHOSTDOWN | Host is down | + * | 113 | 0x71 | EHOSTUNREACH | No route to host | + * | 114 | 0x72 | EALREADY | Operation already in progress | + * | 115 | 0x73 | EINPROGRESS | Operation now in progress | + * | 116 | 0x74 | ESTALE | Stale file handle | + * | 117 | 0x75 | EUCLEAN | Structure needs cleaning | + * | 118 | 0x76 | ENOTNAM | Not a XENIX named type file | + * | 119 | 0x77 | ENAVAIL | No XENIX semaphores available | + * | 120 | 0x78 | EISNAM | Is a named type file | + * | 121 | 0x79 | EREMOTEIO | Remote I/O error | + * | 122 | 0x7a | EDQUOT | Disk quota exceeded | + * | 123 | 0x7b | ENOMEDIUM | No medium found | + * | 124 | 0x7c | EMEDIUMTYPE | Wrong medium type | + * | 125 | 0x7d | ECANCELED | Operation canceled | + * | 126 | 0x7e | ENOKEY | Required key not available | + * | 127 | 0x7f | EKEYEXPIRED | Key has expired | + * | 128 | 0x80 | EKEYREVOKED | Key has been revoked | + * | 129 | 0x81 | EKEYREJECTED | Key was rejected by service | + * | 130 | 0x82 | EOWNERDEAD | Owner died | + * | 131 | 0x83 | ENOTRECOVERABLE | State not recoverable | + * | 132 | 0x84 | ERFKILL | Operation not possible due to RF-kill | + * | 133 | 0x85 | EHWPOISON | Memory page has hardware error | */ - void error_show(char *message, ...); - - /** @endcond */ + void error_show(char *message, ...) __attribute__((format(printf, 1, 2))); #endif /* ERROR_H_ */ diff --git a/src/commons/log.c b/src/commons/log.c index bc322504..476f705a 100644 --- a/src/commons/log.c +++ b/src/commons/log.c @@ -24,26 +24,19 @@ #include #include -#include + #define LOG_ENUM_SIZE 5 static char *enum_names[LOG_ENUM_SIZE] = {"TRACE", "DEBUG", "INFO", "WARNING", "ERROR"}; static char *log_colors[LOG_ENUM_SIZE] = {"\x1b[36m", "\x1b[32m", "", "\x1b[33m", "\x1b[31m" }; static char *reset_color = "\x1b[0m"; + /** * Private Functions */ static void _log_write_in_level(t_log* logger, t_log_level level, const char* message_template, va_list arguments); -static bool _isEnableLevelInLogger(t_log* logger, t_log_level level); - -#define log_impl_template(log_function, level_enum) \ - void log_function(t_log* logger, const char* message_template, ...) { \ - va_list arguments; \ - va_start(arguments, message_template); \ - _log_write_in_level(logger, level_enum, message_template, arguments); \ - va_end(arguments); \ - } \ +static bool _is_level_enabled(t_log* logger, t_log_level level); /** * Public Functions @@ -54,7 +47,7 @@ t_log* log_create(char* file, char *program_name, bool is_active_console, t_log_ t_log* logger = malloc(sizeof(t_log)); if (logger == NULL) { - perror("Cannot create logger"); + error_show("Cannot create logger"); return NULL; } @@ -64,7 +57,7 @@ t_log* log_create(char* file, char *program_name, bool is_active_console, t_log_ file_opened = txt_open_for_append(file); if (file_opened == NULL) { - perror("Cannot create/open log file"); + error_show("Cannot create/open log file"); free(logger); return NULL; } @@ -84,15 +77,40 @@ void log_destroy(t_log* logger) { free(logger); } -log_impl_template(log_trace, LOG_LEVEL_TRACE); +void log_trace(t_log* logger, const char* message_template, ...) { + va_list arguments; + va_start(arguments, message_template); + _log_write_in_level(logger, LOG_LEVEL_TRACE, message_template, arguments); + va_end(arguments); +} -log_impl_template(log_debug, LOG_LEVEL_DEBUG); +void log_debug(t_log* logger, const char* message_template, ...) { + va_list arguments; + va_start(arguments, message_template); + _log_write_in_level(logger, LOG_LEVEL_DEBUG, message_template, arguments); + va_end(arguments); +} -log_impl_template(log_info, LOG_LEVEL_INFO); +void log_info(t_log* logger, const char* message_template, ...) { + va_list arguments; + va_start(arguments, message_template); + _log_write_in_level(logger, LOG_LEVEL_INFO, message_template, arguments); + va_end(arguments); +} -log_impl_template(log_warning, LOG_LEVEL_WARNING); +void log_warning(t_log* logger, const char* message_template, ...) { + va_list arguments; + va_start(arguments, message_template); + _log_write_in_level(logger, LOG_LEVEL_WARNING, message_template, arguments); + va_end(arguments); +} -log_impl_template(log_error, LOG_LEVEL_ERROR); +void log_error(t_log* logger, const char* message_template, ...) { + va_list arguments; + va_start(arguments, message_template); + _log_write_in_level(logger, LOG_LEVEL_ERROR, message_template, arguments); + va_end(arguments); +} char *log_level_as_string(t_log_level level) { return enum_names[level]; @@ -103,9 +121,7 @@ char *log_level_color(t_log_level level) { } t_log_level log_level_from_string(char *level) { - int i; - - for (i = 0; i < LOG_ENUM_SIZE; i++) { + for (int i = 0; i < LOG_ENUM_SIZE; i++) { if (string_equals_ignore_case(level, enum_names[i])){ return i; } @@ -117,42 +133,40 @@ t_log_level log_level_from_string(char *level) { /** PRIVATE FUNCTIONS **/ static void _log_write_in_level(t_log* logger, t_log_level level, const char* message_template, va_list list_arguments) { + if (!_is_level_enabled(logger, level)) { + return; + } - if (_isEnableLevelInLogger(logger, level)) { - char *message, *time, *buffer, *console_buffer; - unsigned int thread_id; - - message = string_from_vformat(message_template, list_arguments); - time = temporal_get_string_time("%H:%M:%S:%MS"); - thread_id = process_get_thread_id(); - - buffer = string_from_format("[%s] %s %s/(%d:%d): %s\n", - log_level_as_string(level), - time, - logger->program_name, - logger->pid, - thread_id, - message); + char *message = string_from_vformat(message_template, list_arguments); + char *time = temporal_get_string_time("%H:%M:%S:%MS"); + unsigned int thread_id = process_get_thread_id(); - if (logger->file != NULL) { - txt_write_in_file(logger->file, buffer); - } + char *buffer = string_from_format("[%s] %s %s/(%d:%d): %s\n", + log_level_as_string(level), + time, + logger->program_name, + logger->pid, + thread_id, + message); - if (logger->is_active_console) { - console_buffer = string_from_format("%s%s%s", - log_level_color(level), - buffer, - reset_color); - txt_write_in_stdout(console_buffer); - free(console_buffer); - } + if (logger->file != NULL) { + txt_write_in_file(logger->file, buffer); + } - free(time); - free(message); - free(buffer); + if (logger->is_active_console) { + char *console_buffer = string_from_format("%s%s%s", + log_level_color(level), + buffer, + reset_color); + txt_write_in_stdout(console_buffer); + free(console_buffer); } + + free(time); + free(message); + free(buffer); } -static bool _isEnableLevelInLogger(t_log* logger, t_log_level level) { +static bool _is_level_enabled(t_log* logger, t_log_level level) { return level >= logger->detail; } diff --git a/src/commons/temporal.c b/src/commons/temporal.c index 81c5e19b..cb711a2e 100644 --- a/src/commons/temporal.c +++ b/src/commons/temporal.c @@ -55,7 +55,11 @@ t_temporal* temporal_create(void) { self->elapsed_ms = 0; self->status = TEMPORAL_STATUS_RUNNING; - clock_gettime(CLOCK_MONOTONIC_RAW, &self->current); + if (clock_gettime(CLOCK_MONOTONIC_RAW, &self->current) == -1) { + error_show("Error getting time!"); + free(self); + return NULL; + } return self; } @@ -89,7 +93,10 @@ void temporal_resume(t_temporal* temporal) { } temporal->status = TEMPORAL_STATUS_RUNNING; - clock_gettime(CLOCK_MONOTONIC_RAW, &temporal->current); + + if (clock_gettime(CLOCK_MONOTONIC_RAW, &temporal->current) == -1) { + error_show("Error getting time!"); + } } int64_t temporal_diff(t_temporal* temporal_1, t_temporal* temporal_2) { @@ -98,9 +105,11 @@ int64_t temporal_diff(t_temporal* temporal_1, t_temporal* temporal_2) { static int64_t calculate_delta_ms(t_temporal* temporal) { struct timespec now; - clock_gettime(CLOCK_MONOTONIC_RAW, &now); - - int64_t delta_ms = (now.tv_sec - temporal->current.tv_sec) * 1000 + (now.tv_nsec - temporal->current.tv_nsec) / 1000000; + if (clock_gettime(CLOCK_MONOTONIC_RAW, &now) == -1) { + error_show("Error getting time!"); + return 0; + } - return delta_ms; + return (now.tv_sec - temporal->current.tv_sec) * 1000 + + (now.tv_nsec - temporal->current.tv_nsec) / 1000000; } diff --git a/tests/integration-tests/logger/main.c b/tests/integration-tests/logger/main.c index 429ad081..2508eb6e 100644 --- a/tests/integration-tests/logger/main.c +++ b/tests/integration-tests/logger/main.c @@ -1,4 +1,4 @@ -/* +/* * File: main.c * Author: shinichi * @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,7 @@ static void log_in_disk(char* temp_file) { log_info(logger, "LOG A NIVEL %s", "INFO"); log_warning(logger, "LOG A NIVEL %s", "WARNING"); log_error(logger, "LOG A NIVEL %s", "ERROR"); + error_show("ERROR DE PRUEBA"); log_destroy(logger); free(temp_file); @@ -27,11 +29,11 @@ static void log_in_disk(char* temp_file) { int main(int argc, char** argv) { pthread_t th1, th2; - + char temp_file[] = "build/XXXXXX"; close(mkstemp(temp_file)); - + if (temp_file != NULL) { pthread_create(&th1, NULL, (void*) log_in_disk, string_duplicate(temp_file)); pthread_create(&th2, NULL, (void*) log_in_disk, string_duplicate(temp_file)); @@ -42,8 +44,7 @@ int main(int argc, char** argv) { } else { printf("No se pudo generar el archivo de log\n"); } - - + + return (EXIT_SUCCESS); } -