Repository: Cog-Creators/Red-DiscordBot

Issue:
Rich logging should have a friendlier copy/paste option
### What component of Red (cog, command, API) would you like to see improvements on?

Rich Logging

### Describe the enhancement you're suggesting.

Currently using Rich logging can break copy/pasting useful output for development. Specifically when working with API data I will often log the response, copy it and put it into a json formatter/validator to read easier. Right now rich will put the loggers name on the right half of the terminal including it inside the data I want to copy invalidating any json output and potentially other data formats. Rich also includes extraneous whitespace in the copied output.




Discussion:
I made a mock-up of how a more copy-paste friendly logging could look ([link to code](https://gist.github.com/jack1142/b848d49f837c1bd089525d8340c5b1b9)):
![](https://cdn.discordapp.com/attachments/718148684629540905/816028448316981298/unknown.png)

The important parts of this mock-up are:
- logger name is shown *before* the log message, which allows the user to *not* select when copying the log message
- whitespace from Rich's columns (time and log level) is not present (in 2nd and following lines) when the log message is wrapped
- automatic hard-wrapping from Rich is disabled so that terminal software handles the wrapping on its own allowing the user to copy-paste the log message without the newlines getting inserted on line wraps

If someone decides to use a different design than the one I proposed here, it is important that these key points are fulfilled by it as well.

Resolving patch:
diff --git a/redbot/logging.py b/redbot/logging.py
--- a/redbot/logging.py
+++ b/redbot/logging.py
@@ -171,43 +171,30 @@ def __call__(
         link_path=None,
         logger_name=None,
     ):
-        output = Table.grid(padding=(0, 1))
-        output.expand = True
-        if self.show_time:
-            output.add_column(style="log.time")
-        if self.show_level:
-            output.add_column(style="log.level", width=self.level_width)
-        output.add_column(ratio=1, style="log.message", overflow="fold")
-        if self.show_path and path:
-            output.add_column(style="log.path")
-        if logger_name:
-            output.add_column()
-        row = []
+        output = Text()
         if self.show_time:
             log_time = log_time or console.get_datetime()
             log_time_display = log_time.strftime(time_format or self.time_format)
             if log_time_display == self._last_time:
-                row.append(Text(" " * len(log_time_display)))
+                output.append(" " * (len(log_time_display) + 1))
             else:
-                row.append(Text(log_time_display))
+                output.append(f"{log_time_display} ", style="log.time")
                 self._last_time = log_time_display
         if self.show_level:
-            row.append(level)
+            # The space needs to be added separately so that log level is colored by
+            # Rich.
+            output.append(level)
+            output.append(" ")
+        if logger_name:
+            output.append(f"[{logger_name}] ", style="bright_black")
 
-        row.append(Renderables(renderables))
+        output.append(*renderables)
         if self.show_path and path:
             path_text = Text()
             path_text.append(path, style=f"link file://{link_path}" if link_path else "")
             if line_no:
                 path_text.append(f":{line_no}")
-            row.append(path_text)
-
-        if logger_name:
-            logger_name_text = Text()
-            logger_name_text.append(f"[{logger_name}]", style="bright_black")
-            row.append(logger_name_text)
-
-        output.add_row(*row)
+            output.append(path_text)
         return output
 
 
@@ -286,7 +273,8 @@ def emit(self, record: LogRecord) -> None:
                 line_no=record.lineno,
                 link_path=record.pathname if self.enable_link_path else None,
                 logger_name=record.name,
-            )
+            ),
+            soft_wrap=True,
         )
         if traceback:
             self.console.print(traceback)


Test patch: