feature #10: log Query

This commit is contained in:
2019-07-18 00:02:26 +02:00
parent 32127b7e19
commit a32b70c316
9 changed files with 79 additions and 54 deletions

View File

@@ -2,7 +2,9 @@
<module type="JAVA_MODULE" version="4"> <module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true"> <component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/var" />
</content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>

View File

@@ -20,8 +20,9 @@ interface Executable {
fun <R: EntityI<*>> select(sql: String, typeReference: TypeReference<List<R>>, values: List<Any?> = emptyList()): List<R> fun <R: EntityI<*>> select(sql: String, typeReference: TypeReference<List<R>>, values: List<Any?> = emptyList()): List<R>
fun <R: EntityI<*>> select(sql: String, typeReference: TypeReference<List<R>>, values: Map<String, Any?>): List<R> fun <R: EntityI<*>> select(sql: String, typeReference: TypeReference<List<R>>, values: Map<String, Any?>): List<R>
fun <R: EntityI<*>> select(sql: String, page: Int, limit: Int, typeReference: TypeReference<List<R>>, values: Map<String, Any?>): Paginated<R> fun <R: EntityI<*>> select(sql: String, page: Int, limit: Int, typeReference: TypeReference<List<R>>, values: Map<String, Any?>): Paginated<R>
fun exec(sql: String, values: List<Any?> = emptyList()): CompletableFuture<QueryResult> fun exec(sql: String, values: List<Any?> = emptyList()): QueryResult
fun exec(sql: String, values: Map<String, Any?>): CompletableFuture<QueryResult> fun exec(sql: String, values: Map<String, Any?>): QueryResult
fun sendQuery(sql: String): QueryResult
} }
class Connection( class Connection(
@@ -35,7 +36,7 @@ class Connection(
private val serializer = Serializer() private val serializer = Serializer()
private val logger: Logger? by LoggerDelegate() private val logger: Logger? by LoggerDelegate()
fun connect(): ConnectionPool<PostgreSQLConnection> { internal fun connect(): ConnectionPool<PostgreSQLConnection> {
if (!::connection.isInitialized || !connection.isConnected()) { if (!::connection.isInitialized || !connection.isConnected()) {
connection = PostgreSQLConnectionBuilder.createConnectionPool( connection = PostgreSQLConnectionBuilder.createConnectionPool(
"jdbc:postgresql://$host:$port/$database?user=$username&password=$password" "jdbc:postgresql://$host:$port/$database?user=$username&password=$password"
@@ -47,8 +48,8 @@ class Connection(
fun <A> inTransaction(f: (Connection) -> CompletableFuture<A>) = connect().inTransaction(f) fun <A> inTransaction(f: (Connection) -> CompletableFuture<A>) = connect().inTransaction(f)
override fun <R: EntityI<*>> select(sql: String, typeReference: TypeReference<R>, values: List<Any?>): R? { override fun <R: EntityI<*>> select(sql: String, typeReference: TypeReference<R>, values: List<Any?>): R? {
val future = connect().sendPreparedStatement(sql, compileArgs(values)) val result = exec(sql, compileArgs(values))
val json = future.get().rows[0].getString(0) val json = result.rows[0].getString(0)
return if (json === null) { return if (json === null) {
null null
} else { } else {
@@ -69,8 +70,8 @@ class Connection(
select(sql, object: TypeReference<R>() {}, values) select(sql, object: TypeReference<R>() {}, values)
override fun <R: EntityI<*>> select(sql: String, typeReference: TypeReference<List<R>>, values: List<Any?>): List<R> { override fun <R: EntityI<*>> select(sql: String, typeReference: TypeReference<List<R>>, values: List<Any?>): List<R> {
val future = connect().sendPreparedStatement(sql, compileArgs(values)) val result = exec(sql, compileArgs(values))
val json = future.get().rows[0].getString(0) val json = result.rows[0].getString(0)
return if (json === null) { return if (json === null) {
listOf<EntityI<*>>() as List<R> listOf<EntityI<*>>() as List<R>
} else { } else {
@@ -94,7 +95,7 @@ class Connection(
.plus("limit" to limit) .plus("limit" to limit)
val line = replaceArgs(sql, newValues) { val line = replaceArgs(sql, newValues) {
connect().sendPreparedStatement(this.sql, compileArgs(this.parameters)).get().rows[0] exec(this.sql, compileArgs(this.parameters)).rows[0]
} }
return line.run { return line.run {
@@ -134,17 +135,24 @@ class Connection(
inline fun <reified R: EntityI<*>> select(sql: String, values: Map<String, Any?>): List<R> = inline fun <reified R: EntityI<*>> select(sql: String, values: Map<String, Any?>): List<R> =
select(sql, object: TypeReference<List<R>>() {}, values) select(sql, object: TypeReference<List<R>>() {}, values)
override fun exec(sql: String, values: List<Any?>): CompletableFuture<QueryResult> { override fun exec(sql: String, values: List<Any?>): QueryResult {
logger?.debug(sql, values) return stopwatchQuery(sql, values) {
return connect().sendPreparedStatement(sql, compileArgs(values)) connect().sendPreparedStatement(sql, compileArgs(values)).join()
}
} }
override fun exec(sql: String, values: Map<String, Any?>): CompletableFuture<QueryResult> { override fun exec(sql: String, values: Map<String, Any?>): QueryResult {
return replaceArgs(sql, values) { return replaceArgs(sql, values) {
exec(this.sql, this.parameters) exec(this.sql, this.parameters)
} }
} }
override fun sendQuery(sql: String): QueryResult {
return stopwatchQuery(sql) {
connect().sendQuery(sql).join()
}
}
private fun compileArgs(values: List<Any?>): List<Any?> { private fun compileArgs(values: List<Any?>): List<Any?> {
return values.map { return values.map {
if (it is EntityI<*>) { if (it is EntityI<*>) {
@@ -174,6 +182,21 @@ class Connection(
} }
data class ParametersQuery(val sql: String, val parameters: List<Any?>) data class ParametersQuery(val sql: String, val parameters: List<Any?>)
private fun <T> stopwatchQuery(sql: String, values: List<Any?> = emptyList(), callback: () -> T): T {
val sqlForLog = "\n"+sql.prependIndent()
try {
val start = System.currentTimeMillis()
val result = callback()
val duration = (System.currentTimeMillis() - start)
logger?.debug("$duration ms for query: $sqlForLog", values)
return result
} catch (e: Throwable) {
logger?.info("Query Error: $sqlForLog", e)
throw e
}
}
} }
data class Paginated<T: EntityI<*>>( data class Paginated<T: EntityI<*>>(

View File

@@ -4,7 +4,6 @@ import com.fasterxml.jackson.core.type.TypeReference
import com.github.jasync.sql.db.QueryResult import com.github.jasync.sql.db.QueryResult
import fr.postgresjson.entity.EntityI import fr.postgresjson.entity.EntityI
import java.io.File import java.io.File
import java.util.concurrent.CompletableFuture
import fr.postgresjson.definition.Function as DefinitionFunction import fr.postgresjson.definition.Function as DefinitionFunction
class Requester( class Requester(
@@ -109,11 +108,11 @@ class Requester(
inline fun <reified R: EntityI<*>> select(page: Int, limit: Int, values: Map<String, Any?> = emptyMap()): Paginated<R> = inline fun <reified R: EntityI<*>> select(page: Int, limit: Int, values: Map<String, Any?> = emptyMap()): Paginated<R> =
select(page, limit, object: TypeReference<List<R>>() {}, values) select(page, limit, object: TypeReference<List<R>>() {}, values)
override fun exec(values: List<Any?>): CompletableFuture<QueryResult> { override fun exec(values: List<Any?>): QueryResult {
return connection.exec(sql, values) return connection.exec(sql, values)
} }
override fun exec(values: Map<String, Any?>): CompletableFuture<QueryResult> { override fun exec(values: Map<String, Any?>): QueryResult {
return connection.exec(sql, values) return connection.exec(sql, values)
} }
} }
@@ -185,14 +184,14 @@ class Requester(
inline fun <reified R: EntityI<*>> select(page: Int, limit: Int, values: Map<String, Any?> = emptyMap()): Paginated<R> = inline fun <reified R: EntityI<*>> select(page: Int, limit: Int, values: Map<String, Any?> = emptyMap()): Paginated<R> =
select(page, limit, object: TypeReference<List<R>>() {}, values) select(page, limit, object: TypeReference<List<R>>() {}, values)
override fun exec(values: List<Any?>): CompletableFuture<QueryResult> { override fun exec(values: List<Any?>): QueryResult {
val args = compileArgs(values) val args = compileArgs(values)
val sql = "SELECT * FROM ${definition.name} ($args)" val sql = "SELECT * FROM ${definition.name} ($args)"
return connection.exec(sql, values) return connection.exec(sql, values)
} }
override fun exec(values: Map<String, Any?>): CompletableFuture<QueryResult> { override fun exec(values: Map<String, Any?>): QueryResult {
val args = compileArgs(values) val args = compileArgs(values)
val sql = "SELECT * FROM ${definition.name} ($args)" val sql = "SELECT * FROM ${definition.name} ($args)"
@@ -239,8 +238,8 @@ class Requester(
fun <R: EntityI<*>> select(page: Int, limit: Int, typeReference: TypeReference<List<R>>, values: Map<String, Any?>): Paginated<R> fun <R: EntityI<*>> select(page: Int, limit: Int, typeReference: TypeReference<List<R>>, values: Map<String, Any?>): Paginated<R>
fun exec(values: List<Any?> = emptyList()): CompletableFuture<QueryResult> fun exec(values: List<Any?> = emptyList()): QueryResult
fun exec(values: Map<String, Any?>): CompletableFuture<QueryResult> fun exec(values: Map<String, Any?>): QueryResult
} }
class RequesterFactory( class RequesterFactory(

View File

@@ -154,10 +154,10 @@ data class Migrations private constructor(
private fun initDB() { private fun initDB() {
if (!initialized) { if (!initialized) {
File(this::class.java.getResource("/sql/migration/createHistoryShema.sql").toURI()).let { File(this::class.java.getResource("/sql/migration/createHistoryShema.sql").toURI()).let {
connection.connect().sendQuery(it.readText()).join() connection.sendQuery(it.readText())
} }
File(this::class.java.getResource("/sql/migration/createFunctionShema.sql").toURI()).let { File(this::class.java.getResource("/sql/migration/createFunctionShema.sql").toURI()).let {
connection.connect().sendQuery(it.readText()).join() connection.sendQuery(it.readText())
} }
initialized = true initialized = true
} }
@@ -165,7 +165,7 @@ data class Migrations private constructor(
private fun lock() { private fun lock() {
File(this::class.java.getResource("/sql/migration/lockMigrationTables.sql").toURI()).let { File(this::class.java.getResource("/sql/migration/lockMigrationTables.sql").toURI()).let {
connection.connect().sendQuery(it.readText()).join() connection.sendQuery(it.readText())
} }
} }
@@ -221,8 +221,8 @@ data class Migrations private constructor(
fun run(): Map<Pair<String, Direction>, Status> { fun run(): Map<Pair<String, Direction>, Status> {
val list: MutableMap<Pair<String, Direction>, Status> = mutableMapOf() val list: MutableMap<Pair<String, Direction>, Status> = mutableMapOf()
connection.connect().apply { connection.apply {
sendQuery("BEGIN").join() sendQuery("BEGIN")
lock() lock()
up().map { up().map {
list[Pair(it.key, Direction.UP)] = it.value list[Pair(it.key, Direction.UP)] = it.value
@@ -230,7 +230,7 @@ data class Migrations private constructor(
down(true).map { down(true).map {
list[Pair(it.key, Direction.DOWN)] = it.value list[Pair(it.key, Direction.DOWN)] = it.value
} }
sendQuery("COMMIT").join() sendQuery("COMMIT")
} }
return list.toMap() return list.toMap()
@@ -242,15 +242,15 @@ data class Migrations private constructor(
private fun runTest(): Map<Pair<String, Direction>, Status> { private fun runTest(): Map<Pair<String, Direction>, Status> {
val list: MutableMap<Pair<String, Direction>, Status> = mutableMapOf() val list: MutableMap<Pair<String, Direction>, Status> = mutableMapOf()
connection.connect().apply { connection.apply {
sendQuery("BEGIN").join() sendQuery("BEGIN")
up().map { up().map {
list[Pair(it.key, Direction.UP)] = it.value list[Pair(it.key, Direction.UP)] = it.value
} }
down(true).map { down(true).map {
list[Pair(it.key, Direction.DOWN)] = it.value list[Pair(it.key, Direction.DOWN)] = it.value
} }
sendQuery("ROLLBACK").join() sendQuery("ROLLBACK")
} }
return list.toMap() return list.toMap()

View File

@@ -16,7 +16,7 @@ data class Query(
override var doExecute: Action? = null override var doExecute: Action? = null
override fun up(): Migration.Status { override fun up(): Migration.Status {
connection.exec(up).join() connection.exec(up)
File(this::class.java.getResource("/sql/migration/insertHistory.sql").toURI()).let { File(this::class.java.getResource("/sql/migration/insertHistory.sql").toURI()).let {
connection.selectOne<MigrationEntity>(it.readText(), listOf(name, up, down))?.let { query -> connection.selectOne<MigrationEntity>(it.readText(), listOf(name, up, down))?.let { query ->
@@ -29,7 +29,7 @@ data class Query(
} }
override fun down(): Migration.Status { override fun down(): Migration.Status {
connection.exec(down).join() connection.exec(down)
File(this::class.java.getResource("/sql/migration/deleteHistory.sql").toURI()).let { File(this::class.java.getResource("/sql/migration/deleteHistory.sql").toURI()).let {
connection.exec(it.readText(), listOf(name)) connection.exec(it.readText(), listOf(name))

View File

@@ -0,0 +1,8 @@
package fr.postgresjson.stopwatch
fun <T> elapse(callback: (start: Long) -> T, after: (elapse: Long) -> Unit): T {
val start = System.currentTimeMillis()
val result = callback(start)
after(System.currentTimeMillis() - start)
return result
}

View File

@@ -1,10 +1,10 @@
package fr.postgresjson package fr.postgresjson
import com.github.jasync.sql.db.util.isCompleted
import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Connection
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.entity.IdEntity import fr.postgresjson.entity.IdEntity
import org.junit.Assert.* import org.junit.Assert.*
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
@@ -41,7 +41,7 @@ class ConnectionTest(): TestAbstract() {
from test2 t from test2 t
JOIN test t2 ON t.test_id = t2.id JOIN test t2 ON t.test_id = t2.id
) j; ) j;
""" """.trimIndent()
) )
assertNotNull(objs) assertNotNull(objs)
assertTrue(objs is List<ObjTest2>) assertTrue(objs is List<ObjTest2>)
@@ -71,9 +71,8 @@ class ConnectionTest(): TestAbstract() {
@Test @Test
fun callExec() { fun callExec() {
val o = ObjTest("myName") val o = ObjTest("myName")
val future = connection.exec("select json_build_object('id', 1, 'name', ?::json->>'name')", listOf(o)) val result = connection.exec("select json_build_object('id', 1, 'name', ?::json->>'name')", listOf(o))
future.join() Assertions.assertEquals(1, result.rowsAffected)
assertTrue(future.isCompleted)
} }
@Test @Test
@@ -107,9 +106,9 @@ class ConnectionTest(): TestAbstract() {
""".trimIndent(), """.trimIndent(),
params params
) )
assertEquals(result[0]!!.first, "ff") assertEquals(result[0].first, "ff")
assertEquals(result[0]!!.seconde, "sec") assertEquals(result[0].seconde, "sec")
assertEquals(result[0]!!.third, 123) assertEquals(result[0].third, 123)
} }
@Test @Test

View File

@@ -1,16 +1,12 @@
package fr.postgresjson package fr.postgresjson
import com.github.jasync.sql.db.QueryResult
import com.github.jasync.sql.db.util.isCompleted
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.IdEntity import fr.postgresjson.entity.IdEntity
import org.junit.Assert import org.junit.Assert
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.io.File import java.io.File
import java.util.concurrent.CompletableFuture
class RequesterTest: TestAbstract() { class RequesterTest: TestAbstract() {
class ObjTest(var name:String): IdEntity(1) class ObjTest(var name:String): IdEntity(1)
@@ -42,25 +38,23 @@ class RequesterTest: TestAbstract() {
@Test @Test
fun `call exec on query`() { fun `call exec on query`() {
val resources = File(this::class.java.getResource("/sql/query").toURI()) val resources = File(this::class.java.getResource("/sql/query").toURI())
val future: CompletableFuture<QueryResult> = Requester(getConnextion()) val result = Requester(getConnextion())
.addQuery(resources) .addQuery(resources)
.getQuery("Test/selectOne") .getQuery("Test/selectOne")
.exec() .exec()
future.join() assertEquals(1, result.rowsAffected)
Assertions.assertTrue(future.isCompleted)
} }
@Test @Test
fun `call exec on function`() { fun `call exec on function`() {
val resources = File(this::class.java.getResource("/sql/function").toURI()) val resources = File(this::class.java.getResource("/sql/function").toURI())
val future: CompletableFuture<QueryResult> = Requester(getConnextion()) val result = Requester(getConnextion())
.addFunction(resources) .addFunction(resources)
.getFunction("test_function") .getFunction("test_function")
.exec(listOf("test", "plip")) .exec(listOf("test", "plip"))
future.join() assertEquals(1, result.rowsAffected)
Assertions.assertTrue(future.isCompleted)
} }
@Test @Test

View File

@@ -4,23 +4,23 @@
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder> </encoder>
</appender> </appender>
<property name="LOG_DIR" value="./postgresjson" /> <property name="LOG_DIR" value="./var/log" />
<appender name="query" class="ch.qos.logback.core.FileAppender"> <appender name="query" class="ch.qos.logback.core.FileAppender">
<file>${LOG_DIR}/query.log</file> <file>${LOG_DIR}/query.log</file>
<append>true</append> <append>true</append>
<encoder> <encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern> <pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder> </encoder>
</appender> </appender>
<appender name="other" class="ch.qos.logback.core.FileAppender"> <appender name="other" class="ch.qos.logback.core.FileAppender">
<file>${LOG_DIR}/other.log</file> <file>${LOG_DIR}/other.log</file>
<append>true</append> <append>true</append>
<encoder> <encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} - %msg%n</pattern> <pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder> </encoder>
</appender> </appender>
<root level="trace"> <root level="debug">
<appender-ref ref="STDOUT"/> <appender-ref ref="STDOUT"/>
<appender-ref ref="other" /> <appender-ref ref="other" />
</root> </root>
@@ -28,7 +28,7 @@
<logger name="org.eclipse.jetty" level="INFO"/> <logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="INFO"/> <logger name="io.netty" level="INFO"/>
<logger name="fr.postgresjson.connexion" level="trace"> <logger name="fr.postgresjson.connexion" level="debug">
<appender-ref ref="query" /> <appender-ref ref="query" />
</logger> </logger>
</configuration> </configuration>