Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@sevasm
Copy link
Contributor

@sevasm sevasm commented May 11, 2016

Pull Request Checklist

Fixes

Fixes #6149

Purpose

To enable SQL logging with jdbcdslog the data source needs to be wrapped in a org.jdbcdslog.LogSqlDataSource. This was previously done at the database API level (in Databases.scala) before data sources were registered with JNDI. This causes database operations that use JPA to not be logged as their data source is not wrapped in a org.jdbcdslog.LogSqlDataSource. Now each supported connection pool wraps its own data sources when they are created and unwraps them before they are closed.

Background Context

The data sources are already registered with JNDI in the implementation classes for both supported connection pools, so I modified both to also automatically wrap the data source in a org.jdbcdslog.LogSqlDataSource if logSql is set to true. I have also moved the code that unwraps a data source when it needs to be closed. Since the functionality is the same for both connection pool implementations I placed the two methods (wrap data source and unwrap data source) in the ConnectionPool object.

It seems that registering any new data sources with JNDI is left up to the implementation of each connection pool at the moment, so it seemed appropriate that wrapping and unwrapping their data sources to support logging would be done there as well. Although I am not a huge fan of this as it makes implementing a new connection pool more difficult (have to remember to bind its data sources to JNDI and to wrap / unwrap the data sources to enable logging).

The code that actually wraps / unwraps the data sources could be copied into both implementations directly and removed from ConnectionPool to match what is done with JNDI registration code at the moment if that is preferable.

Apologies for not writing any test cases for this, not sure how I would go about that unfortunately. Maybe someone can direct me? But I can confirm I have tested the changes with both Java and Scala versions of Play.

Edit: I have now added a few tests to check whether the logSql configuration property is applied correctly and whether the right data source class is used both using the Database API and the JPA API.

References

Here's the original post about this issue on the Google group:

https://groups.google.com/d/msgid/play-framework/bd37c292-60b7-457c-a01c-018e824c8c70%40googlegroups.com?utm_medium=email&utm_source=footer

/**
* Wraps a data source in a org.jdbcdslog.LogSqlDataSource if the logSql configuration property is set to true.
*/
def maybeWrapDataSource(dataSource: DataSource, configuration: Config): DataSource = {
Copy link
Member

@marcospereira marcospereira May 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these methods could be private[db].

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, maybe the signature could be something like:

private[db] def wrapToLogSql(dataSource: DataSource, wrap: Boolean): DataSource

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @marcospereira,

thanks for reviewing the PR. I agree with the reduced visibility of the methods and the name change, but not sure about replacing Configuration with a Boolean. That would just mean the logSql property needs to be extracted in each connection pool implementation?

Would the following changes be acceptable?

/**
 * Wraps a data source in a org.jdbcdslog.LogSqlDataSource if the logSql configuration property is set to true.
 */
private[db] def wrapToLogSql(dataSource: DataSource, configuration: Config): DataSource = {
  if (configuration.getBoolean("logSql")) {
    val proxyDataSource = new LogSqlDataSource()
    proxyDataSource.setTargetDSDirect(dataSource)
    proxyDataSource
  } else {
    dataSource
  }
}

/**
 * Unwraps a data source if it has been previously wrapped in a org.jdbcdslog.LogSqlDataSource.
 */
private[db] def unwrap(dataSource: DataSource): DataSource = {
  dataSource match {
    case ds: LogSqlDataSource => ds.getTargetDatasource
    case _ => dataSource
  }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that using a logSql: Boolean parameter is more explicit than passing the whole configuration. Also, getting is pretty straightforward.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I see your point about passing around the whole configuration, but wouldn't it makes sense then to leave the parameter out of the method altogether and just do the check in each ConnectionPool implementation:

/**
 * Wraps a data source in a org.jdbcdslog.LogSqlDataSource.
 */
private[db] def wrapToLogSql(dataSource: DataSource): DataSource = {
  val proxyDataSource = new LogSqlDataSource()
  proxyDataSource.setTargetDSDirect(dataSource)
  proxyDataSource
}

Then in HikariCPModule (for example):

val wrappedDataSource = configuration.getBoolean("logSql") match {
  case true => ConnectionPool.wrapToLogSql(datasource)
  case _ => datasource
}

or (not sure which is more idiomatic to be honest):

val wrappedDataSource = if (configuration.getBoolean("logSql")) {
  ConnectionPool.wrapToLogSql(datasource)
} else {
  datasource
}

I say that because the Boolean parameter just controls whether the method has any effect at all it feels to me more natural to do the check outside the method itself, whereas passing the whole configuration at least leaves it up to ConnectionPool to control which configuration property is used to decide whether logging is enabled or not?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm ok with you passing the configuration instead of a boolean. This is just a small detail. Also, I would go with the if/else instead of the match/case.

@mkurz
Copy link
Member

mkurz commented May 20, 2016

Is this PR ready to merge?

@mkurz mkurz added this to the 2.5.4 milestone May 20, 2016
Updated data source wrapper method names
@sevasm
Copy link
Contributor Author

sevasm commented May 23, 2016

I have updated data source wrapper method visibility and names as suggested by @marcospereira, so from my point of view the PR can be merged.

@marcospereira marcospereira merged commit 4460cc8 into playframework:master May 24, 2016
marcospereira pushed a commit that referenced this pull request May 24, 2016
…6150)

* jdbcdslog is now enabled at the connection pool / data source level

* Added tests for the logSql configuration property

* Reduced data source wrapper method visibility
Updated data source wrapper method names
@marcospereira
Copy link
Member

Backported to 2.5.x: bb7ed3d

Thank you so much, @sevasm.

@sevasm sevasm deleted the issue-6149 branch May 24, 2016 11:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[2.5.3] SQL queries executed using the JPA API are not logged by jdbcdslog

3 participants