solution: [optionally] provide request/response value with access log
This commit is contained in:
@@ -430,6 +430,12 @@ accessLog:
|
|||||||
| `false`
|
| `false`
|
||||||
| Enable/Disable Access logging
|
| Enable/Disable Access logging
|
||||||
|
|
||||||
|
| `include-messages`
|
||||||
|
| `false`
|
||||||
|
| Include request params and response result/error (i.e., a JSON) in access log.
|
||||||
|
It's an expensive operation, use it for debugging only.
|
||||||
|
Note that for errors it provides only error message, not the error response itself.
|
||||||
|
|
||||||
| `filename`
|
| `filename`
|
||||||
| `access_log.jsonl`
|
| `access_log.jsonl`
|
||||||
| Path to the access log file
|
| Path to the access log file
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
class AccessLogConfig(
|
class AccessLogConfig(
|
||||||
val enabled: Boolean = false
|
val enabled: Boolean = false,
|
||||||
|
val includeMessages: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var filename: String = "./access_log.jsonl"
|
var filename: String = "./access_log.jsonl"
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ class AccessLogReader : YamlConfigReader(), ConfigReader<AccessLogConfig> {
|
|||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
AccessLogConfig.disabled()
|
AccessLogConfig.disabled()
|
||||||
} else {
|
} else {
|
||||||
val config = AccessLogConfig(true)
|
val includeMessages = getValueAsBool(node, "include-messages") ?: false
|
||||||
|
val config = AccessLogConfig(true, includeMessages)
|
||||||
getValueAsString(node, "filename")?.let {
|
getValueAsString(node, "filename")?.let {
|
||||||
config.filename = it
|
config.filename = it
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ class AccessLogWriter(
|
|||||||
}
|
}
|
||||||
log.info("Writing Access Log to ${filename.absolutePath}")
|
log.info("Writing Access Log to ${filename.absolutePath}")
|
||||||
scheduler.schedule(runner, START_SLEEP_MS, TimeUnit.MILLISECONDS)
|
scheduler.schedule(runner, START_SLEEP_MS, TimeUnit.MILLISECONDS)
|
||||||
|
|
||||||
|
//propagate current config to the Event Builder, so it knows which details to include
|
||||||
|
EventsBuilder.accessLogConfig = config
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun flushRunner() {
|
private fun flushRunner() {
|
||||||
|
|||||||
@@ -108,7 +108,9 @@ class Events {
|
|||||||
val succeed: Boolean,
|
val succeed: Boolean,
|
||||||
val rpcError: Int? = null,
|
val rpcError: Int? = null,
|
||||||
val payloadSizeBytes: Long,
|
val payloadSizeBytes: Long,
|
||||||
val nativeCall: NativeCallItemDetails
|
val nativeCall: NativeCallItemDetails,
|
||||||
|
val responseBody: String? = null,
|
||||||
|
val errorMessage: String? = null
|
||||||
) : ChainBase(blockchain, "NativeCall", id, channel)
|
) : ChainBase(blockchain, "NativeCall", id, channel)
|
||||||
|
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
@@ -120,7 +122,8 @@ class Events {
|
|||||||
// info about the initial request, that may include several native calls
|
// info about the initial request, that may include several native calls
|
||||||
val request: StreamRequestDetails,
|
val request: StreamRequestDetails,
|
||||||
val payloadSizeBytes: Long,
|
val payloadSizeBytes: Long,
|
||||||
val nativeSubscribe: NativeSubscribeItemDetails
|
val nativeSubscribe: NativeSubscribeItemDetails,
|
||||||
|
val responseBody: String? = null,
|
||||||
) : ChainBase(blockchain, "NativeSubscribe", id, channel)
|
) : ChainBase(blockchain, "NativeSubscribe", id, channel)
|
||||||
|
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
@@ -159,7 +162,8 @@ class Events {
|
|||||||
data class NativeCallItemDetails(
|
data class NativeCallItemDetails(
|
||||||
val method: String,
|
val method: String,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val payloadSizeBytes: Long
|
val payloadSizeBytes: Long,
|
||||||
|
val requestParams: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
data class NativeCallReplyDetails(
|
data class NativeCallReplyDetails(
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ package io.emeraldpay.dshackle.monitoring.accesslog
|
|||||||
|
|
||||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||||
import io.emeraldpay.api.proto.Common
|
import io.emeraldpay.api.proto.Common
|
||||||
|
import io.emeraldpay.dshackle.Global
|
||||||
|
import io.emeraldpay.dshackle.config.AccessLogConfig
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcError
|
||||||
import io.emeraldpay.grpc.Chain
|
import io.emeraldpay.grpc.Chain
|
||||||
import io.grpc.Attributes
|
import io.grpc.Attributes
|
||||||
import io.grpc.Grpc
|
import io.grpc.Grpc
|
||||||
@@ -36,6 +39,10 @@ class EventsBuilder {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LoggerFactory.getLogger(EventsBuilder::class.java)
|
private val log = LoggerFactory.getLogger(EventsBuilder::class.java)
|
||||||
|
|
||||||
|
// A reference to the config for current _running instance_.
|
||||||
|
// Initialized by AccessLogWriter
|
||||||
|
var accessLogConfig: AccessLogConfig = AccessLogConfig.default()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StartingHttp2Request {
|
interface StartingHttp2Request {
|
||||||
@@ -306,7 +313,10 @@ class EventsBuilder {
|
|||||||
Events.NativeCallItemDetails(
|
Events.NativeCallItemDetails(
|
||||||
item.method,
|
item.method,
|
||||||
item.id,
|
item.id,
|
||||||
item.payload.size().toLong()
|
item.payload.size().toLong(),
|
||||||
|
if (accessLogConfig.includeMessages) {
|
||||||
|
if (item.payload != null && !item.payload.isEmpty && item.payload.isValidUtf8) item.payload.toStringUtf8() else ""
|
||||||
|
} else null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -323,7 +333,11 @@ class EventsBuilder {
|
|||||||
nativeCall = item,
|
nativeCall = item,
|
||||||
payloadSizeBytes = item.payloadSizeBytes,
|
payloadSizeBytes = item.payloadSizeBytes,
|
||||||
id = UUID.randomUUID(),
|
id = UUID.randomUUID(),
|
||||||
channel = Events.Channel.GRPC
|
channel = Events.Channel.GRPC,
|
||||||
|
responseBody = if (accessLogConfig.includeMessages) {
|
||||||
|
if (msg.payload != null && !msg.payload.isEmpty && msg.payload.isValidUtf8) msg.payload.toStringUtf8() else ""
|
||||||
|
} else null,
|
||||||
|
errorMessage = if (accessLogConfig.includeMessages) msg.errorMessage else null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,7 +355,13 @@ class EventsBuilder {
|
|||||||
nativeCall = item,
|
nativeCall = item,
|
||||||
payloadSizeBytes = item.payloadSizeBytes,
|
payloadSizeBytes = item.payloadSizeBytes,
|
||||||
id = UUID.randomUUID(),
|
id = UUID.randomUUID(),
|
||||||
channel = channel
|
channel = channel,
|
||||||
|
responseBody = if (accessLogConfig.includeMessages) (reply.result?.let { String(it) } ?: "") else null,
|
||||||
|
errorMessage = if (accessLogConfig.includeMessages) {
|
||||||
|
reply.error?.let {
|
||||||
|
it.upstreamError?.message ?: it.message
|
||||||
|
} ?: ""
|
||||||
|
} else null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,7 +393,8 @@ class EventsBuilder {
|
|||||||
nativeSubscribe = item!!,
|
nativeSubscribe = item!!,
|
||||||
payloadSizeBytes = msg.payload?.size()?.toLong() ?: 0L,
|
payloadSizeBytes = msg.payload?.size()?.toLong() ?: 0L,
|
||||||
id = UUID.randomUUID(),
|
id = UUID.randomUUID(),
|
||||||
channel = Events.Channel.GRPC
|
channel = Events.Channel.GRPC,
|
||||||
|
responseBody = if (accessLogConfig.includeMessages) (msg.payload?.toStringUtf8() ?: "") else null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class AccessLogWriterSpec extends Specification {
|
|||||||
File accessLog = new File(dir, "accesslog.jsonl")
|
File accessLog = new File(dir, "accesslog.jsonl")
|
||||||
println("Write access log to $accessLog.absolutePath")
|
println("Write access log to $accessLog.absolutePath")
|
||||||
MainConfig config = new MainConfig()
|
MainConfig config = new MainConfig()
|
||||||
config.accessLogConfig = new AccessLogConfig(true).tap {
|
config.accessLogConfig = new AccessLogConfig(true, false).tap {
|
||||||
it.filename = accessLog.absolutePath
|
it.filename = accessLog.absolutePath
|
||||||
}
|
}
|
||||||
AccessLogWriter logWriter = new AccessLogWriter(config)
|
AccessLogWriter logWriter = new AccessLogWriter(config)
|
||||||
|
|||||||
Reference in New Issue
Block a user