Mario Kutlev
Mario Kutlev

Reputation: 5096

What is the best way to define log TAG constant in Kotlin?

I'm creating my first Kotlin classes in my Android application. Usually for logging purposes I have a constant with name TAG. What I would do in Java is:

private static final String TAG = MyClass.class.getSimpleName();

I know that in Kotlin classes I can create TAG using this way:

private val TAG = MyClass::class.java.simpleName

This is OK for projects that use Java and Kotlin but what if I start a new project that is only in Kotlin? How can I define there TAG constant? Is there more Kotlin way where I don't have this strange construction class.java.simpleName?

Upvotes: 108

Views: 49110

Answers (20)

Fredy Mederos
Fredy Mederos

Reputation: 2636

This extension allows us to use TAG in any class

val Any.TAG: String
    get() {
        return if (!javaClass.isAnonymousClass) {
            val name = javaClass.simpleName
            if (name.length <= 23) name else name.substring(0, 23)// first 23 chars
        } else {
            val name = javaClass.name
            if (name.length <= 23) name else name.substring(name.length - 23, name.length)// last 23 chars
        }
    }

//usage
Log.e(TAG,"some value")

It it also validated to work as an Android valid Log tag.

Upvotes: 87

Homayoon Ahmadi
Homayoon Ahmadi

Reputation: 2833

I was looking for a clear and efficient way to log content, but I ended up with using Timber. It's very useful and easy to use:

https://github.com/JakeWharton/timber

dependencies {
  implementation 'com.jakewharton.timber:timber:5.0.1'
}

usage:

Timber.d("Simple Log")

Upvotes: 0

Freeze
Freeze

Reputation: 41

Sometimes I prefer to use live template. In android studio you can duplicate "logd" template, rename it and change "TAG" to "javaClass.simpleName".

Now if you type "logc"(or whatever template name you have) it will generate the whole line including method name without polluting code with extensions, fields or static functions.

settings

how to use

Upvotes: 1

user2808624
user2808624

Reputation: 2530

I have defined an interface, which defines TAG as a property with default getter implementation. The interface can be either "implemented" by a class but also by its companion object:

//Prefix allows easier filtering in LogCat
private const val PREFIX = "somePrefix.";

interface HasLogTag {
    val TAG: String
        get() {
            val name = javaClass.canonicalName?.removeSuffix(".Companion")?.substringAfterLast(".")
            return "$PREFIX${name}"
        }
}

The interface is used as follows:

import yourPackage.HasLogTag     
....
class MyClass : HasLogTag {
    ...
    //Alternatively: Let the companion object "implement" the interface
    companion object : HasLogTag {
    ...
    Log.e(TAG, "Some Info)
}

As the getter is called on every usage, there is however no advantage in defining the TAG for the companion object.

Note: In a previous version, I used reflection to find out, whether the class itself or the companion object defines the interface.

This however seemed to slow down heavily the startup of my app.

Upvotes: 0

Rajesh.k
Rajesh.k

Reputation: 2437

Here is my extension function in kotlin, just add it in your extensions file.

val Any.TAG: String
get() {
    return if (!javaClass.isAnonymousClass) {
        val name = javaClass.simpleName
        if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name else
            name.substring(0, 23)// first 23 chars
    } else {
        val name = javaClass.name
        if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
            name else name.substring(name.length - 23, name.length)// last 23 chars
    }
}

Then you can use TAG in any class like below:

Log.d(TAG, "country list")

Upvotes: 0

Alireza Ghanbarinia
Alireza Ghanbarinia

Reputation: 944

You can try this:

companion object {
    val TAG = ClearCacheTask::class.java.simpleName as String
}

Upvotes: 1

Pavel Berdnikov
Pavel Berdnikov

Reputation: 303

The best way to log (imho) is using Timber: https://github.com/JakeWharton/timber

But if you don't want to use library then

TAG can be defined as an inlined extension property (e.g. in Extensions.kt):

inline val <reified T> T.TAG: String
    get() = T::class.java.simpleName

Some more extensions to not to write TAG all the time in Log.d(TAG, ""):

inline fun <reified T> T.logv(message: String) = Log.v(TAG, message)
inline fun <reified T> T.logi(message: String) = Log.i(TAG, message)
inline fun <reified T> T.logw(message: String) = Log.w(TAG, message)
inline fun <reified T> T.logd(message: String) = Log.d(TAG, message)
inline fun <reified T> T.loge(message: String) = Log.e(TAG, message)

And then you can use them in any class:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    logd("Activity created")
}

Upvotes: 15

Rahul Tiwari
Rahul Tiwari

Reputation: 6978

I Like TAG to be an extension function as suggested by Fredy Mederos.

extending his answer to support anonymous classes :

 /**
 * extension function to provide TAG value
 */
val Any.TAG: String
    get() {
        return if (!javaClass.isAnonymousClass) {
            val name = javaClass.simpleName
            if (name.length <= 23) name else name.substring(0, 23)// first 23 chars
        } else {
            val name = javaClass.name
            if (name.length <= 23) name else name.substring(name.length - 23, name.length)// last 23 chars
        }
    }

Upvotes: 20

TomH
TomH

Reputation: 2719

In Kotlin you could create an extension, and call tag as a method call instead. This would mean you'd never have to define it inside each class, we can construct it dynamically each time we call the method:

inline fun <reified T> T.TAG(): String = T::class.java.simpleName

Upvotes: 15

Max
Max

Reputation: 2136

In Android Studio, the usual way to rename something is to right-click the name, select Refactor->Rename. So, I think it's fine to do something like this,

class MyClass {
    companion object {
        private const LOG_TAG = "MyClass"
    }
}

because if you rename the class MyClass like I described, then the IDE will suggest renaming your LOG_TAG String as well.

Ultimately there are pros and cons of using this method vs. other methods. Because LOG_TAG is a String, there's no need to import the kotlin-reflect.jar, as you would if you set LOG_TAG equal to MyClass::class.simpleName. Also because the variable is declared as a compile-time constant with the const keyword, the generated bytecode is smaller since it doesn't need to generate more hidden getters, as described in this article.

Upvotes: 0

Paolo
Paolo

Reputation: 595

I created some Log extension functions to avoid declaring the log tag as we did in Java (maybe less performant, but given that we are talking about logging this should be acceptable IMO). The approach uses reified type parameters and other Kotlin goodies to retrieve the class simple name. Here is a basic example:

inline fun <reified T> T.logi(message: String) =
   Log.i(T::class.java.simpleName, message)

You can find a more elaborated gist here

Upvotes: 4

nickgzzjr
nickgzzjr

Reputation: 419

Simply doing the following worked for me.

private val TAG = this::class.java.simpleName

Upvotes: 11

Yaroslav Mytkalyk
Yaroslav Mytkalyk

Reputation: 17115

Commonly suggested approach of using the companion object generates extra static final instance of a companion class and thus is bad performance and memory-wise.

The best way (IMHO)

Define a log tag as a top-level constant, thus only extra class is generated (MyClassKt), but compared to companion object there will be no static final instance of it (and no instance whatsoever):

private const val TAG = "MyLogTag"

class MyClass {

    fun logMe() {
        Log.w(TAG, "Message")
    }
}

Another option

Use a normal val. Though this looks unusual to see a log tag not as an all-uppercase constant, this will not generate any classes and has least overhead.

class MyClass {

    private val tag = "myLogTag"

    fun logMe() {
        Log.w(tag, "Message")
    }
}

Upvotes: 23

Kasim Rangwala
Kasim Rangwala

Reputation: 1835

Updated answer with Kotlin 1.2.20

class MyClass {
    companion object {

        @JvmField
        public val FOO = 1
    }
}

uses

MyClass.FOO

Upvotes: 2

Andrew Wang
Andrew Wang

Reputation: 81

You can define your TAG by @JvmField as below:

companion object {
    @JvmField val TAG: String = MyClass::class.java.simpleName
}

For more details, you can read this article : Kotlin's hidden costs

Upvotes: 6

Fredy Mederos
Fredy Mederos

Reputation: 2636

AnkoLogger uses an interface to define the log tag.

interface AnkoLogger {
            /**
             * The logger tag used in extension functions for the [AnkoLogger].
             * Note that the tag length should not be more than 23 symbols.
             */
            val loggerTag: String
                get() = getTag(javaClass)
        }
private fun getTag(clazz: Class<*>): String {
        val tag = clazz.simpleName
        return if (tag.length <= 23) {
            tag
        } else {
            tag.substring(0, 23)
        }
    }
inline fun AnkoLogger.info(message: () -> Any?) {
    val tag = loggerTag
    if (Log.isLoggable(tag, Log.INFO)) {
        Log.i(tag, message()?.toString() ?: "null")
    }
}

You can use it like this:

class MyClass : AnkoLogger {
    fun someFun(){
       info("logging info")
    }
}

Maybe AnkoLogger can give you some ideas to implement a custom logging tool.

Upvotes: 1

jpihl
jpihl

Reputation: 8071

I found a way which is more "copy-paste"-able, since it doesn't require you to type the name of your class:

package com.stackoverflow.mypackage

class MyClass
{
    companion object {
        val TAG = this::class.toString().split(".").last().dropLast(10)
    }
}

It's not the most elegant solution but it works.

this::class.toString().split(".").last() will give you "com.stackoverflow.mypackage.MyClass$Companion" so you need the dropLast(10) to remove $Companion.

Alternatively you can do this:

package com.stackoverflow.mypackage

class MyClass
{
    val TAG = this::class.simpleName
}

But then the TAG member variable is no longer "static" and doesn't follow the recommended naming conventions.

Upvotes: 1

user4696837
user4696837

Reputation:

Declare of TAG variable with val

class YourClass {
   companion object {
      //if use java and kotlin both in project
      //private val TAG = MyClass::class.java.simpleName

      //if use only kotlin in project
      private val TAG = YourClass::class.simpleName
   }
}

Use the variable like

Log.d(YourClass.TAG, "Your message");
//or 
Log.e(TAG, "Your message");

Upvotes: 1

Gabriele Mariotti
Gabriele Mariotti

Reputation: 364095

In general constants are all caps (ex. FOO) and located in the companion object:

class MyClass {
    companion object {
        public const val FOO = 1

    }
}

and to define the TAG field you can use:

private val TAG = MyClass::class.qualifiedName

Upvotes: 65

Matias Elorriaga
Matias Elorriaga

Reputation: 9150

I'm creating the constant as a companion object:

companion object {
    val TAG = "SOME_TAG_VALUE"
}

Then, I can use it like this:

MyClass.TAG

Upvotes: 2

Related Questions