7 n__nodup__log_8c
gullradriel edited this page 2026-10-29 20:54:40 +01:00

src/n_nodup_log.c

generic no duplicate logging system More...

Functions

Name
char * get_nodup_key(const char * file, const char * func, int line)
internal, get a key for a log entry
char * get_nodup_indexed_key(const char * file, const char * func, const char * prefix, int line)
internal, get a key for an indexed log entry
int check_n_log_dup_indexed(const char * log, const char * file, const char * func, int line, const char * prefix)
check if a log was already done or not at the given line, func, file
int check_n_log_dup(const char * log, const char * file, const char * func, int line)
check if a log was already done or not at the given line, func, file

Attributes

Name
HASH_TABLE * _n_nodup_table
internal: no dup hash_table log save

Defines

Name
_GNU_SOURCE_WAS_NOT_DEFINED
flag to detect if gnu source was set from our source file
_GNU_SOURCE
Feature test macro.

Detailed Description

generic no duplicate logging system

Author: Castagnier Mickael

Date: 2015-09-21

Functions Documentation

function get_nodup_key

static char * get_nodup_key(
    const char * file,
    const char * func,
    int line
)

internal, get a key for a log entry

Parameters:

  • file log source file
  • func log source
  • line log source

Return: The key for the the log entry

function get_nodup_indexed_key

static char * get_nodup_indexed_key(
    const char * file,
    const char * func,
    const char * prefix,
    int line
)

internal, get a key for an indexed log entry

Parameters:

  • file log source file
  • func log source
  • line log source
  • prefix prefix for log entry

Return: The key for the the log entry

function check_n_log_dup_indexed

int check_n_log_dup_indexed(
    const char * log,
    const char * file,
    const char * func,
    int line,
    const char * prefix
)

check if a log was already done or not at the given line, func, file

Parameters:

  • log the line of log
  • file the file containing the log
  • func the name of calling func
  • line the line of the log
  • prefix prefix to subgroup logs

Return: 0 if not existing 1 if existing with the same value , 2 if key exist and value is different

function check_n_log_dup

int check_n_log_dup(
    const char * log,
    const char * file,
    const char * func,
    int line
)

check if a log was already done or not at the given line, func, file

Parameters:

  • log the line of log
  • file the file containing the log
  • func the name of calling func
  • line the line of the log

Return: 0 if not existing 1 if existing with the same value , 2 if key exist and value is different

Attributes Documentation

variable _n_nodup_table

static HASH_TABLE * _n_nodup_table = NULL;

internal: no dup hash_table log save

Macros Documentation

define _GNU_SOURCE_WAS_NOT_DEFINED

#define _GNU_SOURCE_WAS_NOT_DEFINED 

flag to detect if gnu source was set from our source file

define _GNU_SOURCE

#define _GNU_SOURCE 

Feature test macro.

GNU source

Source code

/*
 * Nilorea Library
 * Copyright (C) 2005-2026 Castagnier Mickael
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#define _GNU_SOURCE_WAS_NOT_DEFINED
#endif
#include <stdio.h>
#include <stdarg.h>
#ifdef _GNU_SOURCE_WAS_NOT_DEFINED
#undef _GNU_SOURCE
#endif

#include "nilorea/n_common.h"
#include "nilorea/n_log.h"
#include "nilorea/n_str.h"
#include "nilorea/n_list.h"
#include "nilorea/n_hash.h"

#include "nilorea/n_nodup_log.h"

static HASH_TABLE* _n_nodup_table = NULL;

int init_nodup_log(size_t max) {
    if (_n_nodup_table != NULL) {
        n_log(LOG_ERR, "Could not allocate the internal hash table because it's already done");
        return FALSE;
    }
    if (max == 0)
        max = 1024;

    _n_nodup_table = new_ht(max);

    if (_n_nodup_table == NULL) {
        n_log(LOG_ERR, "Could not allocate the internal hash with size %zu", max);
        return FALSE;
    } else {
        n_log(LOG_DEBUG, "LOGGING: nodup system activated with a hash table of %zu cells", max);
    }

    return TRUE;
} /* init_nodup_log() */

int empty_nodup_table() {
    __n_assert(_n_nodup_table, return FALSE);
    return empty_ht(_n_nodup_table);
} /* empty_nodup_table() */

int close_nodup_log() {
    __n_assert(_n_nodup_table, return FALSE);
    return destroy_ht(&_n_nodup_table);
} /* close_nodup_log() */

static char* get_nodup_key(const char* file, const char* func, int line) {
    N_STR* nstr = NULL;
    char* ptr = NULL;
    nstrprintf(nstr, "%s%s%d", file, func, line);
    __n_assert(nstr, return NULL);
    ptr = nstr->data;
    Free(nstr);
    return ptr;
} /* get_nodup_key */

static char* get_nodup_indexed_key(const char* file, const char* func, const char* prefix, int line) {
    N_STR* nstr = NULL;
    char* ptr = NULL;
    nstrprintf(nstr, "%s%s%s%d", file, func, prefix, line);
    __n_assert(nstr, return NULL);
    ptr = nstr->data;
    Free(nstr);
    return ptr;
} /* get_nodup_indexed_key */

int check_n_log_dup(const char* log, const char* file, const char* func, int line) {
    const HASH_NODE* node = NULL;
    char* key = NULL;

    /* check if the nopdup session is on, else do a normal n_log */
    if (!_n_nodup_table) {
        return 3;
    }

    key = get_nodup_key(file, func, line);

    __n_assert(key, return FALSE);

    node = ht_get_node(_n_nodup_table, key);

    Free(key);

    if (node) {
        if (strcmp(log, node->data.string) == 0) {
            return 1;
        }
        return 2;
    }
    return 0;

} /* check_n_log_dup(...) */

int check_n_log_dup_indexed(const char* log, const char* file, const char* func, int line, const char* prefix) {
    const HASH_NODE* node = NULL;
    char* key = NULL;

    /* check if the nopdup session is on, else do a normal n_log */
    if (!_n_nodup_table) {
        return 3;
    }

    key = get_nodup_indexed_key(file, func, prefix, line);

    __n_assert(key, return FALSE);

    node = ht_get_node(_n_nodup_table, key);

    Free(key);

    if (node) {
        if (strcmp(log, node->data.string) == 0) {
            return 1;
        }
        return 2;
    }
    return 0;
} /* check_nolog_dup_indexed() */

void _n_nodup_log(int LEVEL, const char* file, const char* func, int line, const char* format, ...) {
    __n_assert(file, return);
    __n_assert(func, return);
    __n_assert(format, return);

    HASH_NODE* node = NULL;
    va_list args;

    char* syslogbuffer = NULL;

    va_start(args, format);
    if (vasprintf(&syslogbuffer, format, args) == -1) {
        int error = errno;
        n_log(LOG_ERR, "=>%s:%s:%d unable to parse '%s', %s", file, func, line, format, strerror(error));
        va_end(args);
        syslogbuffer = NULL;
        return;
    }
    va_end(args);

    char* key = get_nodup_key(file, func, line);
    int is_dup = check_n_log_dup(syslogbuffer, file, func, line);

    switch (is_dup) {
        /* new log entry for file,func,line */
        case 0:
            if (_n_nodup_table) {
                ht_put_string(_n_nodup_table, key, syslogbuffer);
            }
            _n_log(LEVEL, file, func, line, "%s", syslogbuffer);
            break;

        /* exising and same log entry, do nothing (maybe latter we will add a timeout to repeat logging)  */
        case 1:
            break;
        /* existing but different entry. We have to refresh and log it one time*/
        case 2:
            node = ht_get_node(_n_nodup_table, key);
            if (node) {
                Free(node->data.string);
                node->data.string = syslogbuffer;
                syslogbuffer = NULL;
            }
            _n_log(LEVEL, file, func, line, "%s", node ? node->data.string : "");
            break;
        /* no nodup session started, normal loggin */
        case 3:
        default:
            _n_log(LEVEL, file, func, line, "%s", syslogbuffer);
            break;
    }

    Free(syslogbuffer);
    Free(key);

} /* _n_nodup_log() */

void _n_nodup_log_indexed(int LEVEL, const char* prefix, const char* file, const char* func, int line, const char* format, ...) {
    HASH_NODE* node = NULL;
    va_list args;

    char* syslogbuffer = NULL;

    va_start(args, format);
    if (vasprintf(&syslogbuffer, format, args) == -1) {
        int error = errno;
        n_log(LOG_ERR, "=>%s:%s:%d unable to parse '%s:%s', %s", file, func, line, prefix, format, strerror(error));
        va_end(args);
        syslogbuffer = NULL;
        return;
    }
    va_end(args);

    char* key = get_nodup_indexed_key(file, func, prefix, line);
    int is_dup = check_n_log_dup_indexed(syslogbuffer, file, func, line, prefix);

    switch (is_dup) {
        /* new log entry for file,func,line */
        case 0:
            if (_n_nodup_table) {
                ht_put_string(_n_nodup_table, key, syslogbuffer);
            }
            _n_log(LEVEL, file, func, line, "%s", syslogbuffer);
            break;

        /* exising and same log entry, do nothing (maybe latter we will add a timeout to repeat logging)  */
        case 1:
            break;
        /* existing but different entry. We have to refresh and log it one time*/
        case 2:
            node = ht_get_node(_n_nodup_table, key);
            if (node) {
                Free(node->data.string);
                node->data.string = syslogbuffer;
                syslogbuffer = NULL;
            }
            _n_log(LEVEL, file, func, line, "%s", node ? node->data.string : "");
            break;
        /* no nodup session started, normal loggin */
        case 3:
        default:
            _n_log(LEVEL, file, func, line, "%s", syslogbuffer);
            break;
    }

    Free(syslogbuffer);
    Free(key);

} /* _n_nodup_log_indexed() */

int dump_nodup_log(char* file) {
    FILE* out = NULL;
    __n_assert(file, return FALSE);
    __n_assert(_n_nodup_table, return FALSE);

    char* tmpfile = NULL;
    n_log(LOG_DEBUG, "outfile:%s", file);
    strprintf(tmpfile, "%s.tmp", file);
    if (!tmpfile) {
        n_log(LOG_ERR, "could not create tmp file name from filename %s", _str(file));
        return FALSE;
    }

    int fd = open(tmpfile, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (fd < 0) {
        n_log(LOG_ERR, "could not create file %s with 0644 permissions", _str(tmpfile));
        Free(tmpfile);
        return FALSE;
    }

    out = fdopen(fd, "wb");
    __n_assert(out, close(fd); Free(tmpfile); return FALSE);

    if (_n_nodup_table) {
        for (unsigned long int it = 0; it < _n_nodup_table->size; it++) {
            list_foreach(list_node, _n_nodup_table->hash_table[it]) {
                const HASH_NODE* hash_node = (const HASH_NODE*)list_node->ptr;
                fprintf(out, "%s\n", hash_node->data.string);
            }
        }
    }
    fclose(out);

    if (rename(tmpfile, file) != 0) {
        int error = errno;
        n_log(LOG_ERR, "could not rename '%s' to '%s' , %s", tmpfile, file,
              strerror(error));
    }
    Free(tmpfile);

    return TRUE;

} /* dump_nodup_log() */