JAVA exPress logo Published on JAVA exPress (http://www.javaexpress.pl)

Log4J a komunikatory internetowe

Michał Szynkaruk

Issue 7 (2010-03-30)

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:

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:

    private String receiver;

    public String getReceiver() {
      return receiver;
    }

    public void setReceiver(String receiver) {
      this.receiver = receiver;
    }

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

    public void sendMessage(String content) {
      try {
        Skype.chat(receiver).send(content);
      } catch (SkypeException ex) {

      }
    }

oraz nadpiszmy metody dziedziczone z klasy AppenderSkeleton:

    @Override
    protected void append(LoggingEvent event) {
      sendMessage(getLayout().format(event));
    }

    @Override
    public boolean requiresLayout() {
      return true;
    }

    @Override
    public void close() {
    }

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

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

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

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

    Logger logger = Logger.getRootLogger();
    logger.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:

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:

    private int number;
    private String password;
    private int receiver;
    private boolean isReady;
    private boolean isFirst = true;
    private ISession session;
    private 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:

    public void connect() throws GGException {
      loginContext = new LoginContext(number, password);
      session = SessionFactory.createSession();
      session.addSessionStateListener(new SessionStateListener() {
        public void sessionStateChanged(SessionState oldSessionState,
            SessionState newSessionState) {
          if (newSessionState.equals(SessionState.AUTHENTICATION_AWAITING)) {
            login();
          } else if (newSessionState.equals(SessionState.LOGGED_IN)) {
            isReady = true;
          } else {
            isReady = false;
          }
        }
      });
      IConnectionService connectionService = session.getConnectionService();
      connectionService.addConnectionListener(new ConnectionListener.Stub() {
        @Override
        public void connectionEstablished() {
          System.out.println("Dokonano połączenia");
        }

        @Override
        public void connectionError(Exception ex) {
          System.out.println("Błąd połączenia: " + ex.getMessage());
        }

        @Override
        public void connectionClosed() {
          System.out.println("Połączenie zakończone");
        }
      });
      IServer server = session.getConnectionService().lookupServer(
          loginContext.getUin());
      connectionService.connect(server);
    }

Teraz czas na funkcję login():

    private void login() {
      ILoginService loginService = session.getLoginService();
      loginService.addLoginListener(new LoginListener.Stub() {
        @Override
        public void loginOK() {
          System.out.println("Zalogowano");
        }

        public void loginFailed() {
          System.out.println("Nieudane logowanie");
        }
      });
      try {
        loginService.login(loginContext);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }

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

    public void sendMessage(String content) {
      IMessageService messageService = session.getMessageService();
      OutgoingMessage outMessage = OutgoingMessage.createNewMessage(receiver,
          content);
      try {
        messageService.sendMessage(outMessage);
      } catch (GGException e) {
        e.printStackTrace();
      }
    }

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

    @Override
    protected void append(LoggingEvent event) {
      if (isFirst) {
        try {
          connect();
        } catch (GGException e) {
          e.printStackTrace();
        }
        while (!isReady) {

        }
        isFirst = false;
      }
      sendMessage(getLayout().format(event));
    }

    @Override
    public boolean requiresLayout() {
      return true;
    }

    @Override
    public void close() {
    }

Dzięki tej linii:

    sendMessage(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:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
    <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
      <appender class="log4jtests.GGAppender" name="gg">
        <param name="number" value="TU_PODAJESZ_SWÓJ_NUMER_GG" />
        <param name="password" value="TU_PODAJESZ_SWOJE_HASŁO_GG" />
        <param name="receiver" value="TU_PODAJESZ_NUMER_GG_ODBIORCY" />
        <layout class="org.apache.log4j.PatternLayout">
          <param name="ConversionPattern" value="[%p] %c - %m" />
        </layout>
      </appender>
      <root>
        <priority value="debug"></priority>
        <appender-ref ref="gg" />
      </root>
    </log4j:configuration>

gdzie:

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):

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

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

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

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:

    public class GtalkAppender extends AppenderSkeleton {
      private String user;
      private String password;
      private String receiver;
      // tu umieść gettery i settery dla powyższych trzech zmiennych

      private boolean isFirst = true;
      XMPPConnection connection;
      ConnectionConfiguration connConfig;

      public void sendMessage(String content) {
        try {
          Skype.chat(receiver).send(content);
        } catch (SkypeException ex) {

        }
      }

      @Override
      protected void append(LoggingEvent event) {
        if (isFirst) {
          connConfig = new ConnectionConfiguration("talk.google.com", 5222,
              "gmail.com");
          connection = new XMPPConnection(connConfig);
          try {
            connection.connect();
            connection.login(user, password);
          } catch (XMPPException ex) {

          }
          while (!connection.isConnected()) {
          }
          isFirst = !isFirst;
        }
        Message msg = new Message(receiver, Message.Type.chat);
        msg.setBody(getLayout().format(event));
        connection.sendPacket(msg);
      }

      @Override
      public boolean requiresLayout() {
        return true;
      }

      @Override
      public void close() {
      }
    }

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

    log4j.appender.gtalk=log4jtests.GtalkAppender
    log4j.appender.gtalk.layout=org.apache.log4j.PatternLayout
    log4j.appender.gtalk.layout.ConversionPattern=[%p] %c - %m
    log4j.appender.gtalk.user=TU_PODAJESZ_SWÓJ_ADRES_GMAIL
    log4j.appender.gtalk.password=TU_PODAJESZ_SWOJE_HASŁO
    log4j.appender.gtalk.receiver=TU_PODAJESZ_ADRES_GMAIL_ODBIORCY
    log4j.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.

Source: http://www.javaexpress.pl/article/show/Log4J_a_komunikatory_internetowe