O Kotlin não possui a mesma noção de campos estáticos usada em Java. Em Java, a maneira geralmente aceita de fazer log é:
public class Foo {
private static final Logger LOG = LoggerFactory.getLogger(Foo.class);
}
A questão é qual é a maneira idiomática de executar o log no Kotlin?
kotlin
kotlin-logging
mchlstckl
fonte
fonte
Any
(necessitando, portanto, de uma conversão)?this.javaClass
para cada um. Mas não estou recomendando isso como uma solução.Respostas:
Na maioria dos códigos Kotlin maduros, você encontrará um desses padrões abaixo. A abordagem usando Delegados de Propriedade tira proveito do poder do Kotlin para produzir o menor código.
Nota: o código aqui é para,
java.util.Logging
mas a mesma teoria se aplica a qualquer biblioteca de logEstático (comum, equivalente ao seu código Java na pergunta)
Se você não puder confiar no desempenho dessa pesquisa de hash dentro do sistema de log, poderá obter um comportamento semelhante ao seu código Java usando um objeto complementar que pode conter uma instância e parecer estático para você.
criando saída:
Mais sobre objetos complementares aqui: Objetos complementares ... Observe também que na amostra acima
MyClass::class.java
obtém a instância do tipoClass<MyClass>
para o criador de logs, enquantothis.javaClass
que a instância do tipoClass<MyClass.Companion>
.Por instância de uma classe (comum)
Porém, não há realmente nenhuma razão para evitar ligar e obter um criador de logs no nível da instância. A maneira idiomática de Java que você mencionou está desatualizada e baseada no medo de desempenho, enquanto o criador de logs por classe já está armazenado em cache por quase qualquer sistema de criação de log razoável no planeta. Basta criar um membro para armazenar o objeto logger.
criando saída:
Você pode testar o desempenho por variações por instância e por classe e verificar se há uma diferença realista para a maioria dos aplicativos.
Delegados de propriedades (comuns, mais elegantes)
Outra abordagem, sugerida por @Jire em outra resposta, é criar um delegado de propriedade, que você pode usar para executar a lógica de maneira uniforme em qualquer outra classe que desejar. Existe uma maneira mais simples de fazer isso, já que o Kotlin já fornece um
Lazy
delegado, podemos apenas envolvê-lo em uma função. Um truque aqui é que, se queremos saber o tipo de classe atualmente usando o delegado, fazemos dela uma função de extensão em qualquer classe:Esse código também garante que, se você o usar em um Objeto complementar, o nome do criador de logs será o mesmo que se você o utilizasse na própria classe. Agora você pode simplesmente:
por instância de classe ou se você quiser que seja mais estático com uma instância por classe:
E sua saída ao chamar
foo()
essas duas classes seria:Funções de extensão (incomum neste caso devido à "poluição" de qualquer espaço para nome)
O Kotlin possui alguns truques ocultos que permitem diminuir ainda mais esse código. Você pode criar funções de extensão nas classes e, portanto, fornecer funcionalidades adicionais. Uma sugestão nos comentários acima foi estender
Any
com uma função de logger. Isso pode gerar ruído sempre que alguém usa o preenchimento de código no IDE em qualquer classe. Mas há um benefício secreto em estenderAny
ou em alguma outra interface de marcador: você pode sugerir que está estendendo sua própria classe e, portanto, detectar a classe em que está. Hã? Para ser menos confuso, aqui está o código:Agora, dentro de uma classe (ou objeto complementar), posso simplesmente chamar essa extensão em minha própria classe:
Produção de saída:
Basicamente, o código é visto como uma chamada para extensão
Something.logger()
. O problema é que o seguinte também pode ser verdadeiro, criando "poluição" em outras classes:Funções de extensão na interface do marcador (não tenho certeza de quão comum, mas modelo comum para "características")
Para tornar o uso de extensões mais limpo e reduzir a "poluição", você pode usar uma interface de marcador para estender:
Ou até mesmo torne o método parte da interface com uma implementação padrão:
E use uma destas variações em sua classe:
Produção de saída:
Se você deseja forçar a criação de um campo uniforme para reter o criador de logs, ao usar essa interface, você pode facilmente exigir que o implementador tenha um campo como
LOG
:Agora o implementador da interface deve ficar assim:
Obviamente, uma classe base abstrata pode fazer o mesmo, tendo a opção da interface e de uma classe abstrata implementando essa interface, permitindo flexibilidade e uniformidade:
Juntando tudo (uma pequena biblioteca auxiliar)
Aqui está uma pequena biblioteca auxiliar para facilitar a utilização de qualquer uma das opções acima. É comum no Kotlin estender APIs para torná-las mais ao seu gosto. Nas funções de extensão ou de nível superior. Aqui está uma combinação para fornecer opções de como criar loggers e uma amostra mostrando todas as variações:
Escolha o que você deseja manter e aqui estão todas as opções em uso:
Todas as 13 instâncias dos criadores de logs criados nesta amostra produzirão o mesmo nome e a saída:
Nota: O
unwrapCompanionClass()
método garante que não geremos um criador de logs com o nome do objeto complementar, mas a classe envolvente. Esta é a maneira atualmente recomendada de encontrar a classe que contém o objeto complementar. Retirar " $ Companion " do nome usandoremoveSuffix()
não funciona, pois os objetos complementares podem receber nomes personalizados.fonte
ofClass.enclosingClass.kotlin.objectInstance?.javaClass
em vez deofClass.enclosingClass.kotlin.companionObject?.java
compile 'org.jetbrains.kotlin:kotlin-reflect:1.0.2'
public fun <R : Any> R.logger(): Lazy<Logger> { return lazy{Logger.getLogger(unwrapCompanionClass(this.javaClass).name)}}
) parece criar uma função de extensão que"".logger()
agora é uma coisa, isso deveria se comportar dessa maneira?Dê uma olhada na biblioteca de registro kotlin .
Permite registrar assim:
Ou assim:
Também escrevi uma postagem no blog comparando-a com
AnkoLogger
: Logon no Kotlin e Android: AnkoLogger vs kotlin-loggingDisclaimer: Eu sou o mantenedor dessa biblioteca.
Edit: o kotlin-logging agora tem suporte a multiplataforma: https://github.com/MicroUtils/kotlin-logging/wiki/Multiplatform-support
fonte
logger.info()
chamadas, como Jayson fez em sua resposta aceita.Como um bom exemplo de implementação de log, eu gostaria de mencionar o Anko, que usa uma interface especial
AnkoLogger
que uma classe que precisa de log deve implementar. Dentro da interface, há um código que gera uma marca de log para a classe. O log é feito por meio de funções de extensão que podem ser chamadas dentro da implementação de interace sem prefixos ou mesmo criação da instância do criador de logs.Eu não acho que isso seja idiomático , mas parece uma boa abordagem, pois requer código mínimo, apenas adicionando a interface a uma declaração de classe e você obtém log com tags diferentes para diferentes classes.
O código abaixo é basicamente o AnkoLogger , simplificado e reescrito para uso independente do Android.
Primeiro, há uma interface que se comporta como uma interface de marcador:
Ele permite que sua implementação use as funções de extensões para
MyLogger
dentro de seu código, apenas chamando-asthis
. E também contém marca de log.Em seguida, há um ponto de entrada geral para diferentes métodos de log:
Será chamado pelos métodos de log. Ele obtém uma tag da
MyLogger
implementação, verifica as configurações de log e depois chama um dos dois manipuladores, aquele comThrowable
argumento e o sem.Em seguida, você pode definir quantos métodos de log desejar, desta maneira:
Eles são definidos uma vez para registrar apenas uma mensagem e registrar
Throwable
também, isso é feito com othrowable
parâmetro opcional .As funções que são passadas
handler
ethrowableHandler
podem ser diferentes para diferentes métodos de log, por exemplo, elas podem gravar o log em um arquivo ou carregá-lo em algum lugar.isLoggingEnabled
eLoggingLevels
são omitidos por questões de concisão, mas usá-los fornece ainda mais flexibilidade.Permite o seguinte uso:
Há uma pequena desvantagem: um objeto de logger será necessário para efetuar logon nas funções no nível do pacote:
fonte
android.util.Log
para fazer o log. Qual foi a sua intenção? usar Anko? De criar algo semelhante ao usar o Anko como exemplo (é melhor colocar o código sugerido em linha e corrigi-lo para não Android, em vez de dizer "port isto para não Android, aqui está o link". Em vez disso, adicione código de exemplo (Anko)BEIJO: Para equipes Java migrando para o Kotlin
Se você não se importa de fornecer o nome da classe em cada instanciação do criador de logs (como java), você pode simplificar, definindo isso como uma função de nível superior em algum lugar do seu projeto:
Isso usa um parâmetro do tipo reificado Kotlin .
Agora, você pode usar isso da seguinte maneira:
Essa abordagem é super simples e próxima do equivalente a java, mas apenas adiciona um pouco de açúcar sintático.
Próxima etapa: extensões ou delegados
Pessoalmente, prefiro dar um passo adiante e usar a abordagem de extensões ou delegados. Isso está bem resumido na resposta da @ JaysonMinard, mas aqui está o TL; DR para a abordagem "Delegate" com a API log4j2 ( UPDATE : não é mais necessário escrever esse código manualmente, pois foi lançado como um módulo oficial do projeto log4j2, veja abaixo). Como o log4j2, diferentemente do slf4j, suporta o log com
Supplier
's, também adicionei um delegado para simplificar o uso desses métodos.API de log Kotlin do Log4j2
A maior parte da seção anterior foi diretamente adaptada para produzir o módulo da API Kotlin Logging , que agora é uma parte oficial do Log4j2 (isenção de responsabilidade: eu sou o autor principal). Você pode fazer o download diretamente do Apache ou via Maven Central .
O uso é basicamente como descrito acima, mas o módulo suporta o acesso ao criador de logs com base na interface, uma
logger
função de extensão ativadaAny
para uso ondethis
é definido e uma função de criador de logs nomeado para uso onde nãothis
é definido (como funções de nível superior).fonte
T.logger()
- veja a parte inferior do exemplo de código.Anko
Você pode usar a
Anko
biblioteca para fazer isso. Você teria código como abaixo:kotlin-log
A biblioteca kotlin-logging ( projeto Github - kotlin-logging ) permite escrever o código de registro como abaixo:
StaticLog
ou você também pode usar este pequeno escrito na biblioteca Kotlin chamado,
StaticLog
então seu código se parecerá com:A segunda solução pode ser melhor se você desejar definir um formato de saída para o método de log como:
ou use filtros, por exemplo:
timberkt
Se você já usou a
Timber
biblioteca de registros de Jake Wharton, verifiquetimberkt
.Exemplo de código:
Verifique também: Logon no Kotlin e Android: AnkoLogger vs kotlin-logging
Espero que ajude
fonte
Algo assim funcionaria para você?
fonte
LoggerDelegate
e, em seguida, está criando uma função de nível superior que está criando é mais fácil criar uma instância do delegado (não muito mais fácil, mas um pouco). E essa função deve ser alterada para serinline
. Em seguida, ele usa o delegado para fornecer um criador de logs sempre que desejado. Mas ele fornece um para o acompanhanteFoo.Companion
e não para a turma, porFoo
isso talvez não seja o pretendido.logger()
função deve serinline
se não houver lambdas presentes. IntelliJ sugere que inlining neste caso é desnecessário: i.imgur.com/YQH3NB1.pngLazy
vez disso, usei um wrapper . Com um truque para saber em que classe está.Não ouvi falar em nenhum idioma a esse respeito. Quanto mais simples, melhor, então eu usaria uma propriedade de nível superior
Essa prática serve bem em Python e, por mais diferente que Kotlin e Python possa parecer, acredito que sejam bastante semelhantes no "espírito" (falando de expressões idiomáticas).
fonte
val log = what?!?
... criando um logger por nome? Ignorando o fato de que a pergunta mostrava que ele estava querendo criar um logger para uma classe específicaLoggerFactory.getLogger(Foo.class);
Que tal uma função de extensão na Classe? Dessa forma, você acaba com:
Nota - Eu não testei isso, então pode não estar certo.
fonte
Primeiro, você pode adicionar funções de extensão para a criação do criador de logs.
Em seguida, você poderá criar um logger usando o código a seguir.
Segundo, você pode definir uma interface que forneça um criador de logs e sua implementação mixin.
Essa interface pode ser usada da seguinte maneira.
fonte
crie um objeto complementar e marque os campos apropriados com a anotação @JvmStatic
fonte
Já existem muitas ótimas respostas aqui, mas todas dizem respeito à adição de um criador de logs a uma classe, mas como você faria isso para fazer o log de funções de nível superior?
Essa abordagem é genérica e simples o suficiente para funcionar bem em ambas as classes, objetos complementares e funções de nível superior:
fonte
É para isso que servem os objetos complementares, em geral: substituir coisas estáticas.
fonte
JvmStatic
anotação. E no futuro pode haver mais de um permitido. Além disso, esta resposta não é muito útil sem mais informações ou uma amostra.Factory
e outraHelpers
Exemplo Slf4j, o mesmo para os outros. Isso funciona mesmo para criar criadores de logs no nível do pacote
Uso:
fonte
fonte
Isso ainda é WIP (quase concluído), então eu gostaria de compartilhá-lo: https://github.com/leandronunes85/log-format-enforcer#kotlin-soon-to-come-in-version-14
O principal objetivo desta biblioteca é aplicar um certo estilo de log em um projeto. Ao gerar o código Kotlin, estou tentando resolver alguns dos problemas mencionados nesta pergunta. Com relação à pergunta original, o que costumo fazer é simplesmente:
fonte
Você pode simplesmente criar sua própria "biblioteca" de utilitários. Você não precisa de uma grande biblioteca para esta tarefa, que tornará seu projeto mais pesado e complexo.
Por exemplo, você pode usar o Kotlin Reflection para obter o nome, o tipo e o valor de qualquer propriedade de classe.
Primeiro de tudo, verifique se a meta-dependência foi definida em seu build.gradle:
Depois, você pode simplesmente copiar e colar este código no seu projeto:
Exemplo de uso:
fonte