JAVA exPress > Archive > Issue 7 (2010-03-30) > Log4J a komunikatory internetowe

Log4J a komunikatory internetowe

Logi informujące o błędach w trakcie działania aplikacji można zapisywać zarówno lokalnie (jako pliki tekstowe, krotki w bazie danych itd.) jak i wysyłać na inną maszynę, np. w postaci e-maila. Niewątpliwe wielu z nas ma na stałe uruchomiony jakiś komunikator internetowy niezależnie od tego czy jesteśmy w pracy, w domu czy w podróży. Skoro chcemy być w stałym kontakcie z naszymi znajomymi, to dlaczego nie możemy być z naszą aplikacją?

W tym artykule pokażę jak można wykorzystać bibliotekę Log4j i najpopularniejsze komunikatory w Polsce: Skype, Gadu-Gadu oraz Google Talk.

Skype i Log4j

Dzięki API o nazwie Skype4Java, które jest oficjalnie udostępnione poprzez stronę https://developer.skype.com/wiki/Java_API możemy w łatwy sposób wysyłać logi na podane konto. Aby to uczynić należy:

  • posiadać aktywne konto Skype

  • dołączyć bibliotekę skype_full.jar

  • dołączyć bibliotekę log4j

Na początku konstruujemy własny appender w oparciu o abstrakcyjną klasę AppenderSkeleton (jeśli nie masz podstawowej wiedzy dotyczącej biblioteki log4j, to polecam przeczytanie mojego artykułu, który znajduje się w poprzednim numerze Java Express oraz oficjalnej dokumentacji na stronie http://logging.apache.org/log4j/1.2/index.html). Nazwijmy naszą klasę SkypeAppender i umieśćmy w niej tylko jedną zmienną wraz ze standardowym getterem i setterem:

1private String receiver;
2 
3public String getReceiver() {
4  return receiver;
5}
6 
7public void setReceiver(String receiver) {
8  this.receiver = receiver;
9}

Następnie zdefiniujmy funkcję odpowiedzialną za wysyłanie wiadomości:

1public void sendMessage(String content) {
2  try {
3    Skype.chat(receiver).send(content);
4  } catch (SkypeException ex) {
5 
6  }
7}

oraz nadpiszmy metody dziedziczone z klasy AppenderSkeleton:

01@Override
02protected void append(LoggingEvent event) {
03  sendMessage(getLayout().format(event));
04}
05 
06@Override
07public boolean requiresLayout() {
08  return true;
09}
10 
11@Override
12public void close() {
13}

Konfigurację możemy ustawić przykładowo w pliku właściwości:

1log4j.appender.skype=log4jtests.SkypeAppender
2log4j.appender.skype.layout=org.apache.log4j.PatternLayout
3log4j.appender.skype.layout.ConversionPattern=[%p] %c - %m
4log4j.appender.skype.receiver= TU_PODAJESZ_NAZWE_ODBIORCY
5log4j.rootLogger=DEBUG, skype

Pozostało nam już tylko stworzyć obiekt typu Logger i wysłać wpis.

Przykładowe wywołanie w statycznej funkcji main():

1Logger logger = Logger.getRootLogger();
2logger.info("Czesc, tu aplikacja :)");

Ważne jest by w trakcie działania aplikacji uruchomiony był Skype i umożliwiał on ingerencję naszej aplikacji w Javie.

Wysyłamy loGGi

Gadu-Gadu wykorzystuje własny protokół komunikacji, w którym m. in. każdy użytkownik jest jednoznacznie identyfikowany za pomocą unikalnego numeru. Aby móc wysyłać logi w postaci wiadomości musimy:

  • posiadać aktywne konto GG z którego dane logi wysyłamy,

  • dołączyć bibliotekę JGGApi która implementuje wymieniony protoków,

  • dołączyć bibliotekę Jakarta-Commons Logging, która jest wykorzystywana przez JGGApi,

  • dołączyć bibliotekę log4j.

W tym przykładzie skorzystałem z JGGApi w wersji 1.6, log4j w wersji 1.2.15, Commons Logging w wersji 1.1.1.

Gadu-Gadu wykorzystuje własny protokół komunikacji

Podobnie jak w poprzednim przykładzie konstruujemy własny appender w oparciu o abstrakcyjną klasę AppenderSkeleton. Niech nasza klasa przyjmie nazwę GGAppender. Umieścimy w niej następujące zmienne:

1private int number;
2private String password;
3private int receiver;
4private boolean isReady;
5private boolean isFirst = true;
6private ISession session;
7private LoginContext loginContext;

Gdzie number i password odnoszą się do naszego konta gg, receiver jest numerem GG, na który chcemy wysłać naszą wiadomość.

Zmienna isReady mówi nam kiedy mamy połączenie z serwerem Gadu-Gadu, jesteśmy zalogowani i gotowi wysyłać wiadomości. Zmienna isFirst określa czy potrzebujemy połączenia z serwerem Gadu-Gadu, session jak sama nazwa wskazuje jest sesją, a loginContext zawiera dane o naszym koncie GG. Dla wszystkich wyżej wymienionych zmiennych definiujemy standardowe gettery i settery (tak by móc przypisać im wartości określone w konfiguracji, o czym za chwile będzie mowa). Następnie definiujemy funkcję connect() dzięki której połączymy się z serwerem Gadu-Gadu:

01public void connect() throws GGException {
02  loginContext = new LoginContext(number, password);
03  session = SessionFactory.createSession();
04  session.addSessionStateListener(new SessionStateListener() {
05    public void sessionStateChanged(SessionState oldSessionState,
06        SessionState newSessionState) {
07      if (newSessionState.equals(SessionState.AUTHENTICATION_AWAITING)) {
08        login();
09      } else if (newSessionState.equals(SessionState.LOGGED_IN)) {
10        isReady = true;
11      } else {
12        isReady = false;
13      }
14    }
15  });
16  IConnectionService connectionService = session.getConnectionService();
17  connectionService.addConnectionListener(new ConnectionListener.Stub() {
18    @Override
19    public void connectionEstablished() {
20      System.out.println("Dokonano połączenia");
21    }
22 
23    @Override
24    public void connectionError(Exception ex) {
25      System.out.println("Błąd połączenia: " + ex.getMessage());
26    }
27 
28    @Override
29    public void connectionClosed() {
30      System.out.println("Połączenie zakończone");
31    }
32  });
33  IServer server = session.getConnectionService().lookupServer(
34      loginContext.getUin());
35  connectionService.connect(server);
36}

Teraz czas na funkcję login():

01private void login() {
02  ILoginService loginService = session.getLoginService();
03  loginService.addLoginListener(new LoginListener.Stub() {
04    @Override
05    public void loginOK() {
06      System.out.println("Zalogowano");
07    }
08 
09    public void loginFailed() {
10      System.out.println("Nieudane logowanie");
11    }
12  });
13  try {
14    loginService.login(loginContext);
15  } catch (Exception e) {
16    e.printStackTrace();
17  }
18}

oraz funkcję sendMessage, która w argumencie przyjmuje treść wiadomości:

01public void sendMessage(String content) {
02  IMessageService messageService = session.getMessageService();
03  OutgoingMessage outMessage = OutgoingMessage.createNewMessage(receiver,
04      content);
05  try {
06    messageService.sendMessage(outMessage);
07  } catch (GGException e) {
08    e.printStackTrace();
09  }
10}

Musimy także zaimplementować funkcję append, w której definiujemy gdzie log jest zapisywany, funkcje close oraz requiresLayout:

01@Override
02protected void append(LoggingEvent event) {
03  if (isFirst) {
04    try {
05      connect();
06    } catch (GGException e) {
07      e.printStackTrace();
08    }
09    while (!isReady) {
10 
11    }
12    isFirst = false;
13  }
14  sendMessage(getLayout().format(event));
15}
16 
17@Override
18public boolean requiresLayout() {
19  return true;
20}
21 
22@Override
23public void close() {
24}

Dzięki tej linii:

1sendMessage(getLayout().format(event));

wysyłamy sformatowaną wiadomość według szablonu określonego w pliku konfiguracji.

Zdecydowałem się na konfigurację XML'ową w pliku log4j.xml:

01<?xml version="1.0" encoding="UTF-8" ?>
02<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
03<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
04  <appender class="log4jtests.GGAppender" name="gg">
05    <param name="number" value="TU_PODAJESZ_SWÓJ_NUMER_GG" />
06    <param name="password" value="TU_PODAJESZ_SWOJE_HASŁO_GG" />
07    <param name="receiver" value="TU_PODAJESZ_NUMER_GG_ODBIORCY" />
08    <layout class="org.apache.log4j.PatternLayout">
09      <param name="ConversionPattern" value="[%p] %c - %m" />
10    </layout>
11  </appender>
12  <root>
13    <priority value="debug"></priority>
14    <appender-ref ref="gg" />
15  </root>
16</log4j:configuration>

gdzie:

  • %p to priorytet zdarzenia,

  • %c to kategoria zdarzenia,

  • %m to treść komunikatu,

a poziom, od którego logi są wysyłane ustawiamy na debug.

Podobny efekt uzyskamy, jeśli stworzymy plik właściwości (log4j.properties):

1log4j.appender.gg=log4jtests.GGAppender
2log4j.appender.gg.layout=org.apache.log4j.PatternLayout
3log4j.appender.gg.layout.ConversionPattern=[%p] %c - %m
4log4j.appender.gg.number= TU_PODAJESZ_SWÓJ_NUMER_GG
5log4j.appender.gg.password=TU_PODAJESZ_SWOJE_HASŁO_GG
6log4j.appender.gg.receiver= TU_PODAJESZ_NUMER_GG_ODBIORCY
7log4j.rootLogger=DEBUG, gg

Przykładowy test, który możemy umieścić w statycznej funkcji main():

1Logger logger = Logger.getRootLogger();
2try {
3  int i = 5 / 0;
4} catch (ArithmeticException ex) {
5  logger.error("Wystapil blad w programie");
6}

Google Talk

Wysyłanie wiadomości na konto Google Talk jest równie łatwe, gdyż dysponujemy darmowymi klientami protokołu xmpp, z którego korzysta ten komunikator. Osobiście skorzystałem z biblioteki Smack w wersji 3.1.0.

Sposób monitorowania działania naszej aplikacji
przy wykorzystaniu tych trzech komunikatorów
wydaje się być niezwykle ciekawy i skuteczny

Podobnie, jak w poprzednich dwóch przykładach, tworzymy własny appender, który u mnie prezentuje się następująco:

01public class GtalkAppender extends AppenderSkeleton {
02  private String user;
03  private String password;
04  private String receiver;
05  // tu umieść gettery i settery dla powyższych trzech zmiennych
06 
07  private boolean isFirst = true;
08  XMPPConnection connection;
09  ConnectionConfiguration connConfig;
10 
11  public void sendMessage(String content) {
12    try {
13      Skype.chat(receiver).send(content);
14    } catch (SkypeException ex) {
15 
16    }
17  }
18 
19  @Override
20  protected void append(LoggingEvent event) {
21    if (isFirst) {
22      connConfig = new ConnectionConfiguration("talk.google.com", 5222,
23          "gmail.com");
24      connection = new XMPPConnection(connConfig);
25      try {
26        connection.connect();
27        connection.login(user, password);
28      } catch (XMPPException ex) {
29 
30      }
31      while (!connection.isConnected()) {
32      }
33      isFirst = !isFirst;
34    }
35    Message msg = new Message(receiver, Message.Type.chat);
36    msg.setBody(getLayout().format(event));
37    connection.sendPacket(msg);
38  }
39 
40  @Override
41  public boolean requiresLayout() {
42    return true;
43  }
44 
45  @Override
46  public void close() {
47  }
48}

Przykładowa konfiguracja poprzez plik właściwości:

1log4j.appender.gtalk=log4jtests.GtalkAppender
2log4j.appender.gtalk.layout=org.apache.log4j.PatternLayout
3log4j.appender.gtalk.layout.ConversionPattern=[%p] %c - %m
4log4j.appender.gtalk.user=TU_PODAJESZ_SWÓJ_ADRES_GMAIL
5log4j.appender.gtalk.password=TU_PODAJESZ_SWOJE_HASŁO
6log4j.appender.gtalk.receiver=TU_PODAJESZ_ADRES_GMAIL_ODBIORCY
7log4j.rootLogger=DEBUG, gtalk

Podsumowanie

Możliwości Log4j oraz istnienie javowych bibliotek dla Skype, Gadu-Gadu oraz Gtalk powodują, że każdy z nas ma możliwość szybkiego odbioru informacji o wystąpieniu ewentualnych błędów w trakcie działania naszej aplikacji. Dzięki temu szybko i skutecznie możemy rozpocząć akcje naprawcze. Sposób monitorowania działania naszej aplikacji przy wykorzystaniu tych trzech komunikatorów wydaje się być niezwykle ciekawy i skuteczny.

Nobody has commented it yet.

Only logged in users can write comments

Developers World