9

I want to do this for Java or Kotlin: given the code below

try { ...
} catch (@AutoLog e: Exception) { //position1
}

add a logging statement at position1 automatically during build time. I can add code with AspectJ for catch block(for both Java and Kotlin), but it's for all catch blocks, I can't check whether @AutoLog annotation is present and add code only when it is. So I think I have to turn to APT(Annotation processing tool) for Java (or KAPT for Kotlin)?

Btw, I found a KAPT code generation example here: https://github.com/JetBrains/kotlin-examples/tree/master/gradle/kotlin-code-generation , but it generates code to separate file, while what I want to do is modify the original file/class and add a statement in catch block.

3
  • 3
    The apt is not made for modifying existing code. You can try notatube.blogspot.com/2010/11/… but I don't think it works in Kotlin. The best I can think of is generating a new class which uses the annotation and logs the annotated variable. Commented Oct 9, 2018 at 10:27
  • 1
    @Bal0r Thanks for the excellent link, I found it very helpful. Will look into annotation processor + AST(abstract syntax tree) manipulation approach. The @ cleanup in Lombok uses annotations on local variable and modifies existing code. So that's a feasibility proof. What I want to do must also be feasible with same approach. Commented Oct 10, 2018 at 8:22
  • I think @Bal0r's comment is the correct answer here. You need to do what Lombok does with the AST modification by APT. Bal0r: you should change it from a comment to an answer. Commented Oct 30, 2018 at 10:28

1 Answer 1

1

Instead of using annotations, you can use some of Kotlin's features to create your own functions that operate similarly to the standard try { ... } catch { ... }, but also log exceptions. Here's a quick example:

sealed class Result<S>

class Success<S>(val value: S) : Result<S>()
class Error<S, E : Throwable>(val error: E) : Result<S>()

fun <S> loggingTry(block: () -> S): Result<S> {
    return try {
        Success(block())
    } catch (e: Throwable) {
        Error(e)
    }
}

inline infix fun <reified E : Throwable, R> Result<out R>.catch(handler: (E) -> R): R {
    return when (this) {
        is Success -> value
        is Error<*, *> -> if (error is E) {
            println("Error $error is being handled") // replace this with your own logging
            handler(error)
        } else throw error
    }
}

fun main(args: Array<String>) {
    val x = loggingTry {
        1 / 0
    } catch { e: ArithmeticException ->
        3
    }

    println("Result: $x")

    loggingTry {
        1 / 0
    } catch { e: NullPointerException ->
        println("This won't be called, the exception will just be re-thrown")
    }
}

This code generates the following output:

Error java.lang.ArithmeticException: / by zero is being handled
Division result was 3
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at MainKt$main$1.invoke(Main.kt:34)
    at MainKt$main$1.invoke(Main.kt)
    at MainKt.loggingTry(Main.kt:8)
    at MainKt.main(Main.kt:33)

This isn't a perfect solution, since it's more limited (e.g. no multi-catch) and has a slightly different syntax, but the end result is comparable.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.