Очень часто при разработке небольших утилит сталкиваюсь с проблемой вывода сообщений от этих самых утилит куда бы то ни было. «Продвинутые логгеры» настолько тяжелы, что их использование даже не рассматривается. В результате, каждый раз изобретается один и тот же маленький велосипед.

Представляю простейший в мире логгер для С, который поддерживает вывод как в файл, так и в консоль и форматированную запись. Под катом представлен исходный код H и C файлов, а также архивчик с исходником. Кроме того, он поддерживает вывод в стандартный syslogd для Linux.

Файл log.h.


/** @file log.h */
/** @author Egor 'khaos' Zelensky. */
/** @copyright Public domain. */

#pragma once

#include <stdio.h>
#include <stdbool.h>

#define	LOG_EMERG	0	/**< system is unusable */
#define	LOG_ALERT	1	/**< action must be taken immediately */
#define	LOG_CRIT	2	/**< critical conditions */
#define	LOG_ERR		3	/**< error conditions */
#define	LOG_WARNING	4	/**< warning conditions */
#define	LOG_NOTICE	5	/**< normal but significant condition */
#define	LOG_INFO	6	/**< informational */
#define	LOG_DEBUG	7	/**< debug-level messages */

/** @brief Represents log entry priority value with associated string representation. */
typedef struct log_code_t {
	int value;			/**< Log entry priority value. */
	char* name;			/**< String respresentation. */
} log_code;

#define LOG_MASK(p)	(1 << (p))
#define LOG_UPTO(p)	((1 << ((p) + 1)) - 1)

/** @brief Opens new log with specified name for writing. */
/** @param log Name of the log. */
/** @param fp  File pointer to use if any, NULL otherwise. */
/** @return Boolean value indicating success or not. */
bool log_open(const char* log, FILE* fp);

#ifdef __unix__
/** @brief Opens a new log with Linux syslogd. Log always opens with LOG_USER facility. */
/** @param log Name of the log. */
void log_open_syslog(const char* log);
#endif

/** @brief Sets log priority mask. */
/** @param mask Priority mask to set. */
void log_prioritymask(char mask);

/** @brief Writes a new entry to the log. */
/** @param priority Pritority of the entry. */
/** @param message Message to write. */
void log_entry(char priority, const char* message, ...);

/** @brief Closes current log. */
void log_close(void);

Файл log.c.


/** @file log.c */
/** @author Egor 'khaos' Zelensky. */
/** @copyright Public domain. */

#include "log.h"
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>

#ifdef __unix__
#include <syslog.h>
#endif

FILE* _logfp;
bool _closefp;
bool _usesyslog;
char _priority_mask;

log_code priority_names[] = {
	LOG_EMERG, "EMERGENCY",
	LOG_ALERT, "ALERT",
	LOG_CRIT, "CRITICAL",
	LOG_ERR, "ERROR",
	LOG_WARNING, "WARNING",
	LOG_NOTICE, "NOTICE",
	LOG_INFO, "INFO",
	LOG_DEBUG, "DEBUG"
};

bool log_open(const char* name, FILE* fp) {
	int len;
	char* buf;
	
	if (_logfp != NULL) {
		fclose(_logfp);
	}

	if (fp == NULL) {
		len = strlen(name);
		buf = malloc(len + 5);
		buf[0] = '\0';
		strcpy(buf, name);
		strcat(buf, ".log");

		_logfp = fopen(buf, "a+");

		free(buf);

		if (_logfp == NULL) {
			return false;
		}
	}
	else {
		_logfp = fp;
	}

	_priority_mask = 255;
	_usesyslog = false;
	_closefp = true;
	return true;
}

#ifdef __unix__
void log_open_syslog(const char* log) {
	openlog(log, LOG_PID, LOG_USER);
	_usesyslog = true;
}
#endif

void log_prioritymask(char mask) {
	_priority_mask = mask;
}

void log_close(void) {
	if (!_usesyslog) {
		if (!_closefp) {
			return;
		}

		if (_logfp == NULL) {
			return;
		}

		fclose(_logfp);
	}
#ifdef __unix__
	else {
		closelog();
	}
#endif
}

void log_entry(char priority, const char* message, ...) {
	va_list args;
	time_t t;
	struct tm* timeinfo;
	char buf[25];

	if ((priority & _priority_mask) == 0) {
		return;
	}

	if (!_usesyslog) {
		if (_logfp == NULL) {
			return;
		}

		time(&t);
		timeinfo = localtime(&t);
		strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", timeinfo);


		va_start(args, message);
		fprintf(_logfp, "%s [%s]: ", buf, priority_names[priority].name);
		vfprintf(_logfp, message, args);
		fprintf(_logfp, "\n");
		fflush(_logfp);
		va_end(args);
	}
#ifdef __unix__
	else {
		syslog(priority, message, __VA_ARGS__);
	}
#endif
}

Использование такое же простое:


log_open("someprogram", stdout);
log_entry(LOG_INFO, "This stuff works! This line will be written to stdout.");
log_close();

log_open("otherprogram", NULL);
log_entry(LOG_INFO, "This line will be written to file `otherprogram.log`.");
log_close();
comments powered by HyperComments