Логирование данных в файл на С.

jenya7
Дата: 20.03.2018 12:30:44
У меня по всему коду разбросанны сообщения типа
printf ("LIB ID = %d\n", lib_id);
сообщения выводятся серийно на терминал.
Сейчас нужно некоторые сообщения логировать в файл на SD карте.
Возникли вопросы по алгоритмике логирования данных
1. Прежде всего проверить есть ли место на SD - вопрос как это сделать? и если нет места? очистить файл и начать писать сначала?
2. Если файл открыт - добавить данные в файл, посмотреть флаг закрыть файл или нет.(я не хочу часто дергать файл - открывать\закрывать)
тогда что - держать глобальный указатель на файл?
3.Если файл закрыт - открыть с опцией "а+", добавить данные в файл, посмотреть флаг закрыть файл или нет.
Как вообще сделать покрасивше?
Akina
Дата: 20.03.2018 12:34:48
jenya7
если нет места? очистить файл и начать писать сначала?
Сообщить о недостатке места и завершить работу или запросить новое местоположение лога. Нельзя уничтожать чужие данные.
Dima T
Дата: 20.03.2018 12:54:56
jenya7
У меня по всему коду разбросанны сообщения типа
printf ("LIB ID = %d\n", lib_id);
сообщения выводятся серийно на терминал.
Сейчас нужно некоторые сообщения логировать в файл на SD карте.
Возникли вопросы по алгоритмике логирования данных
1. Прежде всего проверить есть ли место на SD - вопрос как это сделать? и если нет места? очистить файл и начать писать сначала?

Рекомендую периодически начинать новый файл, а старый переименовывать. Например при достижении какого-то размера.
Что делать со старыми тебе видней, если старая инфа неинтересна, то можно удалять через какое-то время.
jenya7
2. Если файл открыт - добавить данные в файл, посмотреть флаг закрыть файл или нет.(я не хочу часто дергать файл - открывать\закрывать)
тогда что - держать глобальный указатель на файл?

ИМХО лучше функцию отдельную сделать и там всю логику прописать
void write_log(const char* data, ...) {
  static FILE* f = NULL;
  if(f == NULL) {
     f = fopen(...
jenya7
3.Если файл закрыт - открыть с опцией "а+", добавить данные в файл, посмотреть флаг закрыть файл или нет.
Как вообще сделать покрасивше?

Если файл не закрываешь, то обязательно fflush() после записи, т.к. иначе при вылете проги часть лога может остаться в буфере ОС и не запишется на диск.
jenya7
Дата: 20.03.2018 13:37:53
Dima T
jenya7
У меня по всему коду разбросанны сообщения типа
printf ("LIB ID = %d\n", lib_id);
сообщения выводятся серийно на терминал.
Сейчас нужно некоторые сообщения логировать в файл на SD карте.
Возникли вопросы по алгоритмике логирования данных
1. Прежде всего проверить есть ли место на SD - вопрос как это сделать? и если нет места? очистить файл и начать писать сначала?

Рекомендую периодически начинать новый файл, а старый переименовывать. Например при достижении какого-то размера.
Что делать со старыми тебе видней, если старая инфа неинтересна, то можно удалять через какое-то время.
jenya7
2. Если файл открыт - добавить данные в файл, посмотреть флаг закрыть файл или нет.(я не хочу часто дергать файл - открывать\закрывать)
тогда что - держать глобальный указатель на файл?

ИМХО лучше функцию отдельную сделать и там всю логику прописать
void write_log(const char* data, ...) {
  static FILE* f = NULL;
  if(f == NULL) {
     f = fopen(...
jenya7
3.Если файл закрыт - открыть с опцией "а+", добавить данные в файл, посмотреть флаг закрыть файл или нет.
Как вообще сделать покрасивше?

Если файл не закрываешь, то обязательно fflush() после записи, т.к. иначе при вылете проги часть лога может остаться в буфере ОС и не запишется на диск.

Что то вроде этого?
FILE *g_log_fp;
int g_log_file_size;

void LOG_LogMessage(char *file, char *message, int value, int close)
{
	int file_size = 0;
	
	char buff[256] = { '\0' };
	char sval[10];
	
	ItoA(value, sval);
	strcat(buff, message);
	strcat(buff, sval);

	if (g_log_fp == NULL)
	{
		g_log_fp = fopen(file, "a+");
		
		if (g_log_fp != NULL)
			fputs(buff, g_log_fp);
	}
	else
		fputs(buff, g_log_fp);
	
	fseek(g_log_fp, 0, SEEK_SET);
	fseek(g_log_fp, 0, SEEK_END);
	file_size = ftell(g_log_fp); 
			
	if (close)
		fclose(g_log_fp);
	else
		fflush(g_log_fp); 
	
	if (file_size >= MAX_LOG_FILE_SIZE)
	{
		//LOG_Clear() ???
		//go to the next file ???
	}	
}
Dima T
Дата: 20.03.2018 13:46:04
Делай переменное количество параметров, как у printf(). Пример тут http://www.cplusplus.com/reference/cstdio/vsprintf/?kw=vsprintf

И не надо fseek(). Ты же знаешь размер добавляемого. При открытии посчитай текущий размер, а дальше к нему добавляй размер записанного и 1-2 байта на перевод строки.

Передавать имя файла в параметрах тоже не очень хорошая идея. Сделай глобальную переменную.
Basil A. Sidorov
Дата: 20.03.2018 13:51:41
Не особо свежее, но готовое - zlog.
jenya7
Дата: 20.03.2018 14:01:37
Basil A. Sidorov
Не особо свежее, но готовое - zlog.

фига се! там же монстр! :)
jenya7
Дата: 20.03.2018 14:26:35
Dima T
Делай переменное количество параметров, как у printf(). Пример тут http://www.cplusplus.com/reference/cstdio/vsprintf/?kw=vsprintf

И не надо fseek(). Ты же знаешь размер добавляемого. При открытии посчитай текущий размер, а дальше к нему добавляй размер записанного и 1-2 байта на перевод строки.

Передавать имя файла в параметрах тоже не очень хорошая идея. Сделай глобальную переменную.

Пока что получилось такое
FILE *g_log_fp;
int g_log_file_size;
char *glob_file_name;

void LOG_LogMessage(char *message, int value, int close)
{
	int file_size = 0;
	
	char buff[256] = { '\0' };
	char sval[10];
	
	ItoA(value, sval);
	strcat(buff, message);
	strcat(buff, sval);

	if (g_log_fp == NULL)
	{
		g_log_fp = fopen(glob_file_name, "a+");
		
		if (g_log_fp != NULL)
			fputs(buff, g_log_fp);
	}
	else
		fputs(buff, g_log_fp);
	
	//fseek(g_log_fp, 0, SEEK_SET);
	fseek(g_log_fp, 0, SEEK_END);
	file_size = ftell(g_log_fp); 
	g_log_file_size += file_size;
			
	if (close)
		fclose(g_log_fp);
	else
		fflush(g_log_fp); 
	
	if (g_log_file_size >= MAX_LOG_FILE_SIZE)
	{
		//LOG_Clear() ???
		//go to the next file ???
	}	
}

void LOG_LogString(char *message, int close)
{
	if (g_log_fp == NULL)
	{
		g_log_fp = fopen(glob_file_name, "a+");
			
		if (g_log_fp != NULL)
		    fputs(message, g_log_fp);
	}
	else
	    fputs(message, g_log_fp);
}

void LOG(char *message, int *value, int close, int log_option)
{
	switch (log_option)
	{
	    case LOG_ON_TERMINAL:
	    	if (value != NULL)
	    		printf(message, *value);
	    	else
	    		printf(message);
	    break;
	    case LOG_ON_FILE:
	    	if (value != NULL)
	    		LOG_LogMessage(message, *value,  close);
	    	else
	    		LOG_LogString(message, close);
	    break;
	    case LOG_ON_ALL:
	    	if (value != NULL)
	    	{
	    		printf(message, *value);
	    		LOG_LogMessage(message, *value,  close);
	    	}
	    	else
	    	{
	    		printf(message);
	    		LOG_LogString(message, close);
	    	}
	    break;
	}
jenya7
Дата: 20.03.2018 14:42:50
Dima T
Делай переменное количество параметров, как у printf(). Пример тут http://www.cplusplus.com/reference/cstdio/vsprintf/?kw=vsprintf.

Придется делать парсинг формата как в printf() а это уже через чур.
Dima T
Дата: 20.03.2018 14:49:23
Лучше проверку в начало
void LOG_LogMessage(char *message, int value, int close)
{
	if (g_log_file_size >= MAX_LOG_FILE_SIZE)
	{
		//LOG_Clear() ???
		//go to the next file ???
// Закрыть файл и переименовать например в glob_file_name + ".YYYYMMDD_N"
fclose(g_log_fp);
g_log_fp = NULL;
	}	
...


ИМХО close не надо постоянно передавать, лучше сделай отдельно LOG_Close()