refactor(log): support advice invoke or invoke with no result via different methods to avoid unnecessary nullable check

This commit is contained in:
2026-04-14 10:08:31 +08:00
parent cb4380eb1e
commit 28d0a43ef3
2 changed files with 72 additions and 31 deletions

View File

@@ -24,16 +24,12 @@ sealed class AbstractAgentModule {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
LogAdviceProvider.createAdvice( LogAdviceProvider.createAdvice(
moduleName, moduleName,
resolveGenericType(0) as Class<T>, resolveGenericType(0) as Class<T>
Void::class.java ) { context -> doExecute(context) }
) { context ->
doExecute(context)
null
}
} }
fun execute(context: T) { fun execute(context: T) {
advice.invoke(context) advice.invokeWithoutResult(context)
} }
protected abstract fun doExecute(context: T) protected abstract fun doExecute(context: T)
@@ -53,11 +49,11 @@ sealed class AbstractAgentModule {
} }
} }
fun execute(input: I): O? { fun execute(input: I): O {
return advice.invoke(input).getOrThrow() return advice.invoke(input).getOrThrow()
} }
protected abstract fun doExecute(input: I): O? protected abstract fun doExecute(input: I): O
} }
abstract class Standalone : AbstractAgentModule() abstract class Standalone : AbstractAgentModule()

View File

@@ -30,15 +30,29 @@ object LogAdviceProvider : Configurable, ConfigRegistration<AdviceLoggingConfig>
inputType: Class<I>, inputType: Class<I>,
outputType: Class<O>, outputType: Class<O>,
meta: Map<String, Any> = emptyMap(), meta: Map<String, Any> = emptyMap(),
invoker: (I) -> O? invoker: (I) -> O
): LogAdvice<I, O> { ): LogAdvice<I, O> {
return LogAdvice( return LogAdvice(
adviceTarget = adviceTarget, adviceTarget,
invoker = invoker, AdviceInvoker.WithResult(invoker),
AdviceMeta(adviceTarget, inputType, outputType, meta) AdviceMeta(adviceTarget, inputType, outputType, meta)
).apply { _adviceRegistry.add(this) } ).apply { _adviceRegistry.add(this) }
} }
@JvmOverloads
fun <I> createAdvice(
adviceTarget: String,
inputType: Class<I>,
meta: Map<String, Any> = emptyMap(),
invoker: (I) -> Unit
): LogAdvice<I, Void> {
return LogAdvice(
adviceTarget,
AdviceInvoker.WithoutResult(invoker),
AdviceMeta(adviceTarget, inputType, Void::class.java, meta)
).apply { _adviceRegistry.add(this) }
}
internal fun record(result: AdviceResult) { internal fun record(result: AdviceResult) {
val path = logPath.resolve(result.adviceTarget).normalize().toAbsolutePath() val path = logPath.resolve(result.adviceTarget).normalize().toAbsolutePath()
val traceEvent = TraceEvent(path, result.toJSON(), result.finishTime.toInstant().toEpochMilli()) val traceEvent = TraceEvent(path, result.toJSON(), result.finishTime.toInstant().toEpochMilli())
@@ -61,7 +75,7 @@ object LogAdviceProvider : Configurable, ConfigRegistration<AdviceLoggingConfig>
class LogAdvice<I, O> internal constructor( class LogAdvice<I, O> internal constructor(
val adviceTarget: String, val adviceTarget: String,
private val invoker: (I) -> O?, private val invoker: AdviceInvoker<I, O>,
private val adviceMeta: AdviceMeta private val adviceMeta: AdviceMeta
) { ) {
@@ -69,11 +83,13 @@ class LogAdvice<I, O> internal constructor(
private val log = LoggerFactory.getLogger(LogAdvice::class.java) private val log = LoggerFactory.getLogger(LogAdvice::class.java)
} }
fun invoke(input: I): Result<O?> { fun invoke(input: I): Result<O> {
val currentInvoker = invoker as? AdviceInvoker.WithResult<I, O>
?: error("LogAdvice[$adviceTarget] does not provide a return value")
val startAt = ZonedDateTime.now() val startAt = ZonedDateTime.now()
return try { return try {
logEnter(input) logEnter(input)
val output = invoker(input) val output = currentInvoker.invoker(input)
logOutput(output) logOutput(output)
createResult(input, output, startAt) createResult(input, output, startAt)
Result.success(output) Result.success(output)
@@ -84,6 +100,22 @@ class LogAdvice<I, O> internal constructor(
} }
} }
fun invokeWithoutResult(input: I) {
val currentInvoker = invoker as? AdviceInvoker.WithoutResult<I>
?: error("LogAdvice[$adviceTarget] expects a return value")
val startAt = ZonedDateTime.now()
try {
logEnter(input)
currentInvoker.invoker(input)
logNoOutput()
createResultWithoutOutput(input, startAt)
} catch (e: Exception) {
logException(e)
createUnexpectedResult(input, e, startAt)
throw e
}
}
private fun logException(e: Exception) { private fun logException(e: Exception) {
when (LogAdviceProvider.logLevel) { when (LogAdviceProvider.logLevel) {
AdviceLoggingConfig.LogLevel.NONE -> return AdviceLoggingConfig.LogLevel.NONE -> return
@@ -92,7 +124,7 @@ class LogAdvice<I, O> internal constructor(
} }
} }
private fun logOutput(output: O?) { private fun logOutput(output: O) {
when (LogAdviceProvider.logLevel) { when (LogAdviceProvider.logLevel) {
AdviceLoggingConfig.LogLevel.NONE -> return AdviceLoggingConfig.LogLevel.NONE -> return
AdviceLoggingConfig.LogLevel.ABSTRACT -> log.info("${adviceMeta.adviceTarget} ended.") AdviceLoggingConfig.LogLevel.ABSTRACT -> log.info("${adviceMeta.adviceTarget} ended.")
@@ -100,12 +132,20 @@ class LogAdvice<I, O> internal constructor(
try { try {
log.info("${adviceMeta.adviceTarget} ended with output: ${JSONObject.toJSONString(output)}") log.info("${adviceMeta.adviceTarget} ended with output: ${JSONObject.toJSONString(output)}")
} catch (_: Exception) { } catch (_: Exception) {
log.info("${adviceMeta.adviceTarget} ended with output: ${output ?: "null"}, which cannot be printed as json string.") log.info("${adviceMeta.adviceTarget} ended with output: ${output.toString()}, which cannot be printed as json string.")
} }
} }
} }
} }
private fun logNoOutput() {
when (LogAdviceProvider.logLevel) {
AdviceLoggingConfig.LogLevel.NONE -> return
AdviceLoggingConfig.LogLevel.ABSTRACT -> log.info("${adviceMeta.adviceTarget} ended.")
AdviceLoggingConfig.LogLevel.DETAIL -> log.info("${adviceMeta.adviceTarget} ended without output.")
}
}
private fun logEnter(input: I) { private fun logEnter(input: I) {
when (LogAdviceProvider.logLevel) { when (LogAdviceProvider.logLevel) {
AdviceLoggingConfig.LogLevel.NONE -> return AdviceLoggingConfig.LogLevel.NONE -> return
@@ -120,16 +160,28 @@ class LogAdvice<I, O> internal constructor(
} }
} }
private fun createResult(input: I, output: O?, startAt: ZonedDateTime) { private fun createResult(input: I, output: O, startAt: ZonedDateTime) {
val inputSerialized = serializeRequired(input) val inputSerialized = serializeRequired(input)
val outputSerialized = serializeNullable(output)
LogAdviceProvider.record( LogAdviceProvider.record(
AdviceResult.Normal( AdviceResult.Normal(
adviceTarget, adviceTarget,
inputSerialized, inputSerialized,
startAt, startAt,
adviceMeta, adviceMeta,
outputSerialized serializeRequired(output)
)
)
}
private fun createResultWithoutOutput(input: I, startAt: ZonedDateTime) {
val inputSerialized = serializeRequired(input)
LogAdviceProvider.record(
AdviceResult.Normal(
adviceTarget,
inputSerialized,
startAt,
adviceMeta,
null
) )
) )
} }
@@ -155,18 +207,11 @@ class LogAdvice<I, O> internal constructor(
value?.toString() ?: "null" value?.toString() ?: "null"
} }
} }
}
private fun serializeNullable(value: Any?): String? { internal sealed interface AdviceInvoker<I, O> {
return if (value == null) { data class WithResult<I, O>(val invoker: (I) -> O) : AdviceInvoker<I, O>
null data class WithoutResult<I>(val invoker: (I) -> Unit) : AdviceInvoker<I, Void>
} else {
try {
JSONObject.toJSONString(value)
} catch (_: JSONException) {
value.toString()
}
}
}
} }
data class AdviceMeta( data class AdviceMeta(