2020년 3월 29일 일요일

KOTLIN 에서 AspectJ



https://github.com/Ibotta/gradle-aspectj-pipeline-plugin/blob/develop/sample-kotlin/src/main/kotlin/com/ibotta/gradle/aop/kotlin/KotlinAspect.kt
여기를 참고해서 설정하고.

@Aspect  
class KotlinAspect {  
    @Before("execution(* com.sugoigroup.mytvapplication*..*(..))")  
    fun before(joinPoint: JoinPoint) {  
        val methodSignature = joinPoint!!.signature as MethodSignature  
        val methodName = methodSignature.name  
  val startTime = System.currentTimeMillis();  
        val result = (joinPoint as ProceedingJoinPoint).proceed()  
        val endTime = System.currentTimeMillis() - startTime  
        Log.e("LoggingVM", "$methodName ---> $endTime")  
    }  
  
    @After("execution(* demonstrateKotlinAOP(..))")  
    fun after(joinPoint: JoinPoint) {  
  
    }  
}
또는




import android.graphics.Bitmap
import android.os.Debug
import android.os.Process
import android.util.Log
import android.view.View
import androidx.annotation.NonNull
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.After
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
import org.aspectj.lang.annotation.Pointcut
import org.aspectj.lang.reflect.MethodSignature
import java.util.Random
import java.util.concurrent.TimeUnit

@Aspect
class KotlinAspect {

private var lastClassMethod = "Start"
private val logType1 = 1
private var argAll: String? = null
@Pointcut(POINTCUT_BEFORE_METHOD) fun pointcutDebugTraceBefore() {}
@Pointcut(POINTCUT_AROUND_METHOD) fun pointcutDebugTraceAround() {}

@Before("pointcutDebugTraceBefore()")
@Throws(Throwable::class)
fun weaveDebugTraceBefore(
joinPoint: JoinPoint
) {
val methodSignature = joinPoint.signature as MethodSignature
val currentClassName = utils.getOnlyClassName(methodSignature.declaringType.simpleName)
val currentMethodName = utils.getOnlyClassName(methodSignature.name)
//not use
argAll = utils.getArgs(joinPoint, methodSignature)
val tid = Process.myTid()
val dummyDepthText = utils.getdepthTexts(tid)
lastClassMethod = utils.getLastClassMethod(lastClassMethod, dummyDepthText, logType1)
if (utils.getdepthCounts(tid) < 1) {
lastClassMethod = "Start"
}
LogPrinter.logBefore(currentClassName, currentMethodName, lastClassMethod, tid)
LogPrinter.logBeforeArg(lastClassMethod, tid, argAll)
printTo(
type2PrintTid(tid) + ASPECT_LOG_SEQUENCE_TYPE,
utils.repeat(
verticalLine,
utils.getdepthCounts(tid) + 1
) + "▼" + "(" + joinPoint.signature.declaringType + ") "
)
printTo(
type2PrintTid(tid) + ASPECT_LOG_SEQUENCE_TYPE,
utils.repeat(verticalLine, utils.getdepthCounts(tid) + 1) + "┏ " + currentMethodName
)
dummyDepthText.add(currentClassName)
}


@Around("pointcutDebugTraceAround()")
@Throws(Throwable::class)
fun weaveDebugTraceAround(
joinPoint: ProceedingJoinPoint
):Any?{
val methodSignature = joinPoint.signature as MethodSignature
val currentClassName = utils.getOnlyClassName(methodSignature.declaringType.simpleName)
val currentMethodName = utils.getOnlyClassName(methodSignature.name)
val tid = Process.myTid()
val dummyDepthText = utils.getdepthTexts(tid)
depthCounts[tid] = utils.getdepthCounts(tid) + 1
LogPrinter.logAroundBefore(currentClassName, tid)
if (argAll!!.length > 0) {
printTo(
type2PrintTid(tid) + ASPECT_LOG_SEQUENCE_TYPE,
utils.repeat(verticalLine, utils.getdepthCounts(tid) + 1) + argAll
)
}
val stopWatch = StopWatch()
stopWatch.start()
val result = joinPoint.proceed()
stopWatch.stop()
LogPrinter.logAroundAfter(
currentClassName,
currentMethodName,
dummyDepthText,
stopWatch.totalTimeMillis,
tid,
result
)
printTo(
type2PrintTid(tid) + ASPECT_LOG_SEQUENCE_TYPE,
utils.repeat(verticalLine, utils.getdepthCounts(tid) + 1) + "result " + result
)
printTo(
type2PrintTid(tid) + ASPECT_LOG_SEQUENCE_TYPE,
utils.repeat(
verticalLine,
utils.getdepthCounts(tid)
) +
"┗ " + currentMethodName +
" : " + stopWatch.totalTimeMillis + "ms"
)

//after process
depthCounts[tid] = utils.getdepthCounts(tid) - 1
if (dummyDepthText.size > 0) {
dummyDepthText.removeAt(dummyDepthText.size - 1)
}
depthTexts[tid] = dummyDepthText

return result
}

@After("execution(* demonstrateKotlinAOP(..))")
fun after(joinPoint: JoinPoint) {

}

/**
* Class representing a StopWatch for measuring time.
*/
inner class StopWatch() {
private var startTime: Long = 0
private var endTime: Long = 0
private var elapsedTime: Long = 0
private fun reset() {
startTime = 0
endTime = 0
elapsedTime = 0
}

fun start() {
reset()
startTime = System.nanoTime()
}

fun stop() {
if (startTime != 0L) {
endTime = System.nanoTime()
elapsedTime = endTime - startTime
} else {
reset()
}
}

val totalTimeMillis: Long
get() = if ((elapsedTime != 0L)) TimeUnit.NANOSECONDS.toMillis(endTime - startTime) else 0
}

object LogPrinter {
fun logBefore(
className: String,
methodName: String,
lastClassMethod: String,
tid: Int
) {

//hidden for UML
printTo(
ASPECT_LOG_PLANTUML_TYPE,
type2PrintTidForUml(tid) + lastClassMethod + umlWithColorBlock(tid) + "->" + type2PrintTidForUml(
tid
) + className + " : " + methodName + "#" + tid
)
}

fun logBeforeArg(className: String, tid: Int, args: String?) {
// note right of Alice
// Alice <u:blue></u>側に
// <back:cadetblue>表示</back>するノート
// end note
if (args!!.isEmpty()) return
printTo(
ASPECT_LOG_PLANTUML_TYPE,
"note right of " + type2PrintTidForUml(tid) + className + "\n " + args + "\n end note"
)
}

fun logAfterResult(className: String, tid: Int, result: Any?) {
// note right of Alice
// Alice <u:blue></u>側に
// <back:cadetblue>表示</back>するノート
// end note
if (result == null) return
printTo(
ASPECT_LOG_PLANTUML_TYPE,
"note right of " + type2PrintTidForUml(tid) + className + "\n result :" + result + "\n end note"
)
}

private fun getProcessColor(tid: Int): Int {
if (!processColor.containsKey(tid)) {
val r = Random()
processColor[tid] = r.nextInt(0xffffff + 1)
}
return (processColor[tid])!!
}

private fun umlWithColor(tid: Int): String {
return String.format("#%06x", getProcessColor(tid))
}

private fun umlWithColorBlock(tid: Int): String {
return "[" + umlWithColor(tid) + "]"
}

fun logAroundBefore(className: String, tid: Int) {
//hidden for UML
printTo(
ASPECT_LOG_PLANTUML_TYPE,
ACTIVATE + type2PrintTidForUml(tid) + className + " " + umlWithColor(tid)
)
}

fun logAroundAfter(
className: String,
methodName: String,
depthText: List<String>,
estimate: Long,
tid: Int,
result: Any?
) {
var returnClassName = START
if (depthText.size > 1) {
returnClassName = depthText[depthText.size - 2]
}
//hidden for UML
//new but not goof relation 420,423 printTo(ASPECT_LOG_PLANTUML_TYPE, "return " +type2PrintTidForUml(tid) + methodName + " (" + estimate + "ms)"); //### weaveDebugTraceBefore:
val nativeMax: Long = Debug.getNativeHeapSize() / 1024
val nativeAllocated: Long = Debug.getNativeHeapAllocatedSize() / 1024
val nativeFree: Long = Debug.getNativeHeapFreeSize() / 1024
printTo(
ASPECT_LOG_PLANTUML_TYPE,
(type2PrintTidForUml(tid) + className + umlWithColorBlock(tid)
+ "-->" + type2PrintTidForUml(tid) + returnClassName + " : " + methodName + "#" + tid + " (" + estimate + "ms," + nativeMax + "MB," + nativeAllocated + "MB," + nativeFree + "MB)")
) //### weaveDebugTraceBefore:
logAfterResult(className, tid, result)
//hidden for UML
printTo(
ASPECT_LOG_PLANTUML_TYPE,
DEACTIVATE + type2PrintTidForUml(tid) + className + " " + umlWithColor(tid)
)
}
}

object utils {
@Synchronized fun repeat(string: String?, times: Int): String {
var times = times
val out = StringBuilder()
while (times-- > 0) {
out.append(string)
}
return out.toString()
}

fun getOnlyClassName(classname: String): String {
if (classname.indexOf("$") < 1) {
return isEmptyName(classname)
}
val str = classname.split("\\\\$").dropLastWhile { it.isEmpty() }
.toTypedArray()
return isEmptyName(str[0])
}

fun getLastClassMethod(
lastClassMethod: String,
dummyDepthText: List<String>,
logType: Int
): String {
return if (dummyDepthText.size > 0) {
isEmptyName(dummyDepthText.get(dummyDepthText.size - logType))
} else lastClassMethod
}

fun getdepthTexts(tid: Int): MutableList<String> {
if (!depthTexts.containsKey(tid)) {
depthTexts[tid] = ArrayList()
}
return (depthTexts[tid])!!
}

fun getdepthCounts(tid: Int): Int {
if (!depthCounts.containsKey(tid)) {
depthCounts[tid] = 0
}
return (depthCounts[tid])!!
}

private fun isEmptyName(methodName: String): String {
return if (methodName.length < 2) {
"NULL"
} else methodName
}

fun getArgs(joinPoint: JoinPoint, methodSignature: MethodSignature): String {
val objArray = joinPoint.args
val sigParamNames = methodSignature.parameterNames
var i = 0
var argName = ""
val argAll = StringBuilder()
for (obj: Any? in objArray) {
if (obj == null) continue
argName = sigParamNames[i]
i++
argAll.append("$argName:[$obj] , ")
}
if (argAll.length > 1) {
argAll.insert(0, "args ")
}
return argAll.toString()
}
}


// companiton


companion object {
//Line형태의 시퀀스 로그
private val ASPECT_LOG_SEQUENCE_TYPE = "ALOGSEQ"

//PlantUML에 이용하기 위한 로그
private val ASPECT_LOG_PLANTUML_TYPE = "ALOGUML"

//버퍼를 모아서 디비에 출력하기용.
private var logBufferCount = 1

//Limit 만큼 카운트가 되면 디비에 저장.
private val logBufferStoreCountLimit = 3000

//버퍼를 모아서 디비에 출력하기용. 프로비저닝 등의 작업에서는 로그출력이 불가능하니까 사용
private var logBufferMessages = ""
private val depthTexts: HashMap<Int, MutableList<String>> = HashMap()
private val depthCounts: HashMap<Int, Int> = HashMap()
private val processColor: HashMap<Int, Int> = HashMap()
private val verticalLine = "│"
private val START = "Start"
private val ACTIVATE = "activate "
private val DEACTIVATE = "deactivate "

private const val POINTCUT_BEFORE_METHOD = ("execution(* com.sugoigroup.mytvapplication*..*(..))")
private const val POINTCUT_AROUND_METHOD = POINTCUT_BEFORE_METHOD
@NonNull fun type2PrintTid(tid: Int): String {
return "$tid: p-"
}

@NonNull fun type2PrintTidForUml(tid: Int): String {
return ""
}

/* @Around("(execution(* android.app.Activity.onResume(..)) || execution(* android.app.Activity.onCreate(..)) || execution(* show(..)) )")
public Object postonResume(ProceedingJoinPoint joinPoint) throws Throwable {
final Activity activity = (Activity) joinPoint.getTarget();
final View rootView = activity.getWindow().getDecorView().getRootView();


final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
storeScreenshot(takescreenshotOfRootView(rootView), Calendar.getInstance().getTimeInMillis() + "-" + activity.getLocalClassName() );
}
}, 500);
Object result = joinPoint.proceed();
return result;
}*/
fun takescreenshot(v: View): Bitmap {
v.setDrawingCacheEnabled(true)
v.buildDrawingCache(true)
val b: Bitmap = Bitmap.createBitmap(v.getDrawingCache(true))
v.setDrawingCacheEnabled(false)
return b
}

fun takescreenshotOfRootView(v: View): Bitmap {
return takescreenshot(v.getRootView())
}

fun printTo(category: String, message: String) {
Log.d(category, message)
if (category != ASPECT_LOG_PLANTUML_TYPE) return
Log.d(category, message)
//--to db
if (logBufferCount < logBufferStoreCountLimit) {
logBufferCount++
logBufferMessages += "$message\\n"
return
}
// try {
// if (MainApp.getInstance() == null) {
// return
// }
// } catch (e: Exception) {
// return
// }
// if (OptimalAgentApplication.TestLogController.logHelper != null) {
// OptimalAgentApplication.TestLogController.insert("$category // $message")
// }
logBufferCount = 1
logBufferMessages = ""
}
}
}

Related Posts:

  • KOTLIN 에서 AspectJ https://github.com/Ibotta/gradle-aspectj-pipeline-plugin/blob/develop/sample-kotlin/src/main/kotlin/com/ibotta/gradle/aop/kotlin/KotlinAspect.kt 여기를 참고해서 설정하고. @Aspect class KotlinAspect { @Before("execution(* com.… Read More
  • Kotlin Ktlint Kotlin Ktlint Ktlint Ktlint 에디터상에서 어시스턴스로 부터 lint 오류를 발견하기 위해선 ktlint 플러그인이 필요하다. https://plugins.jetbrains.com/plugin/15057-ktlint-unofficial- gradle에서 ktlint를 실행하기 위해서는 jlleitschuh-ktl… Read More
  • [Android] View Binding 으로 뷰객체 쉽게 얻어오기 (android studio 3.6 부터) Kotlin 에서 무슨 익스텐션(synthetic binding) 써서 findViewById 대신 간편하게 뷰객체를 사용하던적이 있었다.근데 이게 deprecated 되어버려서 쓸수없게되었다.대신 View Binding이라는게 있어서 함 써볼라한다.일단 build.gradle 에 viewBinding기능 쓴다고 선언해야한다.android { ... buildFeatures … Read More
  • [Android] Kotlin & Android 기초 강좌 -작업중-작성중임… Read More
  • [Android] Kotlin Multiplatform Mobile, 즉 Kotlin KMM사용해보기Android, IOS,웹 애시당초 같은 로직을 언어마다 따로 만든다는게 잘못된거 아닌가? 그냥 C++ 하나 정도면 사람이 프로그램하는게 전혀 문제가 없는데, 뭘또 계속 만드는지...여튼 이런 환경에서 Kotlin 이 어느정도 답을 제시하고 있다.Kotlin MPP 프로젝트는 IntelliJ에서 해도 되고, 익숙한 Android Studio Plugin (Kotlin Multiplatform Mobi… Read More

0 comments:

댓글 쓰기