diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index dba50b3c538a..94327eb0833d 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -95,7 +95,7 @@ def is_config_available(self):
         output = self("configure")
 
         if "configuration is locked" in output.lower():
-            log.error("vtysh 'configure' returned\n%s\n" % (output))
+            log.error(f"vtysh 'configure' returned\n{output}\n")
             return False
 
         return True
@@ -104,7 +104,7 @@ def exec_file(self, filename):
         child = self._call(["-f", filename])
         if child.wait() != 0:
             raise VtyshException(
-                "vtysh (exec file) exited with status %d" % (child.returncode)
+                f"vtysh (exec file) exited with status {child.returncode}"
             )
 
     def mark_file(self, filename, stdin=None):
@@ -312,7 +312,7 @@ def load_from_file(self, filename):
         The internal representation has been marked appropriately by passing it
         through vtysh with the -m parameter
         """
-        log.info("Loading Config object from file %s", filename)
+        log.info(f"Loading Config object from file {filename}")
 
         file_output = self.vtysh.mark_file(filename)
 
@@ -649,7 +649,7 @@ def load_contexts(self):
                 self.save_contexts(ctx_keys, cur_ctx_lines)
 
                 # exit current context
-                log.debug("LINE %-50s: exit context %-50s", line, ctx_keys)
+                log.debug(f"LINE {line:<50}: exit context {' '.join(ctx_keys):<50}")
 
                 ctx_keys.pop()
                 cur_ctx_keywords.pop()
@@ -664,7 +664,7 @@ def load_contexts(self):
                     self.save_contexts(ctx_keys, cur_ctx_lines)
 
                     # exit current context
-                    log.debug("LINE %-50s: exit context %-50s", line, ctx_keys)
+                    log.debug(f"LINE {line:<50}: exit context {' '.join(ctx_keys):<50}")
 
                     ctx_keys.pop()
                     cur_ctx_keywords.pop()
@@ -695,17 +695,21 @@ def load_contexts(self):
                     cur_ctx_keywords.append(v)
                     cur_ctx_lines = []
 
-                    log.debug("LINE %-50s: enter context %-50s", line, ctx_keys)
+                    log.debug(
+                        f"LINE {line:<50}: enter context {' '.join(ctx_keys):<50}"
+                    )
                     break
 
             if new_ctx:
                 continue
 
             if len(ctx_keys) == 0:
-                log.debug("LINE %-50s: single-line context", line)
+                log.debug(f"LINE {line:<50}: single-line context")
                 self.save_contexts([line], [])
             else:
-                log.debug("LINE %-50s: add to current context %-50s", line, ctx_keys)
+                log.debug(
+                    f"LINE {line:<50}: add to current context {' '.join(ctx_keys):<50}"
+                )
                 cur_ctx_lines.append(line)
 
         # Save the context of the last one
@@ -1696,7 +1700,7 @@ def ignore_unconfigurable_lines(lines_to_add, lines_to_del):
                 ]
             ]
         ):
-            log.info('"%s" cannot be removed' % (ctx_keys[-1],))
+            log.info(f'"{ctx_keys[-1]}" cannot be removed')
             lines_to_del_to_del.append((ctx_keys, line))
 
     for ctx_keys, line in lines_to_del_to_del:
@@ -2090,26 +2094,26 @@ def compare_context_objects(newconf, running):
 
     # Verify the new config file is valid
     if not os.path.isfile(args.filename):
-        log.error("Filename %s does not exist" % args.filename)
+        log.error(f"Filename {args.filename} does not exist")
         sys.exit(1)
 
     if not os.path.getsize(args.filename):
-        log.error("Filename %s is an empty file" % args.filename)
+        log.error(f"Filename {args.filename} is an empty file")
         sys.exit(1)
 
     # Verify that confdir is correct
     if not os.path.isdir(args.confdir):
-        log.error("Confdir %s is not a valid path" % args.confdir)
+        log.error(f"Confdir {args.confdir} is not a valid path")
         sys.exit(1)
 
     # Verify that bindir is correct
     if not os.path.isdir(args.bindir) or not os.path.isfile(args.bindir + "/vtysh"):
-        log.error("Bindir %s is not a valid path to vtysh" % args.bindir)
+        log.error(f"Bindir {args.bindir} is not a valid path to vtysh")
         sys.exit(1)
 
     # verify that the vty_socket, if specified, is valid
     if args.vty_socket and not os.path.isdir(args.vty_socket):
-        log.error("vty_socket %s is not a valid path" % args.vty_socket)
+        log.error(f"vty_socket {args.vty_socket} is not a valid path")
         sys.exit(1)
 
     # verify that the daemon, if specified, is valid
@@ -2164,7 +2168,7 @@ def compare_context_objects(newconf, running):
         )
         sys.exit(1)
 
-    log.info('Called via "%s"', str(args))
+    log.info(f'Called via "{args}"')
 
     # Create a Config object from the config generated by newconf
     newconf = Config(vtysh)
@@ -2240,7 +2244,7 @@ def compare_context_objects(newconf, running):
         if not vtysh.is_config_available() or not reload_ok:
             sys.exit(1)
 
-        log.debug("New Frr Config\n%s", newconf.get_lines())
+        log.debug(f"New Frr Config\n{newconf.get_lines()}")
 
         # This looks a little odd but we have to do this twice...here is why
         # If the user had this running bgp config:
@@ -2282,7 +2286,7 @@ def compare_context_objects(newconf, running):
         for x in range(2):
             running = Config(vtysh)
             running.load_from_show_running(args.daemon)
-            log.debug("Running Frr Config (Pass #%d)\n%s", x, running.get_lines())
+            log.debug(f"Running Frr Config (Pass #{x})\n{running.get_lines()}")
 
             (lines_to_add, lines_to_del) = compare_context_objects(newconf, running)
 
@@ -2339,7 +2343,7 @@ def compare_context_objects(newconf, running):
                             #   'no ip ospf authentication message-digest 1.1.1.1' in
                             #   our example above
                             # - Split that last entry by whitespace and drop the last word
-                            log.error("Failed to execute %s", " ".join(cmd))
+                            log.error(f"Failed to execute {' '.join(cmd)}")
                             last_arg = cmd[-1].split(" ")
 
                             if len(last_arg) <= 2:
@@ -2356,7 +2360,7 @@ def compare_context_objects(newconf, running):
                             new_last_arg = last_arg[0:-1]
                             cmd[-1] = " ".join(new_last_arg)
                         else:
-                            log.info('Executed "%s"', " ".join(cmd))
+                            log.info(f'Executed "{" ".join(cmd)}"')
                             break
 
             if lines_to_add:
@@ -2383,7 +2387,7 @@ def compare_context_objects(newconf, running):
                     )
 
                     filename = args.rundir + "/reload-%s.txt" % random_string
-                    log.info("%s content\n%s" % (filename, pformat(lines_to_configure)))
+                    log.info(f"{filename} content\n{pformat(lines_to_configure)}")
 
                     with open(filename, "w") as fh:
                         for line in lines_to_configure:
@@ -2392,7 +2396,7 @@ def compare_context_objects(newconf, running):
                     try:
                         vtysh.exec_file(filename)
                     except VtyshException as e:
-                        log.warning("frr-reload.py failed due to\n%s" % e.args)
+                        log.warning(f"frr-reload.py failed due to\n{e.args}")
                         reload_ok = False
                     os.unlink(filename)