Skip to content

Integrations

Dossier automatically unpacks objects from popular libraries into flat structured log fields. Pass any object as the event argument and dossier infers the event name from the class and flattens the fields.

Pydantic

from pydantic import BaseModel
from dossier import get_session

class RequestModel(BaseModel):
    method: str
    path: str
    body: dict

logger = get_session()
request = RequestModel(method="POST", path="/api/chat", body={"msg": "hi"})
logger.info(request)
# {"event": "RequestModel", "method": "POST", "path": "/api/chat", "body": {"msg": "hi"}, ...}

Nested Pydantic models are recursively unpacked via model_dump().

LangChain

from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
from dossier import get_session

logger = get_session()

logger.info(HumanMessage(content="What's 2+2?"))
# {"event": "HumanMessage", "content": "What's 2+2?", "type": "human", ...}

logger.info(AIMessage(content="4"))
# {"event": "AIMessage", "content": "4", "type": "ai", ...}

All LangChain message types work — HumanMessage, AIMessage, SystemMessage, ToolMessage, etc.

OpenAI SDK

from dossier import get_session

logger = get_session()

# Log the full ChatCompletion response object
logger.info(response)
# Unpacks: model, choices, usage fields, etc.

# Or log specific parts
logger.info(response.choices[0].message)
logger.info(response.usage)

Dataclasses

from dataclasses import dataclass
from dossier import get_session

@dataclass
class ToolCall:
    tool_name: str
    arguments: dict

logger = get_session()
logger.info(ToolCall(tool_name="web_search", arguments={"q": "weather"}))
# {"event": "ToolCall", "tool_name": "web_search", "arguments": {"q": "weather"}, ...}

Dataclasses are unpacked via dataclasses.asdict() for deep recursive conversion.

Generic Objects

Any object with __dict__ is unpacked automatically. Private attributes (those starting with _) are excluded:

class Config:
    def __init__(self):
        self.host = "localhost"
        self.port = 8080
        self._internal = "hidden"

logger.info(Config())
# {"event": "Config", "host": "localhost", "port": 8080, ...}

Unpacking Priority

When an object matches multiple categories, dossier uses this priority:

  1. Dataclassdataclasses.asdict() (deepest conversion)
  2. Pydantic model.model_dump()
  3. Generic object__dict__ (excluding private attrs)

Nested Objects

Objects nested inside kwargs are also unpacked and flattened with key prefixing:

logger.info("api_call", request=RequestModel(method="GET", path="/health", body={}))
# {"event": "api_call", "request_method": "GET", "request_path": "/health", "request_body": {}, ...}