Gremlinq.Extensions.Diagnostics
Gremlinq.Extensions.Diagnostics is a powerful extension library for ExRam.Gremlinq that adds comprehensive diagnostic capabilities to your graph database queries. Whether you're troubleshooting production issues, optimizing query performance, or simply trying to understand query execution patterns, this extension provides the telemetry and debugging tools you need.
OpenTelemetry integration
Gremlinq.Extensions.Diagnostics provides first-class support for OpenTelemetry, the industry-standard observability framework.
Core functionality
This integration automatically creates distributed tracing spans for every query execution, enabling you to
- track query execution times in your observability platform,
- correlate graph queries with other application operations,
- monitor performance trends across your application.
With a single method call, you can instrument your entire Gremlinq query execution pipeline:
services // services is IServiceCollection
.AddOpenTelemetry()
.WithTracing(builder => builder
.AddGremlinqExecutorInstrumentation());
The library also extends IGremlinQuerySource with fluent methods to attach meaningful context
to your queries:
async Task<Customer[]> GetCustomers(
[CallerLineNumber] int lineNumber = 0,
[CallerFilePath] string filePath = "",
[CallerMemberName] string callerMemberName = "")
{
return await source
.WithQueryName(nameof(GetCustomers))
.WithCallerLineNumber(lineNumber)
.WithCallerFilePath(filePath)
.WithCallerMemberName(callerMemberName)
.V<Customer>()
.ToArrayAsync();
}
These extensions allow you to
- name queries for easy identification in traces and logs,
- capture caller information to understand where queries originate in your codebase,
- track execution context with file paths and line numbers.
When OpenTelemetry instrumentation is enabled, the library automatically enriches activity spans with valuable metadata:
gremlinq.execution.id- Unique identifier for each query executiongremlinq.query.name- Custom query name (when provided)gremlinq.caller.memberName- The method that initiated the querygremlinq.caller.filePath- Source file locationgremlinq.caller.lineNumber- Exact line number in source code
The instrumentation layer automatically captures exceptions and marks failed queries with the corresponding exception, making it effortless to identify and diagnose query failures in production.
Key Benefits
Production-ready observability
Stop flying blind in production. With distributed tracing support, you can visualize query execution as part of your complete application flow, identifying bottlenecks and performance issues with precision.
Developer-friendly debugging
The metadata tracking capabilities mean you'll know exactly which line of code triggered a problematic query. No more hunting through logs trying to correlate query strings with source code.
Zero-overhead when disabled
The library is designed with performance in mind. When OpenTelemetry listeners aren't active, the instrumentation has minimal overhead, making it safe to deploy in all environments.
Seamless integration
Built on industry standards like OpenTelemetry, the library integrates effortlessly with your existing observability stack. No vendor lock-in, no proprietary formats—just clean, standard telemetry data.
Diagnostic query-responses on CosmosDb
When working with Azure Cosmos DB's Gremlin API, developers need actionable metrics to optimize their queries, manage costs, and ensure their applications run efficiently. This is where Gremlinq.Extensions.Diagnostics.CosmosDb comes into play. The extension provides comprehensive diagnostic capabilities specifically tailored for Azure Cosmos DB, giving developers unprecedented visibility into their graph query execution.
Core functionality
The library extends Gremlinq's query interface with a suite of diagnostic methods that mirror standard query execution patterns but return rich diagnostic data alongside your results:
var query = source
.V<Person>()
.Where(p => p.Name == "Bob");
await query.DiagnosticFirstAsync();
await query.DiagnosticFirstOrDefaultAsync();
await query.DiagnosticLastAsync();
await query.DiagnosticLastOrDefaultAsync();
await query.DiagnosticSingleAsync();
await query.DiagnosticSingleOrDefaultAsync();
await query.DiagnosticToArrayAsync();
Each diagnostic response provides a wealth of information critical for production applications:
- Request Charge (RU/s): Both individual and total request charges, essential for cost monitoring and optimization
- Server Time: Actual server-side execution time for performance analysis
- Status Codes: Azure Cosmos DB-specific status codes with detailed error categorization
- Sub-Status Codes: Additional context for error scenarios
- Activity ID: Unique identifier for request correlation and support troubleshooting
- Retry-After: Throttling information to implement intelligent retry logic
- Partial Responses: For array results, access to individual response chunks with their respective metrics
Key Benefits
Cost Optimization
Azure Cosmos DB charges are based on Request Units (RUs). With diagnostic extensions, you can:
- Monitor exact RU consumption per query
- Identify expensive operations in your data access layer
- Make data-driven decisions about the query structure
Performance Analysis
Understanding where time is spent is crucial:
- Measure server-side execution time accurately
- Identify slow queries before they impact users
- Compare query variations to choose optimal approaches
- Monitor performance trends over time
Comprehensive Error Handling
The library includes a complete CosmosDbStatusCode enumeration with meaningful status codes
Production-Ready Observability
- Activity IDs for end-to-end request tracing
- Partial response tracking
- Original response status access for comprehensive logging
- Integration-friendly structure for monitoring systems
Usage examples
- Logging the total request charge of a FirstOrDefaultAsync-terminated query
var diagnosticResponse = await source
.V<Person>()
.Where(p => p.Name == "Bob")
.DiagnosticFirstOrDefaultAsync();
logger
.LogInformation("The query consumed {totalRequestCharge} RUs.", diagnosticResponse.TotalRequestCharge);
return diagnosticResponse.Data;
- Logging the total server time of a FirstAsync-terminated query
var diagnosticResponse = await source
.V<Person>()
.Where(p => p.Name == "Bob")
.DiagnosticFirstOrDefaultAsync();
logger
.LogInformation("The query took {totalServerTime} to execute on the server.", diagnosticResponse.TotalServerTime);
return diagnosticResponse.Data;
- Logging total server time and partial request charges
var diagnosticResponses = await source
.V<Person>()
.Where(p => p.Name!.StartsWith("B"))
.DiagnosticToArrayAsync();
logger
.LogInformation("The query took {totalServerTime} to execute on the server.", diagnosticResponses.TotalServerTime);
logger
.LogInformation("The query consumed {totalRequestCharge} RUs.", diagnosticResponses.TotalRequestCharge);
foreach (var diagnosticResponse in diagnosticResponses.PartialResponses)
{
logger
.LogInformation("Partial execution of the query consumed {requestCharge} RUs.", diagnosticResponse.RequestCharge);
}
- Implementing a retry pattern
while (true)
{
var diagnosticResponse = await source
.V<Person>()
.Where(p => p.Name == "Bob")
.DiagnosticFirstOrDefaultAsync();
if (diagnosticResponse.StatusCode != CosmosDbStatusCode.TooManyRequests)
return diagnosticResponse.Data;
if (diagnosticResponse.RetryAfter is { } retryAfter)
await Task.Delay(retryAfter);
}
Formatted and indented Debug()
The UseFormattedGroovyGremlinQueryDebugger is an extension method in the Gremlinq.Extensions.Diagnostics project that
configures a custom debugger for Gremlinq queries. This debugger transforms bytecode into human-readable, formatted
and indented Groovy script syntax, making it significantly easier to understand and debug complex graph traversal queries.
Core functionality
Formatted/indented Output
The Debug-formatter produces multi-line, properly indented Groovy script that mirrors the structure of your Gremlinq queries, making them much easier to read and understand. The formatter intelligently indents nested queries and sub-traversals, using 2-space indentation levels to clearly show the query hierarchy.
Value Redaction
The method accepts a bool redactValues parameter that allows you to control whether actual values in the query are displayed or redacted.
//Without redaction
source
.ConfigureEnvironment(env => env
.UseFormattedGroovyGremlinQueryDebugger(false));
Pricing
| Quantity (Developer Seats) | Unit price (€) / year |
|---|---|
| 1 to ∞ | 129.00 |