細心!用心!耐心!

          吾非文人,乃市井一俗人也,讀百卷書,跨江河千里,故申城一游; 一兩滴辛酸,三四年學業,五六點粗墨,七八筆買賣,九十道人情。

          BlogJava 聯系 聚合 管理
            1 Posts :: 196 Stories :: 10 Comments :: 0 Trackbacks

          有時候我需要查看已經部署到服務器上的應用程序的日志,每次都要遠程登錄服務器感覺很麻煩,所以一般我會把log文件的目錄用apache做個網站,這樣通過IE就可以訪問到了。但是有時要看即時輸出情況,就要不斷的F5,很麻煩。所以就有個想法,不如用DWR2.0的反轉Ajax來做個程序,讓日志有變化時自動的發送到客戶端,這樣就我一個勁的按F5了。

          我下面就把這個程序分享給大家,希望大家提提意見。

          首先是環境:
          DWR 2.0.rc1
          Sun JDK 1.5

          先看張運行起來的圖吧,大家一看就知道這是個什么東西了。
          logviewer.png
          你可以制定要監視的log文件,當然有哪些文件文件可以被監視,你必須在服務端的xml配置中文件設置,當然你也可以監視一個目錄里的log文件,這對于而log文件是每天生成一個的情況很有用。你可以設定在瀏覽器上顯示的行數,操作行數,屏幕會自動滾動。你還可以添加一些過濾器,過濾掉不想看見的行,我目前只做了到了過濾掉一些信息,當然如果你有興趣,你也再添加一些更復雜的過濾器。過濾器的模式是用正則表達式表示的。

          下面是點擊“開始監聽”,運行后樣子
          logviewer2.png
          如果服務器上的catalina.2006-12-09.log文件發生變化,客戶端的瀏覽器上log顯示區也會自動的向上滾動。


          下面我就大致的介紹一下如何用DWR2.0來實現這樣的功能。在這里介紹的可能不是很詳細,不清楚的地方請看我提供的源碼。

          先來介紹一下目錄結構
          ├─lib  -- 編譯和測試用的第三方類庫
          ├─webapp -- 部署目錄
          ├─test -- 測試程序
          ├─java -- 主程序
          └─build.xml -- ant構建文件

          webapp下的文件和目錄
          │  style.css  -- 樣式表文件
          │  index.html -- 主畫面文件

          ├─WEB-INF
          │  │  web.xml -- 部署配置文件
          │  │  dwr.xml -- dwr的配置文件
          │  │  conf.xml -- 我們這個應用程序配置文件,主要是配置log文件
          │  │
          │  ├─classes
          │  │
          │  └─lib

          └─script -- javascript文件


          index.html中就是我們上面的圖片上能看到的頁面元素。其中的控件的事件處理都寫在\script\logviewer.js文件中。

          當頁面加載時執行startPoll()方法,復雜開始與服務器的通信,并且把log文件選擇框初始化,把已經添加過濾器列表顯示出來。

          function  startPoll() {
              DWREngine.setActiveReverseAjax(
          true );

              LogManager.getLogFileNames(
          function  (data) {
                  DWRUtil.removeAllOptions(
          " log_file " );
                  DWRUtil.addOptions(
          " log_file " , data);
              });

              LogManager.getFilters(
          function (data) {
                  
          for  ( var  i  =   0 ; i  <  data.length; i ++ ) {
                      addFilterDiv(data[i].pattern, data[i].id);
                  }
              });
          }


          當點擊“開始監聽”按鈕時調用服務端的LogManager的send方法,服務端開啟監聽線程,開始監聽做為參數傳遞的文件,如果文件有變動就會把最近增加的行發送到瀏覽器上來。

          var  startWatch  =   function () {
              clearLog();
              LogManager.send(DWRUtil.getValue(
          " log_file " ));
          }


          當點擊“結束監聽”按鈕,調用LogManager的stop()方法,結束掉監聽線程。

           

          function  stopWatch() {
              LogManager.stop();
          }

           


          當點擊“清空日志”按鈕,清除mainPanel中的所有子元素

          function  clearLog() {
              
          var  mainPanel  =  $( " main_panel " );
              
          while  (mainPanel.hasChildNodes()) {
                  mainPanel.removeChild(mainPanel.firstChild);
              }
          }

          當點擊“添加過濾器”,填充輸入框,要求輸入做為過濾器的正則表達式,輸入完成后,要做兩件事:
          1、LogManager.addFilter方法,把輸入的正則表達式傳送給服務端。
          2、把這個正則表達式添加到頁面上。

          function  addFilter() {
              
          var  regex  =  prompt( " 輸入正則表達式 " "" );
              
          if  (regex  !=   null   &&  regex  !=   "" ) {
                  LogManager.addFilter(regex, 
          function  (filterId) {
                      addFilterDiv(regex, filterId);
                  });
              }
          }
          注意這里,我們用到了DWR的回調模式,在調用服務端方法LogManager.addFilter成功后我們才調用客戶端的addFilterDiv方法把這個輸入的正則表達式顯示到頁面上。

          如果你足夠細心的話,應該會發現在這個js文件中有一個叫做addNewLine的方法在index.html中是沒有被調用的。這個方法其實是給服務端的LogManager.send函數調用的。

          上面這些內容就是服務端腳本的主要內容了,其實很簡單。主要負責通過DWR與服務端通信和處理頁面顯示。

          下面介紹服務端的核心類:LogManager

          這個類主要就這樣幾個方法:
             /**
               * 停止監控
               
          */
              
          public void stop() {
                  
          if (watcher != null) {
                      watcher.halt();
                  }
              }

              
          /**
               * 發送log信息
               
          */
              
          public void send(String filename) {
                  WebContext wctx 
          = WebContextFactory.get();
                  
          final ScriptSession scriptSession = wctx.getScriptSession();
                  
          if (watcher != null) {
                      watcher.halt();
                  }

                  
          try {
                      watcher 
          = new LogFileWatcher(filename);
                      watcher.addListener(
          new LogUpdateListener() {
                          
          public void onLogUpdate(List<String> lines) {
                              
          for (String line : lines) {
                                  
          if (checkFilters(line)) {
                                      ScriptBuffer scriptBuffer 
          = new ScriptBuffer();
                                      scriptBuffer.appendScript(
          "addNewLine(")
                                              .appendData(line)
                                              .appendScript(
          ");");
                                      scriptSession.addScript(scriptBuffer);
                                  }
                              }
                          }
                      });
                      watcher.start();
                  } 
          catch (IOException e) {
                      ScriptBuffer scriptBuffer 
          = new ScriptBuffer();
                      scriptBuffer.appendScript(
          "addNewLine(")
                              .appendData(e.getMessage())
                              .appendScript(
          ");");
                      scriptSession.addScript(scriptBuffer);
                      log.warn(e);
                  }
              }

              
          /**
               * 取得指定的日志文件路徑
               *
               * 
          @return 指定的日志文件路徑
               
          */
              
          public List<String> getLogFileNames() {
                  List
          <String> filenames = new ArrayList<String>();
                  
          try {
                      XMLConfiguration config 
          = getConfiguration();
                      List logfiles 
          = config.getList("log-files.file");
                      
          for (Object o : logfiles) {
                          filenames.add((String) o);
                      }
                  } 
          catch (ConfigurationException e) {
                      log.warn(e);
                  }

                  
          return filenames;
              }

              
          /**
               * 取得指定的日志目錄下的文件
               *
               * 
          @return 指定的日志目錄下的文件
               
          */
              
          public List<String> getLogFileNamesFromDir() {
                  List
          <String> filenames = new ArrayList<String>();
                  
          try {
                      XMLConfiguration config 
          = getConfiguration();
                      String dir 
          = config.getString("log-dir.dir");
                      
          if (dir != null) {
                          File rootDir 
          = new File(dir);
                          
          if (rootDir.exists()) {
                              
          if (rootDir.isFile()) {
                                  filenames.add(rootDir.getPath().replace(
          '\\''/'));
                              } 
          else if (rootDir.isDirectory()) {
                                  String patternString 
          = config.getString("log-dir.filter");
                                  File[] files;
                                  
          if (patternString != null && !patternString.equals("")) {
                                      files 
          = rootDir.listFiles(new LogFileFilter(patternString));
                                  } 
          else {
                                      files 
          = rootDir.listFiles();
                                  }

                                  
          for (File file : files) {
                                      filenames.add(file.getPath().replace(
          '\\''/'));
                                  }
                              }
                          }
                      }
                  } 
          catch (ConfigurationException e) {
                      log.warn(e);
                  }

                  
          return filenames;
              }

              
          /**
               * 添加一個過濾器,返回過濾器的id
               
          */
              
          public int addFilter(String regex) {
                  
          synchronized (filters) {
                      Filter filter 
          = new Filter(regex, SequenceGenerator.getInstance().next(), FilterType.INCLUDE);
                      filters.add(filter);
                      
          return filter.getId();
                  }

              }

              
          /**
               * 根據id刪除一個過濾器
               
          */
              
          public void removeFilter(int id) {
                  
          synchronized (filters) {
                      filters.remove(
          new Filter(id));
                  }
              }

              
          /**
               * 取得現在所有的過濾器列表
               
          */
              
          public List<Map<String, Object>> getFilters() {
                  List
          <Map<String, Object>> result = new ArrayList<Map<String, Object>>();
                  
          synchronized (filters) {
                      
          for (Filter filter : filters) {
                          Map
          <String, Object> filterItem = new HashMap<String, Object>();
                          filterItem.put(
          "id", filter.getId());
                          filterItem.put(
          "pattern", filter.getPattern().pattern());
                          result.add(filterItem);
                      }
                  }
                  
          return result;
              }

          對于大家都做過Java的朋友來說,這些代碼應該很容易就能看懂,我就不多說了。大家主要注意一下ScriptSession類,這個類就是起到主要功能的類了。

          其中的LogFileWatcher是一個Thread類,它是用來監視log文件的。

          SequenceGenerator.java是用來生成過濾器的id的。

          LogUpdateListener.java是一個接口,用于實現事件回調的。

          然后看一個dwr的配置文件
          <dwr>
              
          <allow>
                  
          <create creator="new" javascript="LogManager" scope="session">
                      
          <param name="class" value="org.devside.logviewer.LogManager"/>
                      
          <include method="send"/>
                      
          <include method="stop"/>
                      
          <include method="getLogFileNames"/>
                      
          <include method="getLogFileNamesFromDir"/>
                      
          <include method="addFilter"/>
                      
          <include method="removeFilter"/>
                      
          <include method="getFilters"/>
                  
          </create>
              
          </allow>
          </dwr>

          這里的配置文件和1.x幾乎沒什么兩樣,就是scope我這里設置成了session范圍的。這樣就可以多個人同時監視不同的log文件了。

          web.xml文件也基本上是老樣子
          <?xml version="1.0" encoding="UTF-8"?>
          <web-app id="LogViewer" version="2.4"
                   xmlns
          ="http://java.sun.com/xml/ns/j2ee"
                   xmlns:xsi
          ="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation
          ="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
              
          <display-name>Web Log Viewer</display-name>

              
          <servlet>
                  
          <description>Direct Web Remoter Servlet</description>
                  
          <display-name>DWR Servlet</display-name>
                  
          <servlet-name>dwr-invoker</servlet-name>
                  
          <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
                  
          <init-param>
                      
          <param-name>debug</param-name>
                      
          <param-value>true</param-value>
                  
          </init-param>
                  
          <init-param>
                      
          <param-name>pollAndCometEnabled</param-name>
                      
          <param-value>true</param-value>
                  
          </init-param>
                  
          <load-on-startup>1</load-on-startup>
              
          </servlet>

              
          <servlet-mapping>
                  
          <servlet-name>dwr-invoker</servlet-name>
                  
          <url-pattern>/dwr/*</url-pattern>
              
          </servlet-mapping>

              
          <welcome-file-list>
                  
          <welcome-file>index.html</welcome-file>
              
          </welcome-file-list>
          </web-app>
          dwr的包名發生了變化,并且要開啟反轉ajax,就要把pollAndCometEnabled參數設置為true。

          總結,總體來說DWR2.0中的反轉ajax還是很容易使用的,這也是dwr的一貫風格,不用知道過多的細節就能容易的實現ajax。dwr絕對是Java開發者的首選ajax框架。
          另外我這個程序其實還是為了演示用的,如果想要用戶實際開發可能還需要修改,比如安全性上面,性能上面。而性能上面的主要問題是客戶端瀏覽器,如果服務端的log文件過大,而瀏覽器有不能即時的回收內存,就會造成客戶端瀏覽器內存占用過大而死掉的問題。而服務端由于java的內容回收機制已經比較成熟應該不會有什么問題。我在ie6和firefox2都試過了,firefox效果能好一些。

          源碼下載:
          http://www.aygfsteel.com/Files/mstar/LogViewer.part1.rar
          http://www.aygfsteel.com/Files/mstar/LogViewer.part2.rar
          posted on 2007-04-16 15:02 張金鵬 閱讀(276) 評論(0)  編輯  收藏 所屬分類: AJAX技術
          主站蜘蛛池模板: 宜君县| 通榆县| 哈密市| 开化县| 临湘市| 临猗县| 怀远县| 安福县| 霍山县| 深泽县| 泾阳县| 来安县| 迁西县| 华容县| 房产| 宜都市| 海城市| 宜城市| 肃北| 葫芦岛市| 华蓥市| 鲁甸县| 望都县| 武夷山市| 区。| 郯城县| 宜丰县| 高唐县| 绍兴县| 昆山市| 宿松县| 葫芦岛市| 鸡东县| 长岭县| 清水县| 上林县| 建宁县| 伊通| 佛山市| 栖霞市| 外汇|