Kotlin nie ma takiego samego pojęcia o polach statycznych, jak używane w Javie. W Javie ogólnie przyjęty sposób rejestrowania to:
public class Foo {
private static final Logger LOG = LoggerFactory.getLogger(Foo.class);
}
Pytanie brzmi, jaki jest idiomatyczny sposób wykonywania logowania w Kotlinie?
kotlin
kotlin-logging
mchlstckl
źródło
źródło
Any
(a więc wymagającego rzucenia)?this.javaClass
dla każdego. Ale nie polecam tego jako rozwiązania.Odpowiedzi:
W większości dojrzałego kodu Kotlin poniżej znajdziesz jeden z tych wzorców. Podejście wykorzystujące delegatów właściwości wykorzystuje moc Kotlin do tworzenia najmniejszego kodu.
Uwaga: kod tutaj jest przeznaczony,
java.util.Logging
ale ta sama teoria dotyczy każdej biblioteki rejestrowaniaStatyczne (typowe, odpowiednik twojego kodu Java w pytaniu)
Jeśli nie możesz ufać wydajności tego wyszukiwania skrótu w systemie rejestrowania, możesz uzyskać podobne zachowanie do kodu Java, używając obiektu towarzyszącego, który może przechowywać instancję i sprawiać wrażenie statycznego.
tworzenie wyników:
Więcej o obiektach towarzyszących tutaj: Obiekty towarzyszące ... Zauważ również, że w powyższym przykładzie
MyClass::class.java
pobiera instancję typuClass<MyClass>
dla rejestrującego, podczas gdythis.javaClass
pobiera instancję typuClass<MyClass.Companion>
.Na wystąpienie klasy (wspólne)
Ale naprawdę nie ma powodu, aby unikać wywoływania i pobierania programu rejestrującego na poziomie instancji. Wspomniany przez ciebie idiomatyczny sposób w Javie jest przestarzały i opiera się na strachu przed wydajnością, podczas gdy rejestrator dla każdej klasy jest już buforowany przez prawie każdy rozsądny system logowania na świecie. Po prostu utwórz członka do przechowywania obiektu rejestrującego.
tworzenie wyników:
Możesz przetestować wydajność zarówno dla instancji, jak i odmian klas, i sprawdzić, czy istnieje realistyczna różnica w przypadku większości aplikacji.
Delegaci nieruchomości (zwykli, najbardziej eleganccy)
Innym podejściem, które sugeruje @Jire w innej odpowiedzi, jest utworzenie delegata właściwości, którego można następnie użyć do jednolitego wykonywania logiki w dowolnej innej klasie, którą chcesz. Jest na to prostszy sposób, ponieważ Kotlin już dostarcza
Lazy
delegata, możemy po prostu opakować go w funkcję. Jedna sztuczka polega na tym, że jeśli chcemy poznać typ klasy aktualnie używającej delegata, robimy z tego funkcję rozszerzającą dla dowolnej klasy:Ten kod zapewnia również, że jeśli użyjesz go w obiekcie towarzyszącym, nazwa programu rejestrującego będzie taka sama, jak w przypadku użycia jej w samej klasie. Teraz możesz po prostu:
dla instancji klasy lub jeśli chcesz, aby była bardziej statyczna z jedną instancją na klasę:
Wynik wywołania
foo()
obu tych klas byłby następujący:Funkcje rozszerzające (rzadkie w tym przypadku z powodu „zanieczyszczenia” dowolnej przestrzeni nazw)
Kotlin ma kilka ukrytych sztuczek, które pozwalają jeszcze zmniejszyć część tego kodu. Możesz tworzyć funkcje rozszerzające na klasach i tym samym nadawać im dodatkową funkcjonalność. Jedna sugestia w komentarzach powyżej dotyczyła rozszerzenia
Any
o funkcję rejestratora. Może to powodować szum za każdym razem, gdy ktoś używa uzupełniania kodu w swoim IDE w dowolnej klasie. Ale jest sekretna korzyść z rozszerzeniaAny
lub innego interfejsu znaczników: możesz zasugerować, że rozszerzasz swoją własną klasę, a tym samym wykryć klasę, w której się znajdujesz. Co? Aby być mniej zagmatwanym, oto kod:Teraz w klasie (lub obiekcie towarzyszącym) mogę po prostu wywołać to rozszerzenie w mojej własnej klasie:
Produkcja wyników:
Zasadniczo kod jest postrzegany jako wezwanie do rozszerzenia
Something.logger()
. Problem polega na tym, że poniższe mogą również być prawdziwe, powodując „zanieczyszczenie” innych klas:Funkcje rozszerzeń w interfejsie znacznika (nie wiem, jak często, ale wspólny model dla „cech”)
Aby ułatwić korzystanie z rozszerzeń i zmniejszyć „zanieczyszczenie”, możesz użyć interfejsu znaczników, aby rozszerzyć:
Lub nawet uczyń metodę częścią interfejsu z domyślną implementacją:
I użyj jednej z tych odmian w swojej klasie:
Produkcja wyników:
Jeśli chciałbyś wymusić utworzenie jednolitego pola do przechowywania loggera, to korzystając z tego interfejsu możesz łatwo wymagać od realizatora posiadania pola takiego jak
LOG
:Teraz implementator interfejsu musi wyglądać tak:
Oczywiście abstrakcyjna klasa bazowa może zrobić to samo, mając opcję zarówno interfejsu, jak i klasy abstrakcyjnej implementującej ten interfejs, co zapewnia elastyczność i jednolitość:
Kładąc wszystko razem (mała biblioteka pomocnicza)
Oto mała biblioteka pomocnicza, dzięki której każda z powyższych opcji jest łatwa w użyciu. Kotlin często rozszerza API, aby bardziej odpowiadały Twoim upodobaniom. Albo w rozszerzeniu, albo w funkcjach najwyższego poziomu. Oto zestawienie opcji tworzenia rejestratorów oraz próbka pokazująca wszystkie odmiany:
Wybierz jedną z tych, które chcesz zachować, a oto wszystkie używane opcje:
Wszystkie 13 wystąpień rejestratorów utworzonych w tym przykładzie będzie generować tę samą nazwę programu rejestrującego i dane wyjściowe:
Uwaga: Do
unwrapCompanionClass()
metoda gwarantuje, że nie generują rejestratora po nazwie obiektu towarzyszącego ale raczej klasy okalającego. Jest to obecnie zalecany sposób znajdowania klasy zawierającej obiekt towarzyszący. Usunięcie „ $ Companion ” z nazwy za pomocąremoveSuffix()
nie działa, ponieważ obiektom towarzyszącym można nadawać niestandardowe nazwy.źródło
ofClass.enclosingClass.kotlin.objectInstance?.javaClass
zamiastofClass.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)}}
) tworzy funkcję rozszerzającą, która"".logger()
jest teraz rzeczą, czy to powinno zachowywać się w ten sposób?Spójrz na bibliotekę rejestrującą kotlin .
Umożliwia takie logowanie:
Lub tak:
Napisałem również wpis na blogu, porównując go z
AnkoLogger
: Logowanie się w Kotlin i Androidzie: AnkoLogger vs kotlin-loggingZastrzeżenie: jestem opiekunem tej biblioteki.
Edycja: logowanie kotlin ma teraz obsługę wielu platform: https://github.com/MicroUtils/kotlin-logging/wiki/Multiplatform-support
źródło
logger.info()
połączeniami, jak Jayson zrobił w swoim przyjętym odpowiedź.Jako dobry przykład implementacji logowania chciałbym wspomnieć o Anko, która używa specjalnego interfejsu,
AnkoLogger
który klasa wymagająca logowania powinna zaimplementować. W interfejsie znajduje się kod, który generuje tag rejestrowania dla klasy. Rejestrowanie odbywa się następnie za pomocą funkcji rozszerzających, które można wywołać w ramach implementacji interfejsu bez prefiksów, a nawet tworzenia instancji rejestratora.Nie sądzę, że jest to idiomatyczne , ale wydaje się dobrym podejściem, ponieważ wymaga minimalnego kodu, wystarczy dodać interfejs do deklaracji klasy, a otrzymasz rejestrowanie z różnymi tagami dla różnych klas.
Poniższy kod to w zasadzie AnkoLogger , uproszczony i przepisany do użytku niezależnego od Androida.
Po pierwsze, istnieje interfejs, który zachowuje się jak interfejs znaczników:
Pozwala jej implementacji na użycie funkcji rozszerzeń
MyLogger
wewnątrz ich kodu, po prostu je wywołującthis
. Zawiera również tag logowania.Następnie znajduje się ogólny punkt wejścia dla różnych metod logowania:
Zostanie wywołany przez metody logowania. Pobiera tag z
MyLogger
implementacji, sprawdza ustawienia rejestrowania, a następnie wywołuje jeden z dwóch programów obsługi, ten zThrowable
argumentem, a drugi bez.Następnie możesz zdefiniować dowolną liczbę metod logowania, w ten sposób:
Są one definiowane tylko raz w celu rejestrowania tylko wiadomości i logowania
Throwable
, jest to wykonywane z opcjąthrowable
parametru.Funkcje, które są przekazywane jako
handler
ithrowableHandler
mogą być różne dla różnych metod rejestrowania, na przykład mogą zapisać dziennik do pliku lub przesłać go gdzieś.isLoggingEnabled
iLoggingLevels
są pomijane dla zwięzłości, ale ich używanie zapewnia jeszcze większą elastyczność.Pozwala na następujące zastosowania:
Jest mała wada: do logowania do funkcji na poziomie pakietu potrzebny będzie obiekt rejestrujący:
źródło
android.util.Log
rejestrowanie. Jaki był Twój zamiar? używać Anko? Zbudować coś podobnego, używając Anko jako przykładu (lepiej jest po prostu wstawić sugerowany kod w tekście i naprawić go dla systemu innego niż Android zamiast mówić „przenieś to na system inny niż Android, oto link”. Zamiast tego dodaj przykładowy kod dzwoniąc do Anko)KISS: Dla zespołów Java przenoszących się do Kotlin
Jeśli nie masz nic przeciwko podawaniu nazwy klasy przy każdej instancji programu rejestrującego (tak jak java), możesz to uprościć, definiując to jako funkcję najwyższego poziomu gdzieś w swoim projekcie:
Używa parametru typu reified Kotlin .
Teraz możesz użyć tego w następujący sposób:
To podejście jest super proste i zbliżone do odpowiednika w Javie, ale dodaje tylko trochę cukru składniowego.
Następny krok: rozszerzenia lub pełnomocnicy
Osobiście wolę pójść o krok dalej i zastosować podejście rozszerzeń lub delegatów. Jest to ładnie podsumowane w odpowiedzi @ JaysonMinard, ale tutaj jest TL; DR dla podejścia „delegata” z log4j2 API ( AKTUALIZACJA : nie ma już potrzeby ręcznego pisania tego kodu, ponieważ został wydany jako oficjalny moduł projekt log4j2, patrz poniżej). Ponieważ log4j2, w przeciwieństwie do slf4j, obsługuje logowanie za pomocą
Supplier
, dodałem również delegata, aby ułatwić korzystanie z tych metod.Log4j2 Kotlin Logging API
Większość poprzedniej sekcji została bezpośrednio przystosowana do tworzenia modułu Kotlin Logging API , który jest teraz oficjalną częścią Log4j2 (zastrzeżenie: jestem głównym autorem). Możesz pobrać to bezpośrednio z Apache lub przez Maven Central .
Użycie jest zasadniczo takie, jak opisano powyżej, ale moduł obsługuje zarówno dostęp do rejestratora oparty na interfejsie, funkcję
logger
rozszerzającąAny
do użytku w miejscu, w którymthis
jest zdefiniowana, jak i nazwaną funkcję rejestrującą do użytku, gdy niethis
jest zdefiniowane (na przykład funkcje najwyższego poziomu).źródło
T.logger()
- patrz dolna część przykładowego kodu.Anko
Możesz to zrobić za pomocą
Anko
biblioteki. Miałbyś kod jak poniżej:logowanie kotlin
Biblioteka kotlin-logging ( projekt Github - kotlin-logging ) umożliwia pisanie kodu do logowania, jak poniżej:
StaticLog
lub możesz również użyć tej małej napisanej w bibliotece Kotlin nazwanej
StaticLog
wtedy Twój kod będzie wyglądał następująco:Drugie rozwiązanie może być lepsze, jeśli chcesz zdefiniować format wyjściowy dla metody logowania, taki jak:
lub użyj filtrów, na przykład:
timberkt
Jeśli korzystałeś już z
Timber
biblioteki logowania Jake'a Whartona, sprawdźtimberkt
.Przykład kodu:
Sprawdź również: Logowanie w Kotlin i Android: AnkoLogger vs kotlin-logging
Mam nadzieję, że to pomoże
źródło
Czy coś takiego zadziała dla Ciebie?
źródło
LoggerDelegate
a następnie tworzy funkcję najwyższego poziomu, która łatwiej jest utworzyć instancję delegata (niewiele łatwiej, ale trochę). I tę funkcję należy zmienić nainline
. Następnie używa delegata, aby zapewnić rejestrator, gdy tylko jest to pożądane. Ale zapewnia jeden dla towarzysza,Foo.Companion
a nie dla klasy,Foo
więc może nie jest zgodny z przeznaczeniem.logger()
funkcja ma działać,inline
jeśli nie ma żadnych lambd. IntelliJ sugeruje, że wstawianie w tym przypadku jest niepotrzebne: i.imgur.com/YQH3NB1.pngLazy
zamiast tego użyłem opakowania . Podstępem, aby dowiedzieć się, w jakiej klasie jest.Nie słyszałem o żadnym idiomie w tym zakresie. Im prostsze, tym lepiej, więc użyłbym właściwości najwyższego poziomu
Ta praktyka dobrze sprawdza się w Pythonie i chociaż Kotlin i Python mogą się różnić, wydaje mi się, że są one dość podobne w swoim „duchu” (mówiąc o idiomach).
źródło
val log = what?!?
... tworzenie loggera według nazwy? Ignorując fakt, że pytanie wskazywało, że chce utworzyć rejestrator dla określonej klasyLoggerFactory.getLogger(Foo.class);
A co z funkcją rozszerzającą w Class? W ten sposób otrzymasz:
Uwaga - w ogóle tego nie testowałem, więc może nie być całkiem poprawne.
źródło
Po pierwsze, możesz dodać funkcje rozszerzające do tworzenia rejestratora.
Wtedy będziesz mógł stworzyć rejestrator używając poniższego kodu.
Po drugie, możesz zdefiniować interfejs, który zapewnia rejestrator i jego implementację mieszaną.
Ten interfejs może być używany w następujący sposób.
źródło
utwórz obiekt towarzyszący i oznacz odpowiednie pola adnotacją @JvmStatic
źródło
Jest tu już wiele świetnych odpowiedzi, ale wszystkie dotyczą dodawania loggera do klasy, ale jak byś to zrobił, aby zarejestrować się w funkcjach najwyższego poziomu?
To podejście jest ogólne i na tyle proste, że działa dobrze w obu klasach, obiektach towarzyszących i funkcjach najwyższego poziomu:
źródło
Generalnie do tego służą obiekty towarzyszące: zastępowanie statycznych elementów.
źródło
JvmStatic
adnotacji. W przyszłości może być więcej niż jedna dozwolona. Poza tym ta odpowiedź nie jest zbyt pomocna bez dodatkowych informacji lub próbki.Factory
a drugiHelpers
Przykład Slf4j, to samo dla innych. Działa to nawet przy tworzeniu rejestratora na poziomie pakietu
Stosowanie:
źródło
źródło
To wciąż WIP (prawie ukończone), więc chciałbym się nim podzielić: https://github.com/leandronunes85/log-format-enforcer#kotlin-soon-to-come-in-version-14
Głównym celem tej biblioteki jest wymuszenie określonego stylu dziennika w całym projekcie. Poprzez wygenerowanie kodu Kotlin próbuję rozwiązać niektóre z problemów wymienionych w tym pytaniu. Jeśli chodzi o pierwotne pytanie, zwykle robię po prostu:
źródło
Możesz po prostu zbudować własną „bibliotekę” narzędzi. Nie potrzebujesz dużej biblioteki do tego zadania, co sprawi, że Twój projekt będzie cięższy i złożony.
Na przykład, możesz użyć Kotlin Reflection, aby uzyskać nazwę, typ i wartość dowolnej właściwości klasy.
Przede wszystkim upewnij się, że meta-zależność została ustalona w pliku build.gradle:
Następnie możesz po prostu skopiować i wkleić ten kod do swojego projektu:
Przykład użycia:
źródło