diff --git a/migrations/versions/006d4747f858_.py b/migrations/versions/006d4747f858_.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a8cf94e9af781d7f8543fb51af1cfed1cb46da2
--- /dev/null
+++ b/migrations/versions/006d4747f858_.py
@@ -0,0 +1,30 @@
+"""v3.8: Add thread_id to audit_log
+
+Revision ID: 006d4747f858
+Revises: d3c0f0403a84
+Create Date: 2022-11-30 22:37:42.163199
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '006d4747f858'
+down_revision = 'd3c0f0403a84'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.exc import OperationalError, ProgrammingError, InternalError
+
+
+def upgrade():
+    try:
+        op.add_column('pidea_audit', sa.Column('thread_id', sa.Unicode(length=20), nullable=True))
+    except (OperationalError, ProgrammingError, InternalError) as exx:
+        print("Looks like the thread_id already exists in the pidea_audit table.")
+        print(exx)
+    except Exception as exx:
+        print("Could not add thread_id to pidea_audit table.")
+        print (exx)
+
+
+def downgrade():
+    op.drop_column('pidea_audit', 'thread_id')
diff --git a/migrations/versions/d3c0f0403a84_.py b/migrations/versions/d3c0f0403a84_.py
new file mode 100644
index 0000000000000000000000000000000000000000..5dcd5f565058a1d0ff5ecad3b8f021ca0091ca7e
--- /dev/null
+++ b/migrations/versions/d3c0f0403a84_.py
@@ -0,0 +1,22 @@
+"""v3.8: Merging revisions
+
+Revision ID: d3c0f0403a84
+Revises: ('fabcf24d9304', 'a28f2733897b')
+Create Date: 2022-11-30 22:37:19.986083
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'd3c0f0403a84'
+down_revision = ('fabcf24d9304', 'a28f2733897b')
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    pass
+
+
+def downgrade():
+    pass
diff --git a/privacyidea/api/auth.py b/privacyidea/api/auth.py
index afaae523a1b6e0acdbc0139ff9da815c0fc01c05..1ef10d886174bdab4daa736ad6dbe8b120f62a7e 100644
--- a/privacyidea/api/auth.py
+++ b/privacyidea/api/auth.py
@@ -75,6 +75,7 @@ from privacyidea.lib.event import event, EventConfiguration
 from privacyidea.lib import _
 import logging
 import traceback
+import threading
 
 log = logging.getLogger(__name__)
 
@@ -106,6 +107,7 @@ def before_request():
                         "privacyidea_server": privacyidea_server,
                         "action": "{0!s} {1!s}".format(request.method, request.url_rule),
                         "action_detail": "",
+                        "thread_id": u"{0!s}".format(threading.current_thread().ident),
                         "info": ""})
 
     username = getParam(request.all_data, "username")
diff --git a/privacyidea/api/before_after.py b/privacyidea/api/before_after.py
index 742d9352c2cff36eddc92be3d4293eb8c97aaf3a..5b93f8ceec015195deb97949499b7ed0e7038434 100644
--- a/privacyidea/api/before_after.py
+++ b/privacyidea/api/before_after.py
@@ -74,6 +74,7 @@ from ..lib.error import (privacyIDEAError,
 from privacyidea.lib.utils import get_client_ip
 from privacyidea.lib.user import User
 import datetime
+import threading
 
 log = logging.getLogger(__name__)
 
@@ -223,6 +224,7 @@ def before_request():
                         "privacyidea_server": privacyidea_server,
                         "action": "{0!s} {1!s}".format(request.method, request.url_rule),
                         "action_detail": "",
+                        "thread_id": u"{0!s}".format(threading.current_thread().ident),
                         "info": ""})
 
     if g.logged_in_user.get("role") == "admin":
diff --git a/privacyidea/lib/auditmodules/base.py b/privacyidea/lib/auditmodules/base.py
index 56e9998d335d58ff603b1a8ad08e48dda5255e2b..cb582efecfaa112d8cda15ef72e10f5c5404490b 100644
--- a/privacyidea/lib/auditmodules/base.py
+++ b/privacyidea/lib/auditmodules/base.py
@@ -143,7 +143,7 @@ class Audit(object):  # pragma: no cover
                 'duration', 'token_type', 'user', 'realm', 'administrator',
                 'action_detail', 'info', 'privacyidea_server', 'client',
                 'log_level', 'policies', 'clearance_level', 'sig_check',
-                'missing_line', 'resolver']
+                'missing_line', 'resolver', 'thread_id']
 
     def get_total(self, param, AND=True, display_error=True, timelimit=None):
         """
diff --git a/privacyidea/lib/auditmodules/sqlaudit.py b/privacyidea/lib/auditmodules/sqlaudit.py
index a960512375be2db2343d587f32fa85eb4990df43..f181edf8569a589df00938e69e25723dd62afa29 100755
--- a/privacyidea/lib/auditmodules/sqlaudit.py
+++ b/privacyidea/lib/auditmodules/sqlaudit.py
@@ -305,7 +305,8 @@ class Audit(AuditBase):
                           clearance_level=self.audit_data.get("clearance_level"),
                           policies=self.audit_data.get("policies"),
                           startdate=self.audit_data.get("startdate"),
-                          duration=duration
+                          duration=duration,
+                          thread_id=self.audit_data.get("thread_id")
                           )
             self.session.add(le)
             self.session.commit()
@@ -375,6 +376,7 @@ class Audit(AuditBase):
         :type le: LogEntry
         :rtype str
         """
+        # TODO: Add thread_id. We really should add a versioning to identify which audit data is signed.
         s = u"id=%s,date=%s,action=%s,succ=%s,serial=%s,t=%s,u=%s,r=%s,adm=%s," \
             u"ad=%s,i=%s,ps=%s,c=%s,l=%s,cl=%s" % (le.id,
                                                    le.date,
@@ -420,7 +422,8 @@ class Audit(AuditBase):
                     'client': LogEntry.client,
                     'log_level': LogEntry.loglevel,
                     'policies': LogEntry.policies,
-                    'clearance_level': LogEntry.clearance_level}
+                    'clearance_level': LogEntry.clearance_level,
+                    'thread_id': LogEntry.thread_id}
         return sortname.get(key)
 
     def csv_generator(self, param=None, user=None, timelimit=None):
@@ -587,4 +590,5 @@ class Audit(AuditBase):
         audit_dict['clearance_level'] = audit_entry.clearance_level
         audit_dict['startdate'] = audit_entry.startdate.isoformat() if audit_entry.startdate else None
         audit_dict['duration'] = audit_entry.duration.total_seconds() if audit_entry.duration else None
+        audit_dict['thread_id'] = audit_entry.thread_id
         return audit_dict
diff --git a/privacyidea/models.py b/privacyidea/models.py
index 4101aa3ed4a0d49bbea197cc107baeafd9766f0e..81b1a170d2dc8259a1c445226ef6895d07e6d424 100644
--- a/privacyidea/models.py
+++ b/privacyidea/models.py
@@ -2747,6 +2747,7 @@ audit_column_length = {"signature": 620,
                        "client": 50,
                        "loglevel": 12,
                        "clearance_level": 12,
+                       "thread_id": 20,
                        "policies": 255}
 AUDIT_TABLE_NAME = 'pidea_audit'
 
@@ -2780,6 +2781,7 @@ class Audit(MethodsMixin, db.Model):
     loglevel = db.Column(db.Unicode(audit_column_length.get("loglevel")))
     clearance_level = db.Column(db.Unicode(audit_column_length.get(
         "clearance_level")))
+    thread_id = db.Column(db.Unicode(audit_column_length.get("thread_id")))
     policies = db.Column(db.Unicode(audit_column_length.get("policies")))
 
     def __init__(self,
@@ -2797,6 +2799,7 @@ class Audit(MethodsMixin, db.Model):
                  client="",
                  loglevel="default",
                  clearance_level="default",
+                 thread_id="0",
                  policies="",
                  startdate=None,
                  duration=None
@@ -2819,6 +2822,7 @@ class Audit(MethodsMixin, db.Model):
         self.client = convert_column_to_unicode(client)
         self.loglevel = convert_column_to_unicode(loglevel)
         self.clearance_level = convert_column_to_unicode(clearance_level)
+        self.thread_id = convert_column_to_unicode(thread_id)
         self.policies = convert_column_to_unicode(policies)
 
 
diff --git a/privacyidea/static/components/audit/views/audit.log.html b/privacyidea/static/components/audit/views/audit.log.html
index 60c8c04d7eb5a7beefae9f9655fe6ac6115772a3..78cd1cda82604a0097f27560227103ac41d2349f 100644
--- a/privacyidea/static/components/audit/views/audit.log.html
+++ b/privacyidea/static/components/audit/views/audit.log.html
@@ -115,6 +115,7 @@
                 </th>
                 <th ng-show="audit_columns.includes('sig_check')" translate>sig_check</th>
                 <th ng-show="audit_columns.includes('missing_line')" translate>missing_line</th>
+                <th ng-show="audit_columns.includes('thread_id')" translate>thread_id</th>
                 <th ng-show="audit_columns.includes('clearance_level')" translate>clearance</th>
                 <th ng-show="audit_columns.includes('log_level')" translate>log level</th>
                 <th ng-show="audit_columns.includes('privacyidea_server')"
@@ -170,6 +171,7 @@
             </span></td>
                 <td ng-if="audit_columns.includes('missing_line')"><span status-class="{{ audit.missing_line }}">
                 {{ audit.missing_line }}</span></td>
+                <td ng-if="audit_columns.includes('thread_id')">{{ audit.thread_id }}</td>
                 <td ng-if="audit_columns.includes('clearance_level')">{{ audit.clearance_level }}</td>
                 <td ng-if="audit_columns.includes('log_level')">{{ audit.log_level }}</td>
                 <td ng-if="audit_columns.includes('privacyidea_server')">{{ audit.privacyidea_server }}</td>
diff --git a/tests/test_api_audit.py b/tests/test_api_audit.py
index 205abd7c95ef07cf107e009c075e4bcbdfe16cd8..1b6712b3911de64ec623c3fddad760244f5a9f63 100644
--- a/tests/test_api_audit.py
+++ b/tests/test_api_audit.py
@@ -74,7 +74,7 @@ class APIAuditTestCase(MyApiTestCase):
             cols = json_response.get("result").get("value").get("auditcolumns")
             self.assertIn("number", cols)
             self.assertIn("serial", cols)
-            self.assertEqual(21, len(cols))
+            self.assertEqual(22, len(cols))
 
     def test_01_get_audit_csv(self):
         @contextmanager
diff --git a/tests/testdata/audit.sqlite b/tests/testdata/audit.sqlite
index 24167945e5abaad7652e0c1a7be6345f87c29338..042ec81840de2acb5dea3826f0a30e5ba7bb5875 100644
Binary files a/tests/testdata/audit.sqlite and b/tests/testdata/audit.sqlite differ