diff --git a/include/rpm/rpmts.h b/include/rpm/rpmts.h
index dc5b31c55c..6090f1704b 100644
--- a/include/rpm/rpmts.h
+++ b/include/rpm/rpmts.h
@@ -663,6 +663,16 @@ int rpmtsGetNotifyStyle(rpmts ts);
  */
 int rpmtsSetChangeCallback(rpmts ts, rpmtsChangeFunction notify, void *data);
 
+/** \ingroup rpmts
+ * Log messages with the same key just once
+ * @param ts		transaction set
+ * @param key		key to match log messages together
+ * @param code		rpmlogLvl
+ * @param fmt		format string and parameter to render
+ * @return		0 for the first call with this key value 1 otherwise
+ */
+int rpmtsLogOnce(rpmts ts, const char * key, int code, const char * fmt, ...) RPM_GNUC_PRINTF(4, 5);
+
 /** \ingroup rpmts
  * Create an empty transaction set.
  * @return		new transaction set
diff --git a/lib/package.cc b/lib/package.cc
index 1267522fe4..5460b6645c 100644
--- a/lib/package.cc
+++ b/lib/package.cc
@@ -27,6 +27,7 @@ typedef struct pkgdata_s *pkgdatap;
 typedef void (*hdrvsmsg)(struct rpmsinfo_s *sinfo, pkgdatap pkgdata, const char *msg);
 
 struct pkgdata_s {
+    rpmts ts;
     hdrvsmsg msgfunc;
     const char *fn;
     char *msg;
@@ -109,28 +110,6 @@ rpmTagVal headerMergeLegacySigs(Header h, Header sigh, char **msg)
     return xl->stag;
 }
 
-/**
- * Remember current key id.
- * XXX: This s*** needs to die. Hook it into keyring or sumthin...
- * @param keyid		signature keyid
- * @return		0 if new keyid, otherwise 1
- */
-static int stashKeyid(unsigned int keyid)
-{
-    static std::mutex keyid_mutex;
-    static std::set<unsigned int> keyids;
-    int seen = 0;
-
-    if (keyid == 0)
-	return 0;
-
-    std::lock_guard<std::mutex> lock(keyid_mutex);
-    auto ret = keyids.insert(keyid);
-    seen = (ret.second == false);
-
-    return seen;
-}
-
 static int handleHdrVS(struct rpmsinfo_s *sinfo, void *cbdata)
 {
     struct pkgdata_s *pkgdata = (struct pkgdata_s *)cbdata;
@@ -287,14 +266,19 @@ static void loghdrmsg(struct rpmsinfo_s *sinfo, struct pkgdata_s *pkgdata,
 			const char *msg)
 {
     int lvl = RPMLOG_DEBUG;
+    char * signid = NULL;
+    int printed = 0;
     switch (sinfo->rc) {
     case RPMRC_OK:		/* Signature is OK. */
 	break;
     case RPMRC_NOTTRUSTED:	/* Signature is OK, but key is not trusted. */
     case RPMRC_NOKEY:		/* Public key is unavailable. */
 	/* XXX Print NOKEY/NOTTRUSTED warning only once. */
-	if (stashKeyid(sinfo->keyid) == 0)
-	    lvl = RPMLOG_WARNING;
+	signid = rpmhex(pgpDigParamsSignID(sinfo->sig), PGP_KEYID_LEN);
+	printed = rpmtsLogOnce(pkgdata->ts, signid, RPMLOG_WARNING, "%s: %s\n", pkgdata->fn, msg);
+	free(signid);
+	if (!printed)
+	    goto exit;
 	break;
     case RPMRC_NOTFOUND:	/* Signature/digest not present. */
 	lvl = RPMLOG_WARNING;
@@ -306,6 +290,8 @@ static void loghdrmsg(struct rpmsinfo_s *sinfo, struct pkgdata_s *pkgdata,
     }
 
     rpmlog(lvl, "%s: %s\n", pkgdata->fn, msg);
+ exit:
+    ;
 }
 
 rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char * fn, Header * hdrp)
@@ -319,6 +305,7 @@ rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char * fn, Header * hdrp)
     rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
     struct rpmvs_s *vs = rpmvsCreate(0, vsflags, keyring);
     struct pkgdata_s pkgdata = {
+	.ts = ts,
 	.msgfunc = loghdrmsg,
 	.fn = fn ? fn : Fdescr(fd),
 	.msg = NULL,
diff --git a/lib/rpmts.cc b/lib/rpmts.cc
index c014b2b049..8fda592a7c 100644
--- a/lib/rpmts.cc
+++ b/lib/rpmts.cc
@@ -1260,6 +1260,26 @@ int rpmtsSetChangeCallback(rpmts ts, rpmtsChangeFunction change, void *data)
     return 0;
 }
 
+int rpmtsLogOnce(rpmts ts, const char * key, int code, const char * fmt, ...)
+{
+    int saved_errno = errno;
+    va_list ap;
+    char *msg = NULL;
+    int seen = 0;
+    auto ret = ts->logged_once.insert(key);
+    seen = (ret.second == false);
+    if (!seen) {
+	va_start(ap, fmt);
+	if (rvasprintf(&msg, fmt, ap) >= 0) {
+	    rpmlog(code, msg);
+	    free(msg);
+	}
+	va_end(ap);
+    }
+    errno = saved_errno;
+    return seen;
+}
+
 tsMembers rpmtsMembers(rpmts ts)
 {
     return (ts != NULL) ? ts->members : NULL;
@@ -1368,6 +1388,7 @@ rpmts rpmtsCreate(void)
     ts->trigs2run = rpmtriggersCreate(10);
 
     ts->min_writes = (rpmExpandNumeric("%{?_minimize_writes}") > 0);
+    ts->logged_once = {};
 
     return rpmtsLink(ts);
 }
diff --git a/lib/rpmts_internal.hh b/lib/rpmts_internal.hh
index ded10ec092..2517ad6514 100644
--- a/lib/rpmts_internal.hh
+++ b/lib/rpmts_internal.hh
@@ -5,6 +5,7 @@
 #include <unordered_map>
 #include <vector>
 #include <atomic>
+#include <set>
 
 #include <rpm/rpmts.h>
 #include <rpm/rpmstrpool.h>
@@ -100,6 +101,7 @@ struct rpmts_s {
     int min_writes;             /*!< macro minimize_writes used */
 
     time_t overrideTime;	/*!< Time value used when overriding system clock. */
+    std::set<std::string> logged_once; /* Messages already logged */
 };
 
 /** \ingroup rpmts
diff --git a/lib/rpmvs.cc b/lib/rpmvs.cc
index 6d95363aff..d423a7f6df 100644
--- a/lib/rpmvs.cc
+++ b/lib/rpmvs.cc
@@ -196,7 +196,6 @@ static void rpmsinfoInit(const struct vfyinfo_s *vinfo,
 	char *lints = NULL;
         int ec = pgpPrtParams2((const uint8_t *)data, dlen, PGPTAG_SIGNATURE,
 				&sinfo->sig, &lints);
-	const uint8_t *signid;
 	if (ec) {
 	    if (lints) {
 		rasprintf(&sinfo->msg,
@@ -214,8 +213,6 @@ static void rpmsinfoInit(const struct vfyinfo_s *vinfo,
 	    free(lints);
 	}
 	sinfo->hashalgo = pgpDigParamsAlgo(sinfo->sig, PGPVAL_HASHALGO);
-	signid = pgpDigParamsSignID(sinfo->sig);	/* 8 bytes key id */
-	sinfo->keyid = signid[4] << 24 | signid[5] << 16 | signid[6] << 8 | signid[7];
     } else if (sinfo->type == RPMSIG_DIGEST_TYPE) {
 	if (td->type == RPM_BIN_TYPE) {
 	    sinfo->dig = rpmhex((const uint8_t *)data, dlen);