Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
10 changes: 9 additions & 1 deletion src/docket/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@

__version__ = version("pydocket")

from .dependencies import CurrentDocket, CurrentExecution, CurrentWorker, Retry, TaskKey
from .dependencies import (
CurrentDocket,
CurrentExecution,
CurrentWorker,
Retry,
TaskKey,
TaskLogger,
)
from .docket import Docket
from .execution import Execution
from .worker import Worker
Expand All @@ -21,6 +28,7 @@
"CurrentWorker",
"CurrentExecution",
"TaskKey",
"TaskLogger",
"Retry",
"__version__",
]
21 changes: 21 additions & 0 deletions src/docket/dependencies.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import abc
import inspect
import logging
from datetime import timedelta
from typing import Any, Awaitable, Callable, Counter, cast

Expand Down Expand Up @@ -55,6 +56,26 @@ def TaskKey() -> str:
return cast(str, _TaskKey())


class _TaskLogger(Dependency):
def __call__(
self, docket: Docket, worker: Worker, execution: Execution
) -> logging.LoggerAdapter:
logger = logging.getLogger(f"docket.task.{execution.key}")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make this configurable but I kind of feel like we always want the logger to be the task identifier?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this should be "docket.task.{execution.function.__name__}"? The key is unique to each time a task runs, so if we made that the logger name, it might be pretty hard to filter it in logging tools? These also form like a hierarchical tree inside of the logging module so it might get bogged down if there are tons of unique ones (not totally sure about that)


extra = {
"task.key": execution.key,
"task.attempt": execution.attempt,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

task.*? execution.*? task.execution.*?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the OTEL stuff I used execution.key, and execution.attempt because I thought the task could refer to the function itself and the execution could refer to this one instance of running the function

"worker.name": worker.name,
"docket.name": docket.name,
}

return logging.LoggerAdapter(logger, extra)


def TaskLogger() -> logging.LoggerAdapter[logging.Logger]:
return cast(logging.LoggerAdapter[logging.Logger], _TaskLogger())


class Retry(Dependency):
single: bool = True

Expand Down
32 changes: 32 additions & 0 deletions tests/test_fundamentals.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
as possible to aid with understanding docket.
"""

import logging
from datetime import datetime, timedelta
from logging import LoggerAdapter
from typing import Callable
from unittest.mock import AsyncMock
from uuid import uuid4
Expand All @@ -20,6 +22,7 @@
Execution,
Retry,
TaskKey,
TaskLogger,
Worker,
)

Expand Down Expand Up @@ -390,3 +393,32 @@ async def the_task(a: str, b: str, this_key: str = TaskKey()):
await worker.run_until_current()

assert called


async def test_logging_inside_of_task(
docket: Docket,
worker: Worker,
now: Callable[[], datetime],
caplog: pytest.LogCaptureFixture,
):
"""docket should support providing a logger with task context"""
called = False

async def the_task(a: str, b: str, logger: LoggerAdapter = TaskLogger()):
assert a == "a"
assert b == "c"

logger.info("Task is running")

nonlocal called
called = True

await docket.add(the_task, key="my-cool-task:123")("a", b="c")

with caplog.at_level(logging.INFO):
await worker.run_until_current()

assert called
assert "Task is running" in caplog.text
assert "docket.task.my-cool-task:123" in caplog.text
print(caplog.text)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stray?