prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Better separation of audit log from privacyidea*
  Hi,
  
  python logging may be easily separated using the qualname. However, privacyidea uses the module/class names. Since they all start with "privacyidea.", it is not possible to log the audit to one place and all the rest to a different place (python logging cannot *exclude* qualnames).
  
  To solve this, one could use a custom qualname for the privacyidea audit. I think here:
  https://github.com/privacyidea/privacyidea/blob/ea7d9e53d42504288ba3909f7057924fe8d250b0/privacyidea/lib/auditmodules/loggeraudit.py#L62
  
  Best regards,
  
  Henning
  
  
  
  </issue>
  <code>
  [start of privacyidea/lib/auditmodules/loggeraudit.py]
  1 # -*- coding: utf-8 -*-
  2 #
  3 #  2019-11-06 Cornelius Kölbel <cornelius.koelbel@netknights.it>
  4 #             initial code for writing audit information to a file
  5 #
  6 # This code is free software; you can redistribute it and/or
  7 # modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  8 # License as published by the Free Software Foundation; either
  9 # version 3 of the License, or any later version.
  10 #
  11 # This code is distributed in the hope that it will be useful,
  12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 # GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  15 #
  16 # You should have received a copy of the GNU Affero General Public
  17 # License along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18 #
  19 #
  20 __doc__ = """The Logger Audit Module is used to write audit entries to the Python logging module.
  21 
  22 The Logger Audit Module is configured like this:
  23 
  24     PI_AUDIT_MODULE = "privacyidea.lib.auditmodules.loggeraudit"
  25     PI_AUDIT_SERVERNAME = "your choice"
  26 
  27     PI_LOGCONFIG = "/etc/privacyidea/logging.cfg"
  28 
  29 The LoggerAudit Class uses the same PI logging config as you could use anyways.
  30 To explicitly write audit logs, you need to add something like the following to
  31 the logging.cfg
  32 
  33 Example:
  34 
  35 [handlers]
  36 keys=file,audit
  37 
  38 [loggers]
  39 keys=root,privacyidea,audit
  40 
  41 ...
  42 
  43 [logger_audit]
  44 handlers=audit
  45 qualname=privacyidea.lib.auditmodules.loggeraudit
  46 level=INFO
  47 
  48 [handler_audit]
  49 class=logging.handlers.RotatingFileHandler
  50 backupCount=14
  51 maxBytes=10000000
  52 formatter=detail
  53 level=INFO
  54 args=('/var/log/privacyidea/audit.log',)
  55 
  56 """
  57 
  58 import logging
  59 from privacyidea.lib.auditmodules.base import (Audit as AuditBase)
  60 import datetime
  61 
  62 log = logging.getLogger(__name__)
  63 
  64 
  65 class Audit(AuditBase):
  66     """
  67     This is the LoggerAudit module, which writes the audit entries
  68     to the Python logging
  69 
  70     .. note:: This audit module does not provide a *Read* capability.
  71     """
  72 
  73     def __init__(self, config=None):
  74         super(Audit, self).__init__(config)
  75         self.name = "loggeraudit"
  76 
  77     def finalize_log(self):
  78         """
  79         This method is used to log the data
  80         e.g. write the data to a file.
  81         """
  82         self.audit_data["policies"] = ",".join(self.audit_data.get("policies", []))
  83         self.audit_data["timestamp"] = datetime.datetime.utcnow()
  84         log.info(u"{0!s}".format(self.audit_data))
  85         self.audit_data = {}
  86 
  87 
  88
  [end of privacyidea/lib/auditmodules/loggeraudit.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/privacyidea/lib/auditmodules/loggeraudit.py b/privacyidea/lib/auditmodules/loggeraudit.py
  --- a/privacyidea/lib/auditmodules/loggeraudit.py
  +++ b/privacyidea/lib/auditmodules/loggeraudit.py
  @@ -56,10 +56,9 @@
   """
   
   import logging
  +import json
   from privacyidea.lib.auditmodules.base import (Audit as AuditBase)
  -import datetime
  -
  -log = logging.getLogger(__name__)
  +from datetime import datetime
   
   
   class Audit(AuditBase):
  @@ -73,6 +72,8 @@
       def __init__(self, config=None):
           super(Audit, self).__init__(config)
           self.name = "loggeraudit"
  +        self.qualname = self.config.get('PI_AUDIT_LOGGER_QUALNAME', __name__)
  +        self.logger = logging.getLogger(self.qualname)
   
       def finalize_log(self):
           """
  @@ -80,8 +81,6 @@
           e.g. write the data to a file.
           """
           self.audit_data["policies"] = ",".join(self.audit_data.get("policies", []))
  -        self.audit_data["timestamp"] = datetime.datetime.utcnow()
  -        log.info(u"{0!s}".format(self.audit_data))
  +        self.audit_data["timestamp"] = datetime.utcnow().isoformat()
  +        self.logger.info("{0!s}".format(json.dumps(self.audit_data, sort_keys=True)))
           self.audit_data = {}
  -
  -
  
verification_info: {"golden_diff": "diff --git a/privacyidea/lib/auditmodules/loggeraudit.py b/privacyidea/lib/auditmodules/loggeraudit.py\n--- a/privacyidea/lib/auditmodules/loggeraudit.py\n+++ b/privacyidea/lib/auditmodules/loggeraudit.py\n@@ -56,10 +56,9 @@\n \"\"\"\n \n import logging\n+import json\n from privacyidea.lib.auditmodules.base import (Audit as AuditBase)\n-import datetime\n-\n-log = logging.getLogger(__name__)\n+from datetime import datetime\n \n \n class Audit(AuditBase):\n@@ -73,6 +72,8 @@\n     def __init__(self, config=None):\n         super(Audit, self).__init__(config)\n         self.name = \"loggeraudit\"\n+        self.qualname = self.config.get('PI_AUDIT_LOGGER_QUALNAME', __name__)\n+        self.logger = logging.getLogger(self.qualname)\n \n     def finalize_log(self):\n         \"\"\"\n@@ -80,8 +81,6 @@\n         e.g. write the data to a file.\n         \"\"\"\n         self.audit_data[\"policies\"] = \",\".join(self.audit_data.get(\"policies\", []))\n-        self.audit_data[\"timestamp\"] = datetime.datetime.utcnow()\n-        log.info(u\"{0!s}\".format(self.audit_data))\n+        self.audit_data[\"timestamp\"] = datetime.utcnow().isoformat()\n+        self.logger.info(\"{0!s}\".format(json.dumps(self.audit_data, sort_keys=True)))\n         self.audit_data = {}\n-\n-\n", "issue": "Better separation of audit log from privacyidea*\nHi,\r\n\r\npython logging may be easily separated using the qualname. However, privacyidea uses the module/class names. Since they all start with \"privacyidea.\", it is not possible to log the audit to one place and all the rest to a different place (python logging cannot *exclude* qualnames).\r\n\r\nTo solve this, one could use a custom qualname for the privacyidea audit. I think here:\r\nhttps://github.com/privacyidea/privacyidea/blob/ea7d9e53d42504288ba3909f7057924fe8d250b0/privacyidea/lib/auditmodules/loggeraudit.py#L62\r\n\r\nBest regards,\r\n\r\nHenning\r\n\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n#  2019-11-06 Cornelius K\u00f6lbel <cornelius.koelbel@netknights.it>\n#             initial code for writing audit information to a file\n#\n# This code is free software; you can redistribute it and/or\n# modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE\n# License as published by the Free Software Foundation; either\n# version 3 of the License, or any later version.\n#\n# This code is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU AFFERO GENERAL PUBLIC LICENSE for more details.\n#\n# You should have received a copy of the GNU Affero General Public\n# License along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\n#\n__doc__ = \"\"\"The Logger Audit Module is used to write audit entries to the Python logging module.\n\nThe Logger Audit Module is configured like this:\n\n    PI_AUDIT_MODULE = \"privacyidea.lib.auditmodules.loggeraudit\"\n    PI_AUDIT_SERVERNAME = \"your choice\"\n\n    PI_LOGCONFIG = \"/etc/privacyidea/logging.cfg\"\n\nThe LoggerAudit Class uses the same PI logging config as you could use anyways.\nTo explicitly write audit logs, you need to add something like the following to\nthe logging.cfg\n\nExample:\n\n[handlers]\nkeys=file,audit\n\n[loggers]\nkeys=root,privacyidea,audit\n\n...\n\n[logger_audit]\nhandlers=audit\nqualname=privacyidea.lib.auditmodules.loggeraudit\nlevel=INFO\n\n[handler_audit]\nclass=logging.handlers.RotatingFileHandler\nbackupCount=14\nmaxBytes=10000000\nformatter=detail\nlevel=INFO\nargs=('/var/log/privacyidea/audit.log',)\n\n\"\"\"\n\nimport logging\nfrom privacyidea.lib.auditmodules.base import (Audit as AuditBase)\nimport datetime\n\nlog = logging.getLogger(__name__)\n\n\nclass Audit(AuditBase):\n    \"\"\"\n    This is the LoggerAudit module, which writes the audit entries\n    to the Python logging\n\n    .. note:: This audit module does not provide a *Read* capability.\n    \"\"\"\n\n    def __init__(self, config=None):\n        super(Audit, self).__init__(config)\n        self.name = \"loggeraudit\"\n\n    def finalize_log(self):\n        \"\"\"\n        This method is used to log the data\n        e.g. write the data to a file.\n        \"\"\"\n        self.audit_data[\"policies\"] = \",\".join(self.audit_data.get(\"policies\", []))\n        self.audit_data[\"timestamp\"] = datetime.datetime.utcnow()\n        log.info(u\"{0!s}\".format(self.audit_data))\n        self.audit_data = {}\n\n\n", "path": "privacyidea/lib/auditmodules/loggeraudit.py"}]}