diff --git a/rpmio/rpmlog.cc b/rpmio/rpmlog.cc index 6253e45d51..fdbbed8e5a 100644 --- a/rpmio/rpmlog.cc +++ b/rpmio/rpmlog.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -17,11 +18,22 @@ #include #include "debug.h" +struct pair_hash { + template + std::size_t operator () (const std::pair &p) const { + auto h1 = std::hash{}(p.first); + auto h2 = std::hash{}(p.second); + /* based on boost::hash_combine */ + return h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2); + } +}; + typedef struct rpmlogCtx_s * rpmlogCtx; struct rpmlogCtx_s { unsigned mask; int nrecsPri[RPMLOG_NPRIS]; std::vector recs; + std::unordered_map, int, pair_hash>> seen; rpmlogCallback cbfunc; rpmlogCallbackData cbdata; FILE *stdlog; @@ -41,7 +53,7 @@ using rdlock = std::shared_lock; static rpmlogCtx rpmlogCtxAcquire() { static struct rpmlogCtx_s _globalCtx = { RPMLOG_UPTO(RPMLOG_NOTICE), - {0}, {}, NULL, NULL, NULL }; + {0}, {}, {}, NULL, NULL, NULL }; return &_globalCtx; } @@ -127,6 +139,7 @@ void rpmlogClose (void) wrlock lock(ctx->mutex); ctx->recs.clear(); + ctx->seen.clear(); memset(ctx->nrecsPri, 0, sizeof(ctx->nrecsPri)); } @@ -412,3 +425,36 @@ void rpmlog (int code, const char *fmt, ...) exit: errno = saved_errno; } + +int rpmlogOnce (uint64_t domain, const char * key, int code, const char *fmt, ...) +{ + int saved_errno = errno; + rpmlogCtx ctx = rpmlogCtxAcquire(); + int newkey = 0; + + if (ctx) { + wrlock lock(ctx->mutex); + /* members get initialized automatically on first access */ + newkey = !(ctx->seen[domain][{code, key}]++); + } + + if (newkey) { + va_list ap; + char *msg = NULL; + va_start(ap, fmt); + if (rvasprintf(&msg, fmt, ap) >= 0) { + rpmlog(code, msg); + free(msg); + } + va_end(ap); + } + errno = saved_errno; + return newkey; +} + +void rpmlogReset(uint64_t domain, int mode=0) +{ + rpmlogCtx ctx = rpmlogCtxAcquire(); + wrlock lock(ctx->mutex); + ctx->seen.erase(domain); +} diff --git a/rpmio/rpmlog_internal.hh b/rpmio/rpmlog_internal.hh new file mode 100644 index 0000000000..8024eddc0c --- /dev/null +++ b/rpmio/rpmlog_internal.hh @@ -0,0 +1,23 @@ +#ifndef H_RPMLOG_INTERNAL +#define H_RPMLOG_INTERNAL 1 + + +/** \ingroup rpmlog + * Generate a log message using FMT string and option arguments. + * Only actually log on the first time passing the key value + * @param domain group of messages to be reset together + * @param key key to match log messages together + * @param code rpmlogLvl + * @param fmt format string and parameter to render + * @return 1 if actually logging 0 otherwise + */ +int rpmlogOnce (uint64_t domain, const char * key, int code, const char *fmt, ...) RPM_GNUC_PRINTF(4, 5); + +/** \ingroup rpmlog + * Clear memory of logmessages for a given domain + * @param domain group of messages to be reset together + * @param mode curretnly only 0 supported whihc drops everything + */ +void rpmlogReset(uint64_t domain, int mode=0); + +#endif