LINUX.ORG.RU

Ещё один логгер для С

 , ,


0

2

Без долгих вступлений. Ещё один логгер для С и на С11 с “перезгузкой”, не строгими уровнями логирования которые можно менять и после сборки проекта, двумя уровнями фильтрации, некоторые специфические особенности некоторых фильтров, переопределяемый формат вывода для всех сообщений и возможность определения формата атомарно для текущего вывода, возможность определить свои фильтры или форматы вывода, настройка потока вывода, возможность использования как обобщенного на весь проект, без необходимости каждый раз передавать указатель так отдельных указателей с разными настройками для каждого из логгеров.

Представляю на ваш суд. Ссылка на репозиторий GitHub

   if((*lvg)->out_offset > 0){
                strncpy((*lvg)->psett->out_buff, (*lvg)->out_buff, (*lvg)->psett->size_buff - 1);
                (*lvg)->psett->out_buff[(*lvg)->psett->size_buff - 1] = '\0';
                *(*lvg)->psett->out_size = strlen((*lvg)->psett->out_buff);
                if(((*lvg)->out_offset - (*lvg)->psett->size_buff >= 0)){
                    strncpy((*lvg)->tmp_buff, (*lvg)->out_buff + (*lvg)->psett->size_buff - 1, LOGGER_TMP_BUFF_SIZE);
                    strncpy((*lvg)->out_buff, (*lvg)->tmp_buff, LOGGER_OUT_BUFF_SIZE);
                    (*lvg)->out_offset -= (*lvg)->psett->size_buff - 1;
                }else{
                    (*lvg)->out_offset = 0;
                    return true;
                }
                return false;
            }

зачем писать треш такого рода? конструкция (*lvg)-> это двойная косвенность, которую надо сначала превратить в одинарную и потом делать без лишнего разименования (*lvg).

то есть написать

... xx = (*lvg);

и потом писать

xx->...

причем таких мест там в коде у вас много. «по научному» это вычисление общего подвыражения.

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

alysnix ★★★
()
Ответ на: комментарий от doperst
 if((filter->ptr[i].flag & (1ULL << (i)))
           &&
           (filter->ptr[i].flag != LOGGER_FLAG_ALL(uintmax_t))) {
            return LE_ERR;
        }

        if (filter->ptr[i].flag == LOGGER_FLAG_ALL(uintmax_t)) {
            filter->num = i;
            break;
        }

вот та же проблема - несколько раз берется флаг по сложному адресному выражению.

filter->ptr[i].flag

а в программировании любое выражение это команды процессора. потому такого рода выражения надо вычислять только один раз, это и повышает читабельность, убирает копипейстинг с возможностью ошибки, повышает скорость работы программы.

alysnix ★★★
()
Ответ на: комментарий от alysnix

Тут не страшно, это функция инициализации. Она прогоняется только один раз за всё время. Но, тоже согласен, можно один раз вычислить.

doperst
() автор топика
Ответ на: комментарий от doperst

еще хочу заметить что конструкции вида

(*ptr)->field

применяются в коде, где возможно перемещение обьекта, навроде системы со сборкой мусора. тогда вы имеете указатель ptr на память, где лежит актуальный адрес обьекта. вот поскольку акутальный адрес может меняться сборщиком, то надо писать двойное разименование. вычислить адрес один раз тут не получится, поскольку он может быть изменен… но вроде это не ваш случай.

alysnix ★★★
()
Ответ на: комментарий от IvanRia

это все сразу, это разыменование поля out_size, а lvg - указатель на указатель.

alysnix ★★★
()
Ответ на: комментарий от theNamelessOne

В заголовок я больше вкладывал иронию. Т.к. годных логеров я не нашел, а на форумах предлагают просто printf в #define заворачивать.

doperst
() автор топика
Ответ на: комментарий от doperst

Т.к. годных логеров я не нашел

Хех. И понятно почему. Всё гораздо сложнее чем Вы думаете. То что Вы пытаетесь сделать - tip of an iceberg. Кроме шуток.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

А где шутки? У меня появилась потребность в логере с двумя потоками фильтрации и без строгой иерархии. А потом немного развил эту идею. В своём решении я не гарантирую потокобезопасность и много много других мелких проблем.

Зачем два потока? Один для «вида» сообщения INFO ERROR WARNING и другие, второй для «типа» INIT MAIN DRVR и остальное что сам впишешь. Причем я могу вывести все INFO из INIT, или только WARNING без ERROR.

Могу отформатировать вывод как мне нужно. Например:

.logs_format = "LOGS ## $line%03. -- | $type%.15 $name%.08 >> $func%10. $mesg%.10 << $file%20.",

Выдаст:

LOGS ## 025 -- | L_BEGF           VOID     >>   main  Start            << ..._logger/using/advanced/main.c
LOGS ## 026 -- | L_DEBUG          VOID     >>   main  Foo fnk start    << ..._logger/using/advanced/main.c
LOGS ## 011 -- | L_BEGF           FOO      >>    foo  im a foo         << ..._logger/using/advanced/main.c
LOGS ## 013 -- | L_DEBUG          FOO      >>    foo  im a debug       << ..._logger/using/advanced/main.c
LOGS ## 015 -- | L_TODO           FOO      >>    foo  im a todo        << ..._logger/using/advanced/main.c
LOGS ## 017 -- | L_ENDF           FOO      >>    foo  return to foo    << ..._logger/using/advanced/main.c
LOGS ## 030 -- | L_ENDF           VOID     >>   main  end              << ..._logger/using/advanced/main.c

Изменив в том же примере одну строку на:

.logs_format = "$data_curent $time_curent $func%.5 $line%.3 $type%.8 $name%.5 $recnum%2. >> $rectab $mesg",

Получим:

2022.11.20 02:49:03 main  25  L_BEGF   VOID   0 >>  Start
2022.11.20 02:49:03 main  26  L_DEBUG  VOID   0 >>  Foo fnk start
2022.11.20 02:49:03 foo   11  L_BEGF   FOO    1 >> 	 im a foo
2022.11.20 02:49:03 foo   13  L_DEBUG  FOO    1 >> 	 im a debug
2022.11.20 02:49:03 foo   15  L_TODO   FOO    1 >> 	 im a todo
2022.11.20 02:49:03 foo   17  L_ENDF   FOO    1 >> 	 return to foo
2022.11.20 02:49:03 main  30  L_ENDF   VOID   0 >>  end
doperst
() автор топика
Ответ на: комментарий от doperst

В своём решении я не гарантирую потокобезопасность

Т.е. за скобками оставили одну из самых интересных вещей.

и много много других мелких проблем.

Дьявол в деталях. Я видел весьма много логгеров в своей жизни. С довольно разной областью применения (вплоть до сброса потенциально длинных сообщений в mmap’d files, когда ждать «ну никак нельзя»). Прежде чем я начну смотреть на код (API на самом деле интересует гораздо больше) - Вы можете в 2х словах обрисовать чем именно то что Вы предлагаете лучше чем, например, syslog?

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 1)
Ответ на: комментарий от bugfixer

Не гарантирую, потому что еще не делал тестов и не проверял. Под мелкими, я имею в виду это статическое выделение памяти, но работа в этом направлении уже идет, буферизированый вывода пока реализован как просто в массив, есть планы сделать кольцевой буфер. Это чтобы под NoOS использовать.

Если коротко чем отличается. То почти всем. Я бы назвал своё решение как Front-end для своих методов вывода. Можно даже syslog обернуть и получить все фишки которые предоставляет eё инфраструктура. Но при этом на стороне программы использовать подробное описание конкретного места логгирования.

Если начать рассматривать список параметров моей функции логгирования.

logger_error yaya_log_func (uintmax_t count, const char *file, uintmax_t line, const char *func, const char* generic, void* logger_ptr, logger_l1_type level_one, logger_l2_type level_two, const char *mes, ...);

Но т.к. это всё обернуто в макрос(ы), большая часть скрыта, а оставшаяся вариативна.

То её можно вызывать как:

loggerf(); //без каких либо параметров
loggerf(L_WARNING); //только тип(уровень) сообщения
loggerf(L_WARNING, AUDIO_DRV); //тип(уровень) и название сообщения
loggerf(AUDIO_DRV); //только тип сообщения
loggerf(L_WARNING, AUDIO_DRV, "Not find devise"); //тип, название и текст сообщения 
loggerf(AUDIO_DRV, "Not find devise");
loggerf(L_WARNING, "Not find devise");
loggerf("Not find devise");

То есть можно миксовать как пожелаешь (Сохраняя порядок следования).

loggerf(ptr, L1, L2, "print format msg", ...);

Есть специальные L1 типы, тригеры. Например: loggerf(L_ATOM, "$type_filter $mesg", "- L1 filters"); через который можно вывести всё тоже самое что и при настройке .logs_format (см. выше) но по месту.

Можно завести два отдельных логера в одной программе.

void *log1 = NULL;
void *log2 = NULL;

loggerf_init(&log1);
loggerf_init(&log2);

loggerf(log1, "Hello");
loggerf(log2, "World");

И настроить их так чтобы один писал в файл, а второй в консоль.

doperst
() автор топика
Ответ на: комментарий от Kroz

Там рассматриваются С++ логгеры, а у меня С11. Насколько это корректное сравнение? И что сравнивать? Насколько быстро у работают стандартный fputs и fwrite с предсборкой строки вывода strcat-ом.

doperst
() автор топика
Ответ на: комментарий от bugfixer

лучше чем, например, syslog?

например syslog после fork у вас уйдет в deadlock.
сейчас рулит sd_journal_print

anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.