Loguru: Replace newline `\n` with literal `\\n` in log

Created on 8 Jan 2020  路  6Comments  路  Source: Delgan/loguru

I'd love the option to force log messages to span just one line by replacing \n with \\n.

This is an issue which are sometimes encountered when logging Pandas DataFrames and Numpy Arrays.

question

All 6 comments

Hi @AllanLRH.

If you need to customize the line-ending of an handler, you can use a custom format function like this for example:

def my_format(record):
    return "[{time}] {function}:{file}:{line} - {message}\\n"

logger.add(sys.stderr, format_my_format)

Thanks, but what I'd really like is an (easy) way to configure the logger to transforn the input as such:

# This is what I want
logger.debug(str(my_numpy_array).replace('\n', '\\n'))
# Perhaps with the option of `repr` instead of `str`
logger.debug(repr(my_numpy_array).replace('\n', '\\n'))

# But I'd love to just write this, and configure the logger to do the work for me :)
logger.debug(my_numpy_array)

I tried looking at the docs for quite a while, but I can't find a straightforward way of doing this.

BTW, Numpy and Pandas arrays are truncated when convert to a string by str, thus preventing the log (or terminal) to be flodded by output.


See example

In [1]: import numpy as np

In [2]: import pandas as pd

In [3]: X = np.random.randint(0, 100, (1000, 5))

In [4]: X
Out[4]:
array([[ 7, 23, 29, 41, 64],
       [23, 30, 36, 29, 44],
       [61, 94, 31, 60, 45],
       ...,
       [48, 73, 73, 75,  9],
       [59, 51, 64, 79,  0],
       [19, 74, 58, 27, 89]])

In [5]: df = pd.DataFrame(X)

In [6]: df
Out[6]:
      0   1   2   3   4
0     7  23  29  41  64
1    23  30  36  29  44
2    61  94  31  60  45
3     3  57  76   3  10
4    95  89  40  86  63
..   ..  ..  ..  ..  ..
995  91  76  15  46  81
996  70  74  66  14  75
997  48  73  73  75   9
998  59  51  64  79   0
999  19  74  58  27  89

[1000 rows x 5 columns]

In [7]: str(X)
Out[7]: '[[ 7 23 29 41 64]\n [23 30 36 29 44]\n [61 94 31 60 45]\n ...\n [48 73 73 75  9]\n [59 51 64 79  0]\n [19 74 58 27 89]]'

In [8]: str(df)
Out[8]: '      0   1   2   3   4\n0     7  23  29  41  64\n1    23  30  36  29  44\n2    61  94  31  60  45\n3     3  57  76   3  10\n4    95  89  40  86  63\n..   ..  ..  ..  ..  ..\n995  91  76  15  46  81\n996  70  74  66  14  75\n997  48  73  73  75   9\n998  59  51  64  79   0\n999  19  74  58  27  89\n\n[1000 rows x 5 columns]'

Oh, yeah, sorry, I completely misinterpreted your question.

So, here is another workaround I can offer you. :smile:

>>> from loguru import logger
>>> multi = "foo\nbar\nbaz\n"
>>> logger.info(multi)
2020-01-11 17:32:33.496 | INFO - __main__ - foo
bar
baz

>>> def flatten_message(record):
...     record["message"] = record["message"].replace("\n", "\\n")
... 
>>> logger = logger.patch(flatten_message)
>>> logger.info(multi)
2020-01-11 17:33:16.592 | INFO - __main__ - foo\nbar\nbaz\n

I think you can use patch() to force the message to span only one line. You can also apply the change for your whole application by using the patcher parameter of the configure() method.

Sorry to reopen this, but it seems silly to create a new issue... can I patch the messages (or record-dict, I guess) on a sink-level?
Would be nice to force one-line output in files but keep multi-line output in console.

Yep, that's totally possible. Instead of modifying record["message"], just create a new entry to be used by the format parameter of some specific sinks only.

def flatten_message(record):
    record["extra"]["flattened_message"] = record["message"].replace("\n", "\\n")

logger.add("file.log", format="[{time}] {extra[flattened_message]}")
logger = logger.patch(flatten_message)

Closing as it seems solved. :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

volfco picture volfco  路  5Comments

filantus picture filantus  路  5Comments

flybaozi picture flybaozi  路  4Comments

af6140 picture af6140  路  5Comments

rightx2 picture rightx2  路  3Comments