Skip to content

[Bug] logging adapter does not include extra attributes? #276

@jmdacruz

Description

@jmdacruz

What are you really trying to do?

I'm trying to use structured logging with the Python SDK, using the extra attributes to provide more context to log entries. The Python SDK seems to honor existing attributes when adding the activity/workflow contextual info:

extra = kwargs.get("extra", None) or {}
extra["activity_info"] = context.info()
kwargs["extra"] = extra

Describe the bug

When enabling basic logging for one of the Python samples, using a format string that references an extra attribute that is provided in all log entries (on workflow, activity, and main entry point for the app), the logging calls inside the workflow and activities raises a KeyError exception because it can't find the attribute. The same logging call on the main() entry point works just fine

Minimal Reproduction

On the https://github.com/temporalio/samples-python repo, apply the following diff (to file hello/hello_activity.py):

diff --git a/hello/hello_activity.py b/hello/hello_activity.py
index 6615b55..e7716ef 100644
--- a/hello/hello_activity.py
+++ b/hello/hello_activity.py
@@ -20,7 +20,8 @@ class ComposeGreetingInput:
 # Basic activity that logs and does string concatenation
 @activity.defn
 async def compose_greeting(input: ComposeGreetingInput) -> str:
-    activity.logger.info("Running activity with parameter %s" % input)
+    # Logging a record with extra attribute "foo" here causes a "KeyError" exception
+    activity.logger.info("Running activity with parameter %s" % input, extra={"foo": "in activity"})
     return f"{input.greeting}, {input.name}!"
 
 
@@ -29,7 +30,8 @@ async def compose_greeting(input: ComposeGreetingInput) -> str:
 class GreetingWorkflow:
     @workflow.run
     async def run(self, name: str) -> str:
-        workflow.logger.info("Running workflow with parameter %s" % name)
+        # Logging a record with extra attribute "foo" here causes a "KeyError" exception
+        workflow.logger.info("Running workflow with parameter %s" % name, extra={"foo": "in workflow"})
         return await workflow.execute_activity(
             compose_greeting,
             ComposeGreetingInput("Hello", name),
@@ -39,7 +41,12 @@ class GreetingWorkflow:
 
 async def main():
     # Uncomment the line below to see logging
-    # logging.basicConfig(level=logging.INFO)
+    # Including attribute "foo" on the log format
+    logging.basicConfig(level=logging.INFO, format="--- %(message)s --- %(foo)s")
+
+    # Logging a record with extra attribute "foo" here works fine
+    logger = logging.getLogger("root")
+    logger.info("logging in main", extra={"foo": "in main"})
 
     # Start client
     client = await Client.connect("localhost:7233")

Run the example against temporalite, started like:

temporalite start --ephemeral --namespace=default

The expected result is that all 3 log entries are printed, with the foo attribute. The actual result is that only the entry generated by the main() function is printed, and the two entries on the workflow and activity fail with a KeyError exception when trying to index the foo attribute.

Environment/Versions

  • OS and processor: Intel Mac, Monterey
  • Temporal Version: temporalite version (devel) (server 1.19.1), Python SDK with latest commit temporalio/samples-python@a4f0a05
  • Are you using Docker or Kubernetes or building Temporal from source?: No, running directly with Python 3.10 and 3.11, using temporalite

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions