Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Starting with version 13.4, the Microsoft JDBC Driver for SQL Server provides a performance metrics framework for tracking the timing of critical driver operations. You can use this framework to observe and analyze connection and statement execution behavior, helping identify latency bottlenecks in your application's interactions with SQL Server.
Metrics are available through two mechanisms that can be used independently or together:
- Programmatic callback - Register a
PerformanceLogCallbackto receive metrics in your application code. - Java logging - Subscribe to dedicated
java.util.loggingloggers to capture metrics in log output.
Tracked activities
The driver tracks activities at two levels: connection and statement.
Connection-level activities
| Activity | Description |
|---|---|
CONNECTION |
Total time for establishing a connection, including all subactivities. |
PRELOGIN |
Time for TDS prelogin negotiation with the server. |
LOGIN |
Time for the TDS login and authentication handshake. |
TOKEN_ACQUISITION |
Time to acquire federated authentication tokens when using Microsoft Entra authentication. |
Statement-level activities
| Activity | Description |
|---|---|
STATEMENT_REQUEST_BUILD |
Client-side time to build the TDS request (parameter binding, SQL processing, packet construction). Timing-only; doesn't track exceptions. |
STATEMENT_FIRST_SERVER_RESPONSE |
Time from sending the request to receiving the first server response. Timing-only; doesn't track exceptions. |
STATEMENT_PREPARE |
Time for sp_prepare when prepareMethod=prepare. |
STATEMENT_PREPEXEC |
Time for combined prepare and execute via sp_prepexec. |
STATEMENT_EXECUTE |
Time for statement execution (sp_executesql, sp_execute, direct SQL, or batch). |
Enable performance metrics
Option 1: Register a callback
Register a PerformanceLogCallback to receive performance data programmatically:
SQLServerDriver.registerPerformanceLogCallback(new PerformanceLogCallback() {
@Override
public void publish(PerformanceActivity activity, int connectionId,
long durationMs, Exception exception) {
// Connection-level metrics
System.out.printf("Activity: %s, Connection: %d, Duration: %d ms%n",
activity, connectionId, durationMs);
}
@Override
public void publish(PerformanceActivity activity, int connectionId,
int statementId, long durationMs, Exception exception) {
// Statement-level metrics
System.out.printf("Activity: %s, Connection: %d, Statement: %d, Duration: %d ms%n",
activity, connectionId, statementId, durationMs);
}
});
Use nanosecond timing and statement context
Starting with version 13.6, a callback can opt in to nanosecond timing by overriding useNanoseconds(). The callback can also call getCurrentUserSql() and getCurrentStatementType() to correlate a statement-level event with the SQL text submitted by the application and the type of statement that executed. getCurrentStatementType() returns STATEMENT, PREPARED_STATEMENT, or CALLABLE_STATEMENT. Use getCurrentApplicationName() to identify the connection or pool that produced an event from its applicationName connection property. If applicationName isn't set, the method returns the driver's default application name.
SQLServerDriver.registerPerformanceLogCallback(new PerformanceLogCallback() {
@Override
public boolean useNanoseconds() {
return true;
}
@Override
public void publish(PerformanceActivity activity, int connectionId,
long durationNs, Exception exception) {
// Connection-level metrics don't have SQL statement context.
System.out.printf("Application: %s, Activity: %s, Connection: %d, Duration: %d ns%n",
getCurrentApplicationName(), activity, connectionId, durationNs);
}
@Override
public void publish(PerformanceActivity activity, int connectionId,
int statementId, long durationNs, Exception exception) {
String applicationName = getCurrentApplicationName();
String userSql = getCurrentUserSql();
StatementType statementType = getCurrentStatementType();
System.out.printf("Application: %s, Type: %s, Activity: %s, SQL: %s, Duration: %d ns%n",
applicationName, statementType, activity, userSql, durationNs);
}
});
Nanosecond timing is opt-in. Callbacks that don't override useNanoseconds() continue to receive durations in milliseconds. When a registered callback opts in, the dedicated performance logger output also uses nanoseconds and labels durations with ns; otherwise, it uses milliseconds and ms. Calling System.nanoTime() can have more overhead than System.currentTimeMillis() on some platforms.
Callback context is available only during the corresponding publish() invocation. getCurrentApplicationName() is available for connection-level and statement-level events and returns null outside publish(). It can also return null when connection properties haven't been parsed, such as when a connection fails before the application name is resolved. The SQL context methods return null for connection-level events, statement subactivity metrics that don't have SQL context, and calls made outside publish().
Caution
SQL text can contain sensitive data in literals or comments. Sanitize it and apply your organization's data-handling requirements before writing it to logs or telemetry.
Option 2: Configure Java logging
Configure java.util.logging for the performance metrics loggers at FINE level.
In a logging.properties file:
com.microsoft.sqlserver.jdbc.PerformanceMetrics.Connection.level = FINE
com.microsoft.sqlserver.jdbc.PerformanceMetrics.Statement.level = FINE
handlers = java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = FINE
Or programmatically:
Logger.getLogger("com.microsoft.sqlserver.jdbc.PerformanceMetrics.Connection")
.setLevel(Level.FINE);
Logger.getLogger("com.microsoft.sqlserver.jdbc.PerformanceMetrics.Statement")
.setLevel(Level.FINE);
Prepare method effect on statement activities
The activities tracked for PreparedStatement depend on the prepareMethod connection property. For more information about prepareMethod, see Setting the connection properties.
prepareMethod setting |
First execution | Second execution | Third+ execution |
|---|---|---|---|
prepexec (default) |
STATEMENT_EXECUTE (sp_executesql) |
STATEMENT_PREPEXEC (sp_prepexec) |
STATEMENT_EXECUTE (sp_execute) |
prepare |
STATEMENT_PREPARE + STATEMENT_EXECUTE |
STATEMENT_EXECUTE |
STATEMENT_EXECUTE |
none |
STATEMENT_EXECUTE (direct SQL) |
STATEMENT_EXECUTE (direct SQL) |
STATEMENT_EXECUTE (direct SQL) |
Note
With the default prepexec setting, the driver defers preparation assuming single use. The second execution uses sp_prepexec (combined prepare and execute). From the third execution onward, the cached handle is reused via sp_execute. To force sp_prepexec on the first call, set the connection property enablePrepareOnFirstPreparedStatementCall to true.
Sample log output
The following output shows the activities tracked across three consecutive executions of a PreparedStatement with the default prepexec setting:
ConnectionID:1, StatementID:1 Request build time, duration: 8ms
ConnectionID:1, StatementID:1 First server response, duration: 17ms
ConnectionID:1, StatementID:1 Statement execute, duration: 75ms ← 1st call: sp_executesql
ConnectionID:1, StatementID:1 Request build time, duration: 9ms
ConnectionID:1, StatementID:1 First server response, duration: 0ms
ConnectionID:1, StatementID:1 Statement prepexec, duration: 0ms ← 2nd call: sp_prepexec
ConnectionID:1, StatementID:1 Request build time, duration: 0ms
ConnectionID:1, StatementID:1 First server response, duration: 0ms
ConnectionID:1, StatementID:1 Statement execute, duration: 0ms ← 3rd call: sp_execute