ログメッセージをフォーマットせずに出力したいです。桁区切りのカンマを表示するには、どのように指定すればよいでしょうか？


# 環境

  * Python 3.10.2

# やりたいこと

ログメッセージに、数値を出力したいです。  
数値は桁区切りのカンマも出力したいです。

[code]

    In [27]: import logging
        ...: logging_formatter = '%(levelname)-8s : %(asctime)s : %(name)s : %(message)s'
        ...: logging.basicConfig(format=logging_formatter)
        ...: 
    
    In [29]: i = 123456789 
    
    In [30]: logger.error(f"{i:,} byte")
    ERROR    : 2022-07-23 00:04:48,683 : __main__ : 1,234,567,789 byte
    
[/code]

以下の記事によると、f-stringを使ってログメッセージを出力するのは、ログメッセージの集約の観点でよろしくありません。

[ログメッセージをフォーマットしてロガーに渡さない](https://jisou-
programmer.beproud.jp/%E3%83%AD%E3%82%AE%E3%83%B3%E3%82%B0/69-%E3%83%AD%E3%82%B0%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%82%92%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88%E3%81%97%E3%81%A6%E3%83%AD%E3%82%AC%E3%83%BC%E3%81%AB%E6%B8%A1%E3%81%95%E3%81%AA%E3%81%84.html)

したがって、次のようなコードでログメッセージを出力したいです。

[code]

    In [31]: logger.error("%d byte", i)
    ERROR    : 2022-07-23 00:09:48,531 : __main__ : 1234567789 byte
    
[/code]

ただし、上記のコードで桁区切りのカンマを表示する方法が分かりませんでした。

# 質問

`logger.error("%d byte", i)`のようなコードで、桁区切りのカンマを表示するには、どのように指定すればよいでしょうか？  
以下のサイトを眺めましたが、分かりませんでした。  
<https://docs.python.org/ja/3/library/logging.html#logging.Formatter>




フォーマットせずに渡すけど、フォーマットしたい、という矛盾した要求に思えますが、ニーズは分かります。

[ログメッセージをフォーマットしてロガーに渡さない](https://jisou-
programmer.beproud.jp/%E3%83%AD%E3%82%AE%E3%83%B3%E3%82%B0/69-%E3%83%AD%E3%82%B0%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%82%92%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88%E3%81%97%E3%81%A6%E3%83%AD%E3%82%AC%E3%83%BC%E3%81%AB%E6%B8%A1%E3%81%95%E3%81%AA%E3%81%84.html)
のが良いのは、loggerの第一引数（メッセージ部分）をキーにログ出力を集約することがあるためです。言い換えると、第二引数以降にフォーマットした数値（の文字列）を渡してしまえば良いと思います。

[code]

    >>> import logging
    >>> logger = logging.getLogger()
    >>> value = 12345678
    >>> logger.error('size %s mb', value)
    size 12345678 mb
    >>> logger.error('size %s mb', f'{value:,}')
    size 12,345,678 mb
    
[/code]

これで、メッセージ部分は `'size %s mb'` で一意なキーには変わりないので、目的は達成できると思います。  
もし、ログ表示とは別に裏で生データも扱えるようにしておきたいのであれば、extraで元データも渡しておく方法があります。

[code]

    >>> logger.error('size %s mb', f'{value:,}', extra={'value': value})
    size 12,345,678 mb
    
[/code]

値を2つ渡すのが面倒、表示は3桁区切りにしたいが生データを維持したい、ということであれば、ロギング用の3桁区切りラッパークラスを実装してしまう方法もあります。

[code]

    >>> class ThreeDigit(int):
    ...   def __str__(self):
    ...     return f'{self:,}'
    ... 
    >>> ThreeDigit(value)
    12345678
    >>> print(ThreeDigit(value))
    12,345,678
    >>> logger.error('size %s mb', ThreeDigit(value))
    size 12,345,678 mb
    
[/code]

いずれの方法でも実現はできますが、JSONの構造化ログでログデータを扱い始めると、Python
loggerのレイヤーで3桁区切りのフォーマットをする必要がなくなる（構造化ログのビューワーを別途用意すると思われる）ため、将来どうしていきたいか次第で選べば良さそうです。

ログ出力はコンソールに表示するだけで収集しない、ということであれば、（良いプラクティスではありませんが）気にせずフォーマットしてしまうという割り切りもありだと思います。




% によるフォーマッティングで3桁区切りさせる方法はないと思いますよ。  
3桁区切りにすることが必要ならば、数値を3桁区切りの文字列にしてからロガーに渡せばどうでしょうか。

[code]

    logger.error("%s bytes", f"{i:,d}")
    
[/code]

