Brendonovich / prisma-client-rust

Type-safe database access for Rust
https://prisma.brendonovich.dev
Apache License 2.0
1.75k stars 106 forks source link

Is it possible to extract query's metadata for tracing? #404

Open bangbaew opened 8 months ago

bangbaew commented 8 months ago

in Prisma Client Go, i'm able to do this

func ExecuteWithTracing[T any](ctx context.Context, query builder.Query) (*T, error) {

    ctx, span := tracer.Start(ctx, "Execute Query", trace.WithSpanKind(trace.SpanKindClient))
    defer span.End()

    span.SetAttributes(attribute.Key("query.method").String(query.Method))
    span.SetAttributes(attribute.Key("query.model").String(query.Model))
    span.SetAttributes(attribute.Key("query.name").String(query.Name))
    span.SetAttributes(attribute.Key("query.operation").String(query.Operation))
    span.SetAttributes(attribute.Key("query.build").String(query.Build()))
    span.SetAttributes(attribute.Key("query.build.inner").String(query.BuildInner()))
    span.SetAttributes(attribute.Key("query.engine.name").String(query.Engine.Name()))
    span.SetAttributes(attribute.Key("peer.service").String(dbHost))

    var result *T

    err := query.Exec(ctx, &result)
    if err != nil {
        span.SetStatus(codes.Error, err.Error())
        return nil, err
    }

    span.SetStatus(codes.Ok, "")

    b, _ := json.Marshal(&result)
    if b != nil {
        span.SetAttributes(attribute.Key("query.result").String(string(b)))
    }

    return result, nil
}

and call something like

func (dc *DishCuisineQueryParams) delete(ctx context.Context) (*db.DishCuisineModel, error) {
    q := prisma.Client.DishCuisine.FindUnique(
        db.DishCuisine.DishIDCuisineID(
            db.DishCuisine.DishID.Equals(dc.DishID),
            db.DishCuisine.CuisineID.Equals(dc.CuisineID),
        ),
    ).Delete()

    result, err := prisma.ExecuteWithTracing[db.DishCuisineModel](ctx, q.ExtractQuery())
    if err != nil {
        return nil, err
    }
    return result, nil
}

to achieve this tracing result: image

I would like to do the same in Prisma Client Rust, I'm currently passing a generic Future that returns prisma result like this, so I could not get anything but .await

pub async fn execute_with_tracing<T, F>(prisma_exec_fn: F) -> Result<T, ErrorResponse>
where
    T: serde::Serialize,
    F: std::future::Future<Output = prisma_client_rust::Result<T>>,
{
    let span = PRISMA_TRACER
        .span_builder("Execute Query")
        .with_kind(SpanKind::Client)
        .with_attributes(vec![
            Key::from_static_str("peer.service").string(DATABASE_HOST.to_owned())
        ])
        .start(&*PRISMA_TRACER);

    let cx = Context::current_with_span(span);
    let result = prisma_exec_fn.with_context(cx.to_owned()).await;

    match result.as_ref() {
        Ok(v) => {
            cx.span().set_status(Status::Ok);

            match prisma_client_rust::serde_json::ser::to_string(&v) {
                Ok(str) => cx.span().add_event(
                    "Success Execution".to_string(),
                    vec![Key::new("query.result").string(str)],
                ),
                Err(e) => cx.span().add_event(
                    "Failed Serialization".to_string(),
                    vec![Key::new("error.message").string(e.to_string())],
                ),
            }
        }
        Err(e) => {
            cx.span().set_status(Status::Error {
                description: e.to_string().into(),
            });

            return Err(handle_prisma_error(e));
        }
    };

    Ok(result.unwrap())
}

and call

async fn delete_chant(
    client: Data<PrismaClient>,
    id: Path<String>,
) -> Result<impl Responder, ErrorResponse> {
    let exec = client
        .chant()
        .delete(chant::id::equals(id.to_owned()))
        .exec();

    let deleted = execute_with_tracing(exec).await?;
    Ok(Json(deleted))
}

I would like to know if I can get query string, query method, operation, model name, crate version, as well as database name and database username or other tracing information from Prisma Client Rust, it would be great if someone could suggest me a better way to implement this function, like actix-web-opentelemetry crate which adds .trace_request() function to actix web client, thanks.