Skip to content

Commit

Permalink
Add MaybeTimeStamper
Browse files Browse the repository at this point in the history
Fixes #81
  • Loading branch information
hynek committed Jul 22, 2023
1 parent c1edd3e commit c0ac5da
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ You can find our backwards-compatibility policy [here](https://github.com/hynek/

- Official support for Python 3.12.
[#515](https://github.com/hynek/structlog/issues/515)
- `structlog.processors.MaybeTimeStamper` that only adds a timestamp if there isn't one already.
[#81](https://github.com/hynek/structlog/issues/81)


### Fixed
Expand Down
10 changes: 10 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,16 @@ API Reference
>>> TimeStamper(fmt="%Y", key="year")(None, None, {}) # doctest: +SKIP
{'year': '2013'}

.. autoclass:: MaybeTimeStamper

.. doctest::

>>> from structlog.processors import MaybeTimeStamper
>>> MaybeTimeStamper()(None, None, {}) # doctest: +SKIP
{'timestamp': 1690036074.494428}
>>> MaybeTimeStamper()(None, None, {"timestamp": 42})
{'timestamp': 42}

.. autoclass:: CallsiteParameter
:members:

Expand Down
31 changes: 31 additions & 0 deletions src/structlog/processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,37 @@ def stamper_fmt(event_dict: EventDict) -> EventDict:
return stamper_fmt


class MaybeTimeStamper:
"""
A timestamper that only adds a timestamp if there is none.
This allows you to overwrite the ``timestamp`` key in the event dict for
example when the event is coming from another system.
It takes the same arguments as `TimeStamper`.
.. versionadded:: 23.2.0
"""

__slots__ = ("stamper",)

def __init__(
self,
fmt: str | None = None,
utc: bool = True,
key: str = "timestamp",
):
self.stamper = TimeStamper(fmt=fmt, utc=utc, key=key)

def __call__(
self, logger: WrappedLogger, name: str, event_dict: EventDict
) -> EventDict:
if "timestamp" not in event_dict:
return self.stamper(logger, name, event_dict)

return event_dict


def _figure_out_exc_info(v: Any) -> ExcInfo:
"""
Depending on the Python version will try to do the smartest thing possible
Expand Down
19 changes: 19 additions & 0 deletions tests/test_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
JSONRenderer,
KeyValueRenderer,
LogfmtRenderer,
MaybeTimeStamper,
StackInfoRenderer,
TimeStamper,
UnicodeDecoder,
Expand Down Expand Up @@ -452,6 +453,24 @@ def test_apply_freezegun_after_instantiation(self):
assert "1980-03-25T17:00:00" == d["timestamp"]


class TestMaybeTimeStamper:
def test_overwrite(self):
"""
If there is a timestamp, leave it.
"""
mts = MaybeTimeStamper()

assert {"timestamp": 42} == mts(None, None, {"timestamp": 42})

def test_none(self):
"""
If there is no timestamp, add one.
"""
mts = MaybeTimeStamper()

assert "timestamp" in mts(None, None, {})


class TestFormatExcInfo:
def test_custom_formatter(self):
"""
Expand Down

0 comments on commit c0ac5da

Please sign in to comment.