Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Structlog support #133

Merged
merged 3 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,11 @@ from datetime import datetime,timedelta

client = axiom_py.Client()

time = datetime.utcnow() - timedelta(hours=1)
time_formatted = rfc3339.format(time)

client.ingest_events(
dataset="my-dataset",
events=[
{"foo": "bar", "_time": time_formatted},
{"bar": "baz", "_time": time_formatted},
{"foo": "bar"},
{"bar": "baz"},
])
client.query(r"['my-dataset'] | where foo == 'bar' | limit 100")
```
Expand All @@ -96,6 +93,30 @@ def setup_logger():

For a full example, see [`examples/logger.py`](examples/logger.py).

If you use [structlog](https://github.com/hynek/structlog), you can set up the
`AxiomProcessor` like this:

```python
from axiom_py import Client
from axiom_py.structlog import AxiomProcessor


def setup_logger():
client = Client()

structlog.configure(
processors=[
# ...
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso", key="_time"),
AxiomProcessor(client, "my-dataset"),
# ...
]
)
```

For a full example, see [`examples/structlog.py`](examples/structlog.py).

## Contributing

This project uses [uv](https://docs.astral.sh/uv) for dependency management
Expand Down
File renamed without changes.
File renamed without changes.
26 changes: 26 additions & 0 deletions examples/structlog_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from axiom_py import Client
from axiom_py.structlog import AxiomProcessor
import structlog


def main():
client = Client()

structlog.configure(
processors=[
structlog.contextvars.merge_contextvars,
structlog.processors.add_log_level,
structlog.processors.StackInfoRenderer(),
structlog.dev.set_exc_info,
structlog.processors.TimeStamper(fmt="iso", key="_time"),
AxiomProcessor(client, "my-dataset"),
structlog.dev.ConsoleRenderer(),
]
)

log = structlog.get_logger()
log.info("hello", who="world")


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion src/axiom_py/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class AxiomHandler(Handler):

client: Client
dataset: str
logcache: list
buffer: list
interval: int
last_run: float

Expand Down
42 changes: 42 additions & 0 deletions src/axiom_py/structlog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Structlog contains the AxiomProcessor for structlog."""

from typing import List
import time
import atexit

from .client import Client


class AxiomProcessor:
"""A processor for sending structlogs to Axiom."""

client: Client
dataset: str
buffer: List[object]
interval: int
last_run: float

def __init__(self, client: Client, dataset: str, interval=1):
self.client = client
self.dataset = dataset
self.buffer = []
self.last_run = time.monotonic()
self.interval = interval

atexit.register(self._flush)

def _flush(self):
self.last_run = time.monotonic()
if len(self.buffer) == 0:
return
self.client.ingest_events(self.dataset, self.buffer)
self.buffer = []

def __call__(self, logger: object, method_name: str, event_dict: object):
self.buffer.append(event_dict.copy())
if (
len(self.buffer) >= 1000
or time.monotonic() - self.last_run > self.interval
):
self.flush()
return event_dict