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

Async custom metrics #650

Open
gpkc opened this issue Dec 5, 2024 · 2 comments
Open

Async custom metrics #650

gpkc opened this issue Dec 5, 2024 · 2 comments
Labels
Question Further information is requested

Comments

@gpkc
Copy link

gpkc commented Dec 5, 2024

Question

It seems that async custom metrics are not supported, as this is the signature for the callback:

    Callable[[CallbackOptions], Iterable[Observation]],
    Generator[Iterable[Observation], CallbackOptions, None],
]

Does it mean that collecting each metric is blocking, or for example do they run in separate threads and then aggregated before the interval ends?

@gpkc gpkc added the Question Further information is requested label Dec 5, 2024
@alexmojaki
Copy link
Contributor

The PeriodicExportingMetricReader runs in a single separate thread. In that thread, the callbacks run synchronously.

@gpkc
Copy link
Author

gpkc commented Dec 5, 2024

For anyone reading in the future, my solution for this was to create a function that creates my metrics and returns some closured periodic exporter. For example, in my case I needed something that grabbed values from SQLAlchemy:

async def get_metric_value(session: AsyncSession) -> Iterable[Observation]:
    counts = await # select from DB
    return [Observation(count, {"status": status}) for status, count in counts.items()]


def init_custom_metrics():
    sample_metric = logfire.metric_gauge(
        "sample_metric", unit="count"
    )

    async def export():
        while True:
            await asyncio.sleep(60)

            async with sessionmanager.session() as session:
                observations = await get_metric_value(session)

                for observation in observations:
                    sample_metric.set(
                        amount=observation.value, attributes=observation.attributes
                    )

    return export

Then, in my case, I wanted this to run alongside my FastAPI app. So I simply added it as a startup callback that creates an asyncio task that runs forever:

@server.on_event("startup")
async def setup_custom_metrics():
    export = init_custom_metrics()
    asyncio.ensure_future(export())

Not sure if it's ideal or not but it seems to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants