hasql-opentelemetry: OpenTelemetry tracing for hasql

[ bsd3, database, library, telemetry ] [ Propose Tags ] [ Report a vulnerability ]

Wraps the pqi adapter that hasql 2 connects through, so that every command sent to Postgres becomes an OpenTelemetry client span carrying the SQL, the row count and, on failure, the SQLSTATE. Spans are timed from the send to the last result, including in pipeline mode. Parameter values and error messages are never recorded. . See Hasql.OpenTelemetry for usage.


[Skip to Readme]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0.0
Change log CHANGELOG.md
Dependencies base (>=4.18 && <5), bytestring (>=0.11 && <0.13), containers (>=0.6 && <0.9), hs-opentelemetry-api (>=1.0 && <1.1), pqi (>=1.1 && <1.2), text (>=2.0 && <2.2), unordered-containers (>=0.2.14 && <0.3) [details]
Tested with ghc ==9.12.4
License BSD-3-Clause
Copyright 2026 Kronor
Author Kronor
Maintainer lorenzo@kronor.io
Uploaded by lorenzo at 2026-10-13T10:36:30Z
Category Database, Telemetry
Home page https://github.com/kronor-io/hasql-opentelemetry
Bug tracker https://github.com/kronor-io/hasql-opentelemetry/issues
Source repo head: git clone https://github.com/kronor-io/hasql-opentelemetry
Distributions
Downloads 3 total (3 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs uploaded by user
Build status unknown [no reports yet]

Readme for hasql-opentelemetry-0.1.0.0

[back to package description]

hasql-opentelemetry

OpenTelemetry tracing for hasql 2.

hasql 2 talks to Postgres through a pqi adapter, which is a record of IO closures. This package wraps that adapter, so each command sent to Postgres becomes a client span. Your statements and sessions stay as they are.

import Hasql.Connection qualified as Connection
import Hasql.OpenTelemetry qualified as HasqlOtel
import Pqi.Ffi qualified

Connection.acquire (HasqlOtel.traced Pqi.Ffi.adapter) settings

Pass the same wrapped adapter to hasql-pool. It works with both pqi-ffi and pqi-native.

What a span carries

Attribute Value
db.system.name postgresql
db.namespace database name
server.address, server.port from the connection
db.query.text the statement, whitespace collapsed, cut at 1024 characters
db.operation.name leading keyword (SELECT, INSERT, …)
db.postgresql.prepared_statement name it was prepared under, for executions of prepared statements
db.response.returned_rows rows returned
db.postgresql.affected_rows rows affected by a command that returns none
db.response.status_code, error.type SQLSTATE, on failure

Spans are timed from the send to the last result, and this holds in pipeline mode too, which is how hasql 2 runs every roundtrip. Prepares appear as separate PREPARE … spans.

By default spans are only started when a span is already active on the calling thread, so pool warm-up and health checks don't start their own traces. Set requireParentSpan = False to trace everything.

What is never recorded

Parameter values and the text of server error reports are never recorded. Postgres often puts the offending value in the error message (for example, invalid input syntax for type uuid: "…"), so a failure records only its SQLSTATE.

SQL built at runtime with literal values spliced into it is recorded exactly as sent. Pass such values as parameters instead.

Datadog

tracedWith defaultConfig { datadogCompat = True } changes three things so Datadog APM renders these spans as database queries:

  • span names become postgresql.query, postgresql.prepare and postgresql.describe;
  • span.type=sql is added;
  • the statement is also set as resource.name and db.statement.

Testing

The test suite runs real sessions against Postgres, configured through the usual PGHOST / PGPORT / PGUSER / PGPASSWORD / PGDATABASE variables. Without PGHOST it is skipped.

docker run --rm -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:18
PGHOST=localhost PGPASSWORD=postgres cabal test