Repository: sanic-org/sanic

Issue:
Access logging raise TypeError after `logging.logProcesses=False`
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Describe the bug

If someone use `logging.logProcesses = False` to disable the calling of `os.getpid()` while logging, the [default formatter of Sanic](https://github.com/sanic-org/sanic/blob/5e7f6998bdccce325a4c30d940d02d9d1e40b11e/sanic/log.py#L65) will lead to the exception as shown below.

See [`logging.logProcesses`](https://docs.python.org/3/howto/logging.html#optimization)

```log
  File "C:\Program Files\Python\Python311\Lib\logging\__init__.py", line 445, in _format
    return self._fmt % values
           ~~~~~~~~~~^~~~~~~~
  File "C:\Program Files\Python\Python311\Lib\logging\__init__.py", line 449, in format
    return self._format(record)
  File "C:\Program Files\Python\Python311\Lib\logging\__init__.py", line 659, in formatMessage
    return self._style.format(record)
  File "C:\Program Files\Python\Python311\Lib\logging\__init__.py", line 690, in format
    s = self.formatMessage(record)
  File "C:\Program Files\Python\Python311\Lib\logging\__init__.py", line 953, in format
    return fmt.format(record)
  File "C:\Program Files\Python\Python311\Lib\logging\__init__.py", line 1110, in emit
    msg = self.format(record)
  File "C:\Program Files\Python\Python311\Lib\logging\__init__.py", line 978, in handle
    self.emit(record)
  File "C:\Program Files\Python\Python311\Lib\logging\__init__.py", line 1706, in callHandlers
    hdlr.handle(record)
  File "C:\Program Files\Python\Python311\Lib\logging\__init__.py", line 1644, in handle
    self.callHandlers(record)
  File "C:\Program Files\Python\Python311\Lib\logging\__init__.py", line 1634, in _log
    self.handle(record)
  File "C:\Program Files\Python\Python311\Lib\logging\__init__.py", line 1489, in info
    self._log(INFO, msg, args, **kwargs)
  File "C:\Program Files\Python\Python311\Lib\site-packages\sanic\application\motd.py", line 113, in display
    out(indent("\n".join(lines), "  "))
  File "C:\Program Files\Python\Python311\Lib\site-packages\sanic\application\motd.py", line 39, in output
    motd_class(logo, serve_location, data, extra).display()
  File "C:\Program Files\Python\Python311\Lib\site-packages\sanic\mixins\startup.py", line 579, in motd
    MOTD.output(logo, serve_location, display, extra)
  File "C:\Program Files\Python\Python311\Lib\site-packages\sanic\mixins\startup.py", line 533, in _helper
    self.motd(server_settings=server_settings)
  File "C:\Program Files\Python\Python311\Lib\site-packages\sanic\mixins\startup.py", line 327, in prepare
    server_settings = self._helper(
  File "C:\Program Files\Python\Python311\Lib\site-packages\sanic\mixins\startup.py", line 176, in run
    self.prepare(
...
TypeError: %d format: a real number is required, not NoneType
```

Without `os.getpid()`, the LogRecord will only be generated with a value dict like `values = {'process': None, ...}`. Then, exception raises when the formatter tries to translate `values = {'process': None, ...}` into `"[%(process)d]"`.

I suggest to use `[%(process)s]` instead of `[%(process)d]`.

https://github.com/sanic-org/sanic/blob/5e7f6998bdccce325a4c30d940d02d9d1e40b11e/sanic/log.py#L65

If there is no [conversion flags](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting) (like `%06d`) setted, `%s` also has a better performance on converting unsigned integer to string.

See [Why is %s faster than %d for integer substitution in python?](https://stackoverflow.com/a/27800584/18677995)

And here is a shell snippet for you to make a brief test:
```shell
python -m timeit -n 100000 -s "fstr='[%(process)s]'" "fstr % {'process':12345}"
python -m timeit -n 100000 -s "fstr='[%(process)d]'" "fstr % {'process':12345}"
```

Result on my laptop is:
```log
100000 loops, best of 5: 157 nsec per loop
100000 loops, best of 5: 160 nsec per loop
```

### Code snippet

```python
import logging

from sanic import Sanic
from sanic.response import text

logging.logProcesses = False

app = Sanic("MyHelloWorldApp")


@app.get("/")
async def hello_world(request):
    return text("Hello, world.")


if __name__ == '__main__':
    app.run(host="127.0.0.1", port=8080, debug=True)
```

### Expected Behavior

Log be like (pid shows `None` instead of raising exceptions):

```log
[2023-02-09 10:39:08 +0800] [None] [INFO] 
  ┌─────────────────────────────────────────────────────────────┐
  │                        Sanic v22.12.0                       │
  │              Goin' Fast @ http://127.0.0.1:8080             │
  ├───────────────────────┬─────────────────────────────────────┤
  │                       │     mode: debug, single worker      │
  │     ▄███ █████ ██     │   server: sanic, HTTP/1.1           │
  │    ██                 │   python: 3.11.1                    │
  │     ▀███████ ███▄     │ platform: Windows-10-10.0.22621-SP0 │
  │                 ██    │ packages: sanic-routing==22.8.0     │
  │    ████ ████████▀     │                                     │
  │                       │                                     │
  │ Build Fast. Run Fast. │                                     │
  └───────────────────────┴─────────────────────────────────────┘

[2023-02-09 10:39:08 +0800] [None] [DEBUG] Creating multiprocessing context using 'spawn'
[2023-02-09 10:39:08 +0800] [None] [DEBUG] Starting a process: Sanic-Server-0-0
[2023-02-09 10:39:09 +0800] [None] [DEBUG] Process ack: Sanic-Server-0-0 [13504]
[2023-02-09 10:39:09 +0800] [None] [INFO] Starting worker [13504]
```

### How do you run Sanic?

As a script (`app.run` or `Sanic.serve`)

### Operating System

Windows

### Sanic Version

22.12.0

### Additional context

_No response_


Discussion:

Resolving patch:
diff --git a/sanic/log.py b/sanic/log.py
--- a/sanic/log.py
+++ b/sanic/log.py
@@ -62,13 +62,13 @@ class StrEnum(str, Enum):
     },
     formatters={
         "generic": {
-            "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
+            "format": "%(asctime)s [%(process)s] [%(levelname)s] %(message)s",
             "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
             "class": "logging.Formatter",
         },
         "access": {
             "format": "%(asctime)s - (%(name)s)[%(levelname)s][%(host)s]: "
-            + "%(request)s %(message)s %(status)d %(byte)d",
+            + "%(request)s %(message)s %(status)s %(byte)s",
             "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
             "class": "logging.Formatter",
         },


Test patch: