]> git.puffer.fish Git - mirror/frr.git/commitdiff
tools: add logfmt option for frr-reload.py 16796/head
authorGiovanni Tataranni <g.tataranni@gmail.com>
Thu, 19 Sep 2024 13:46:02 +0000 (15:46 +0200)
committerGiovanni Tataranni <g.tataranni@gmail.com>
Mon, 17 Feb 2025 16:33:28 +0000 (17:33 +0100)
Add the option of printing logs in logfmt format.

Additional machine readable information can be printed via the `extra`
argument.
Example:
```python
log.debug("exit context"), extra={"line": line, "ctx_keys": ctx_keys})

log.error(f"Failed to execute command {' '.join(cmd)}", extra={"cmd": cmd})
```

Signed-off-by: Giovanni Tataranni <g.tataranni@gmail.com>
tools/frr-reload.py

index 94327eb0833d1389c51d4fb67b33210b14421a88..ee6f2456af2c98224d2d238361870e9e36c89cbd 100755 (executable)
@@ -14,6 +14,7 @@ This program
 
 from __future__ import print_function, unicode_literals
 import argparse
+import datetime
 import logging
 import os, os.path
 import random
@@ -1978,6 +1979,50 @@ def compare_context_objects(newconf, running):
     return (lines_to_add, lines_to_del)
 
 
+class LogFmtFormatter(logging.Formatter):
+    def format(self, record: logging.LogRecord) -> str:
+        """
+        Creates log messages with key=value pairs, in logfmt format.
+        """
+        # Escape double quotes in the message, replace newlines with '\n'
+        msg = record.getMessage().replace('"', '\\"').replace("\n", "\\n").strip()
+        # Format the time in RFC 3339 format
+        timestamp = datetime.datetime.fromtimestamp(
+            record.created, datetime.timezone.utc
+        )
+        asctime = timestamp.astimezone().isoformat(timespec="seconds")
+        # Create logfmt style log message, ignore default fields
+        logfmt = f'ts={asctime} level={record.levelname} msg="{msg}"'
+        default_fields = [
+            "args",
+            "asctime",
+            "created",
+            "exc_info",
+            "exc_text",
+            "filename",
+            "funcName",
+            "levelname",
+            "levelno",
+            "lineno",
+            "module",
+            "msecs",
+            "msg",
+            "name",
+            "pathname",
+            "process",
+            "processName",
+            "relativeCreated",
+            "stack_info",
+            "thread",
+            "threadName",
+        ]
+        for key, value in vars(record).items():
+            if key in default_fields:
+                continue
+            logfmt += f" {key}={value}"
+        return logfmt
+
+
 if __name__ == "__main__":
     # Command line options
     parser = argparse.ArgumentParser(
@@ -2045,15 +2090,24 @@ if __name__ == "__main__":
         action="store_true",
         help="Used by topotest to not delete debug or log file commands",
     )
+    parser.add_argument(
+        "--logfmt",
+        action="store_true",
+        help="Use logfmt as log format",
+        default=False,
+    )
 
     args = parser.parse_args()
 
     # Logging
     # For --test log to stdout
     # For --reload log to /var/log/frr/frr-reload.log
-    if args.test or args.stdout:
-        logging.basicConfig(format="%(asctime)s %(levelname)5s: %(message)s")
-
+    # If --logfmt, use the logfmt format
+    formatter = logging.Formatter("%(asctime)s %(levelname)5s: %(message)s")
+    handler = logging.StreamHandler()
+    if args.logfmt:
+        formatter = LogFmtFormatter()
+    elif args.test or args.stdout:
         # Color the errors and warnings in red
         logging.addLevelName(
             logging.ERROR, "\033[91m  %s\033[0m" % logging.getLevelName(logging.ERROR)
@@ -2061,20 +2115,15 @@ if __name__ == "__main__":
         logging.addLevelName(
             logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)
         )
-
-    elif args.reload:
+    if args.reload:
         if not os.path.isdir("/var/log/frr/"):
             os.makedirs("/var/log/frr/", mode=0o0755)
-
-        logging.basicConfig(
-            filename="/var/log/frr/frr-reload.log",
-            format="%(asctime)s %(levelname)5s: %(message)s",
-        )
-
-    # argparse should prevent this from happening but just to be safe...
-    else:
-        raise Exception("Must specify --reload or --test")
+        handler = logging.FileHandler("/var/log/frr/frr-reload.log")
+    if args.stdout:
+        handler = logging.StreamHandler(sys.stdout)
+    handler.setFormatter(formatter)
     log = logging.getLogger(__name__)
+    log.addHandler(handler)
 
     if args.debug:
         log.setLevel(logging.DEBUG)