Debugging on Linux made easy for Testers.

While testing on linux or linux based product, it’s always a challenge to track different logs for debugging purpose.
One of the typical ways of doing this is to open multiple SSH terminal logins to server and tail various log files of the product. That becomes tiring & correlating different logs is difficult.

 

So, what did I do?

First thing as everyone does is to google for tools which can tail logs of linux server. No luck there. So here is my attempt of creating my own tool and sharing it here to help everyone who would come across the same problem.

This tool , let’s call it ‘Linux Log Viewer’ runs on Windows & Mac OSX.

Software requirements to run the tool:
 –  jre version 1.8 to run the tool( if you have developed using jdk version 1.8)
 –  Set JAVA_HOME environmental variable in your host.
 –  Double click the .jar file.

 

The tool specific to my project has lots of features customized to my requirement. But, the generic tool that you can download here can only tail logs.
Go ahead & try it out. If you want to build or customize this tool, providing the source code as well below.

 

This tool is built on java
Why Java? Because i was familiar with it and also easy to build. Eclipse IDE for java has a very nice tool called WindowBuilder which will help in design.

 

Some of the use cases which I’ve used & you might be familiar with.
– Display version of your product you are working on
– Display status of services
– Reseting / Restarting services.
– Quey the database and see the result on the Tool.

 

All these from a single window & single click, without executing any commands on the terminal.

 

Software requirements to develop the tool:
– eclipse IDE (https://www.eclipse.org/downloads/download.php?file=/oomph/epp/neon/R/eclipse-inst-win64.exe)
 – WindowBuilder tool (http://download.eclipse.org/windowbuilder/WB/release/R201506241200-1/4.5/) . How to install windowBuilder tool( https://www.eclipse.org/forums/index.php/t/1067889/ )
 -Java JDK 1.8

 

Snapshot showing Eclipse IDE + windows builder :
 eclipse+windowbuilder

 

Snapshot of Tool on running:
tool-snapshot

 

Runnable Jar file download:
When you download it it will be in .txt  extension ,due to security reasons I was not able to attach the file in .jar format. Please download it and change it to .jar extension before executing it

 

Now you got everthing to play around the tool and create your own functions to meet your testing requirements. Enjoy and do let me know if it was useful

 

Thank You,
Raghavendra Balgi

 

Source Code:
package linuxlogviewer;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.SystemColor;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.SwingConstants;
import javax.swing.SwingWorker;
import javax.swing.UIManager;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;




public class linuxlogviewer {

        private JFrame frmlogviewer ;
        private JTextArea log2 ;
        private JTextArea log ;
        private JTextArea log3 ;
        private JTextArea  log4 ;
        private JTextField ipaddress ;
        private JButton btnConnect ;
        private JPasswordField passwordField ;
        public Session session =null;
        private JTextField log1txt ;
        private JTextField log2txt ;
        private JTextField log3txt ;
        private JTextField log4txt ;
        private JTextField log5txt ;
        private JTextArea log5 ;
        int numberOfThreads = 2;
    final ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads);
       
    private JLabel lblusername;
    private JTextField usernameField;
    private JButton refreshbutton;
       

        /**
        * Launch the application.
        */
        public static void main(String[] args) {        
              EventQueue. invokeLater(new Runnable() {
                      public void run() {
                            try {
                                  linuxlogviewer window = new linuxlogviewer();
                                   window. frmlogviewer.setVisible(true);
                           } catch (Exception e ) {
                                   e.printStackTrace();
                           }
                     }
              });
       }

        /**
        * Create the application.
        */
        public linuxlogviewer() {
              initialize();
       }

        /**
        * Initialize the contents of the frame.
        */
        private void initialize() {
               frmlogviewer = new JFrame();
               frmlogviewer.getContentPane().setFont(new Font("Times New Roman", Font.PLAIN, 12));
               frmlogviewer.setIconImage(Toolkit.getDefaultToolkit().getImage(linuxlogviewer. class.getResource("/com/sun/javafx/scene/web/skin/IncreaseIndent_16x16_JFX.png" )));
               frmlogviewer.setTitle("Linux Log Viewer" );
               frmlogviewer.setBounds(100, 100, 1800, 800);

              
              
               frmlogviewer.addWindowListener(new java.awt.event.WindowAdapter() {
                  @Override
                  public void windowClosing(java.awt.event.WindowEvent windowEvent) {
                     

                             if(session != null){
                                          if (session .isConnected()==true){
                                                System. out.println("connect inside if"+session);
                                       Channel channel= null;
                                                 try {
                                                        channel = session.openChannel("exec" );
                                                } catch (JSchException e ) {
                                                        // TODO Auto-generated catch block
                                                        e.printStackTrace();
                                                }
                                                ((ChannelExec) channel).setCommand( "killall tail;"+"kill `ps auxww | grep "+"\"bash -c while\""+ " | awk '{print $2}'`");
                                       System. out.println("after kill all statement"+session);
                                       channel.setInputStream( null);
                                       ((ChannelExec)channel).setErrStream(System.err);
                                       
                                       try {
                                                       InputStream in=channel .getInputStream();
                                                } catch (IOException e ) {
                                                        // TODO Auto-generated catch block
                                                        e.printStackTrace();
                                                }
                                       try {
                                                        channel.connect();
                                                } catch (JSchException e ) {
                                                        // TODO Auto-generated catch block
                                                        e.printStackTrace();
                                                }
                                       try{Thread.sleep(2000);} catch(Exception ee){}  
                                      
                                       session.disconnect();
                                         }
                                         
                                          session.disconnect();
                                                
                                    
                                         }
                            
                          System. exit(0);
                      }
                     
                     
                
                     
                  }
              );
               
              
              JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
               tabbedPane.setBounds(0, 112, 1099, 635);
              
              JScrollPane log1pane = new JScrollPane();
               tabbedPane.addTab("log1" , null, log1pane, null);
              
               log = new JTextArea();
               log.setLineWrap( true);
               log.setFont( new Font("Times New Roman" , Font.PLAIN, 14));
               log.setWrapStyleWord( true);
               log1pane.setViewportView( log);
              
               log1txt = new JTextField();
               log1txt.setText( "Enter command like : tail -f /var/log/../../xyz.log");
               log1txt.setForeground(Color. DARK_GRAY);
               log1txt.setFont( new Font("Tahoma" , Font.BOLD, 12));
               log1pane.setColumnHeaderView( log1txt);
               log1txt.setColumns(10);
              
              JScrollPane log2pane = new JScrollPane();
               tabbedPane.addTab("log2" , null, log2pane, null);
              
               log2 = new JTextArea();
               log2.setLineWrap( true);
               log2.setFont( new Font("Times New Roman" , Font.PLAIN, 14));
               log2.setWrapStyleWord( true);
               log2pane.setViewportView( log2);
              
               log2txt = new JTextField();
               log2txt.setText( "Enter command like : tail -f /var/log/../../xyz.log");
               log2txt.setForeground(Color. DARK_GRAY);
               log2txt.setFont( new Font("Tahoma" , Font.BOLD, 12));
               log2pane.setColumnHeaderView( log2txt);
               log2txt.setColumns(10);
              
              JScrollPane log3pane = new JScrollPane();
               tabbedPane.addTab("log3" , null, log3pane, null);
              
               log3 = new JTextArea();
               log3.setWrapStyleWord( true);
               log3.setLineWrap( true);
               log3.setFont( new Font("Times New Roman" , Font.PLAIN, 14));
               log3pane.setViewportView( log3);
              
               log3txt = new JTextField();
               log3txt.setText( "Enter command like : tail -f /var/log/../../xyz.log");
               log3txt.setForeground(Color. DARK_GRAY);
               log3txt.setFont( new Font("Tahoma" , Font.BOLD, 12));
               log3pane.setColumnHeaderView( log3txt);
               log3txt.setColumns(10);
              
              JLabel lblIpAddress = new JLabel("IP Address:" );
               lblIpAddress.setFont(new Font("Tahoma" , Font.BOLD, 11));
               lblIpAddress.setBounds(10, 14, 82, 14);
              
               ipaddress = new JTextField();
               ipaddress.setBounds(89, 10, 113, 23);
               ipaddress.setColumns(10);
              
              JLabel lblRootPassword = new JLabel("Password:" );
               lblRootPassword.setFont(new Font("Tahoma" , Font.BOLD, 11));
               lblRootPassword.setBounds(439, 14, 73, 14);
              
               passwordField = new JPasswordField();
               passwordField.setBounds(518, 10, 120, 23);
              
               btnConnect = new JButton("Connect");
               btnConnect.setFont(new Font("Tahoma" , Font.BOLD, 11));
               btnConnect.setBackground(UIManager.getColor( "Button.background"));

               // when you click on Tail log button on the UI below action will be performed. I have placed tail log functions inside this so that it starts tailing the log file
               btnConnect.addActionListener(new ActionListener() {
                      public void actionPerformed(ActionEvent arg0) {
                           
                            //System.out.println("connect before  if"+session.getPort());
                            if(session != null){
                            if (session .isConnected()==true){
                                  System. out.println("connect inside if" +session );
                          Channel channel= null;
                                   try {
                                          channel = session.openChannel( "exec");
                                  } catch (JSchException e ) {
                                          // TODO Auto-generated catch block
                                          e.printStackTrace();
                                  }
                         // ((ChannelExec)channel).setCommand("killall tail");
                                  ((ChannelExec) channel).setCommand( "killall tail;"+"kill `ps auxww | grep "+ "\"bash -c while\""+ " | awk '{print $2}'`" );
                          channel.setInputStream( null);
                          ((ChannelExec) channel).setErrStream(System. err);
                          
                          try {
                                         InputStream in=channel .getInputStream();
                                  } catch (IOException e ) {
                                          // TODO Auto-generated catch block
                                          e.printStackTrace();
                                  }
                          try {
                                          channel.connect();
                                  } catch (JSchException e ) {
                                          // TODO Auto-generated catch block
                                          e.printStackTrace();
                                  }
                          try{Thread.sleep(1000);} catch(Exception ee ){}     
                         
                          session.disconnect();
                           }
                           
                            session.disconnect();
                                  
                       
                           }
                           
                           System. out.println(session );
                           
                           
                           
                           getsession();
                            log2.setText( null);
                            log1.setText( null);
                            log3.setText( null);
                            log4.setText( null);

                            log5.setText( null);

                         
                           log1( session, log1txt);
                           log2( session, log2txt);
                           log3( session, log3txt);
                           log4( session, log4txt);
                           log5( session, log5txt);


                     
                           
                     }
                                  
                     
              });
               btnConnect.setBounds(660, 8, 133, 26);
               frmlogviewer.getContentPane().setLayout(null);
               frmlogviewer.getContentPane().add(tabbedPane );
              
              JScrollPane log4pane = new JScrollPane();
               tabbedPane.addTab("log4" , null, log4pane, null);
              
               log4 = new JTextArea();
               log4.setLineWrap( true);
               log4.setWrapStyleWord( true);
               log4.setFont( new Font("Times New Roman" , Font.PLAIN, 14));
               log4pane.setViewportView( log4);
              
               log4txt = new JTextField();
               log4txt.setText( "Enter command like : tail -f /var/log/../../xyz.log");
               log4txt.setForeground(Color. DARK_GRAY);
               log4txt.setFont( new Font("Tahoma" , Font.BOLD, 12));
               log4pane.setColumnHeaderView( log4txt);
               log4txt.setColumns(10);
              
              JScrollPane log5pane = new JScrollPane();
               tabbedPane.addTab("log5" , null, log5pane, null);
              
               log5 = new JTextArea();
               log5.setWrapStyleWord( true);
               log5.setLineWrap( true);
               log5.setFont( new Font("Times New Roman" , Font.PLAIN, 14));
               log5pane.setViewportView( log5);
              
               log5txt = new JTextField();
               log5txt.setToolTipText( "");
               log5txt.setText( "Enter command like : tail -f /var/log/../../xyz.log");
               log5txt.setForeground(Color. DARK_GRAY);
               log5txt.setBackground(Color. WHITE);
               log5txt.setFont( new Font("Tahoma" , Font.BOLD, 12));
               log5pane.setColumnHeaderView( log5txt);
               log5txt.setColumns(10);
               frmlogviewer.getContentPane().add(lblIpAddress );
               frmlogviewer.getContentPane().add(ipaddress );
               frmlogviewer.getContentPane().add(lblRootPassword );
               frmlogviewer.getContentPane().add(passwordField );
               frmlogviewer.getContentPane().add(btnConnect );
              
               lblusername = new JLabel("User Name:" );
               lblusername.setFont(new Font("Tahoma" , Font.BOLD, 11));
               lblusername.setBounds(227, 14, 73, 14);
               frmlogviewer.getContentPane().add(lblusername );
              
               usernameField = new JTextField();
               usernameField.setBounds(309, 10, 120, 22);
               frmlogviewer.getContentPane().add(usernameField );
               usernameField.setColumns(10);
              
               refreshbutton = new JButton("Tail Log");
               refreshbutton.addActionListener(new ActionListener() {
                      public void actionPerformed(ActionEvent e) {
                           
                           log1( session, log1txt);
                           log2( session, log2txt);
                           log3( session, log3txt);
                           log4( session, log4txt);
                           log5( session, log5txt);
                           
                           
                           
                     }
              });
               refreshbutton.setFont(new Font("Tahoma" , Font.BOLD, 11));
               refreshbutton.setBackground(UIManager.getColor( "Button.background"));
               refreshbutton.setBounds(875, 93, 133, 26);
               frmlogviewer.getContentPane().add(refreshbutton );
       }
       
       
// For tailing log1
// This is a function which is executing the command specified on the tool UI and streaming the command output and redirecting it on the tool UI
// Edit the function to your requirement like executing sql query or see the status of your product services  
     
        private void log1(Session session,JTextField textfield){
              
              SwingWorker<Void,String> worker = new SwingWorker<Void,String>(){

                      @Override
                      protected Void doInBackground() throws Exception {
                           
                   Channel channel= session.openChannel( "exec");
                   ((ChannelExec) channel).setCommand( textfield.getText());
                   channel.setInputStream( null);
                   ((ChannelExec) channel).setErrStream(System. err);
                   
                   InputStream in= channel.getInputStream();
                   channel.connect();
                   byte[] tmp =new byte[1024];
                   while(true ){
                     while(in .available()>0){
                       int i =in .read(tmp , 0, 1024);
                       if(i <0)break;
                       String s= new String(tmp , 0, i );
                       System. out.println(s );
                       publish( s);
                       s= null;
                      
                     }
                     if(channel .isClosed()){
                       System. out.println("exit-status: "+channel.getExitStatus());
                       break;
                     }
                     try{Thread.sleep(1000);} catch(Exception ee ){}
                   }
                   channel.disconnect();
                            return null ;
                           
                     }

                      @Override
                      protected void process(List<String> chunks) {
                            // TODO Auto-generated method stub
                           String value = chunks.get( chunks.size()-1);
                            log.append( value);   
                            value= null;
                     }
                           
              };
               worker.execute();
       
       }
       
// For tailing log2
// This is a function which is executing the command specified on the tool UI and streaming the command output and redirecting it on the tool UI
// Edit the function to your requirement like executing sql query or see the status of your product services
        private void log2(Session session,JTextField textfield){
              SwingWorker<Void,String> worker = new SwingWorker<Void,String>(){

                      @Override
                      protected Void doInBackground() throws Exception {
                           
                   Channel channel= session.openChannel( "exec");
                   ((ChannelExec) channel).setCommand( textfield.getText());
                   channel.setInputStream( null);
                   ((ChannelExec) channel).setErrStream(System. err);
                   
                   InputStream in= channel.getInputStream();
                   channel.connect();
                   byte[] tmp =new byte[1024];
                   while(true ){
                     while(in .available()>0){
                       int i =in .read(tmp , 0, 1024);
                       if(i <0)break;
                       String s= new String(tmp , 0, i );
                       System. out.println(s );
                       publish( s);
                       s= null;
                     }
                     if(channel .isClosed()){
                       System. out.println("exit-status: "+channel.getExitStatus());
                       break;
                     }
                     try{Thread.sleep(1000);} catch(Exception ee ){}
                   }
                   channel.disconnect();
                            return null ;
                           
                     }

                      @Override
                      protected void process(List<String> chunks) {
                            // TODO Auto-generated method stub
                           String value = chunks.get( chunks.size()-1);
                            log2.append( value);  
                            value= null;
                           
                     }
                           
              };
               worker.execute();
       
       }
       
// For tailing log3
// This is a function which is executing the command specified on the tool UI and streaming the command output and redirecting it on the tool UI
// Edit the function to your requirement like executing sql query or see the status of your product services

private void log3(Session session,JTextField textfield){
              
              SwingWorker<Void,String> worker = new SwingWorker<Void,String>(){

                      @Override
                      protected Void doInBackground() throws Exception {
                           
                   Channel channel= session.openChannel( "exec");
                   ((ChannelExec) channel).setCommand( textfield.getText());
                   channel.setInputStream( null);
                   ((ChannelExec) channel).setErrStream(System. err);
                   
                   InputStream in= channel.getInputStream();
                   channel.connect();
                   byte[] tmp =new byte[1024];
                   while(true ){
                     while(in .available()>0){
                       int i =in .read(tmp , 0, 1024);
                       if(i <0)break;
                       String s= new String(tmp , 0, i );
                       System. out.println(s );
                       publish( s);
                       s= null;
                     }
                     if(channel .isClosed()){
                       System. out.println("exit-status: "+channel.getExitStatus());
                       break;
                     }
                     try{Thread.sleep(1000);} catch(Exception ee ){}
                   }
                   channel.disconnect();
                            return null ;
                           
                     }

                      @Override
                      protected void process(List<String> chunks) {
                            // TODO Auto-generated method stub
                           String value = chunks.get( chunks.size()-1);
                            log3.append( value);  
                            value= null;
                           
                     }
                           
              };
               worker.execute();
       
       }
       
// For tailing log4
// This is a function which is executing the command specified on the tool UI and streaming the command output and redirecting it on the tool UI
// Edit the function to your requirement like executing sql query or see the status of your product services

private void log4(Session session,JTextField textfield){
              
              SwingWorker<Void,String> worker = new SwingWorker<Void,String>(){

                      @Override
                      protected Void doInBackground() throws Exception {
                           
                           
                   Channel channel= session.openChannel( "exec");
                   ((ChannelExec) channel).setCommand( textfield.getText());
                   channel.setInputStream( null);
                   ((ChannelExec) channel).setErrStream(System. err);
                   
                   InputStream in= channel.getInputStream();
                   channel.connect();
                   byte[] tmp =new byte[1024];
                   while(true ){
                     while(in .available()>0){
                       int i =in .read(tmp , 0, 1024);
                       if(i <0)break;
                       String s= new String(tmp , 0, i );
                       System. out.println(s );
                       publish( s);
                       s= null;
                     }
                     if(channel .isClosed()){
                       System. out.println("exit-status: "+channel.getExitStatus());
                       break;
                     }
                     try{Thread.sleep(1000);} catch(Exception ee ){}
                   }
                   channel.disconnect();
                            return null ;
                           
                     }

                      @Override
                      protected void process(List<String> chunks) {
                            // TODO Auto-generated method stub
                           String value = chunks.get( chunks.size()-1);
                            log4.append( value);  
                            value= null;
                     }
                           
              };
               worker.execute();
       
       }
       
//For tailing log5
private void log5(Session session,JTextField textfield){
              
              SwingWorker<Void,String> worker = new SwingWorker<Void,String>(){

                      @Override
                      protected Void doInBackground() throws Exception {
                           
                           
                   Channel channel= session.openChannel( "exec");
                   ((ChannelExec) channel).setCommand( textfield.getText());
                   channel.setInputStream( null);
                   ((ChannelExec) channel).setErrStream(System. err);
                   
                   InputStream in= channel.getInputStream();
                   channel.connect();
                   byte[] tmp =new byte[1024];
                   while(true ){
                     while(in .available()>0){
                       int i =in .read(tmp , 0, 1024);
                       if(i <0)break;
                       String s= new String(tmp , 0, i );
                       System. out.println(s );
                       publish( s);
                       s= null;
                     }
                     if(channel .isClosed()){
                       System. out.println("exit-status: "+channel.getExitStatus());
                       break;
                     }
                     try{Thread.sleep(1000);} catch(Exception ee ){}
                   }
                   channel.disconnect();
                            return null ;
                           
                     }

                      @Override
                      protected void process(List<String> chunks) {
                            // TODO Auto-generated method stub
                           String value = chunks.get( chunks.size()-1);
                            log5.append( value);  
                            value= null;
                     }
                           
              };
               worker.execute();
       
       }





        // ssh session creation and handling
        @SuppressWarnings("deprecation" )
        public void getsession(){
              
               try{
                     
        java.util.Properties config = new java.util.Properties();
        config.put( "StrictHostKeyChecking", "no" );
        JSch jsch = new JSch();
        session= jsch.getSession( usernameField.getText(), ipaddress .getText(), 22);
        System. out.println("get session" +session );
        session.setPassword( passwordField.getText());
        session.setConfig( config);
        session.connect();
        System. out.println("Connected" );
        System. out.println(session .isConnected());

        btnConnect.setBackground(new Color(0, 153, 51));
        btnConnect.setText("Connected" );

       
        
       
               } catch(Exception e ){
                      //passwordField.setText(null);
                            log2.setText( null);
                            log.setText( null);
                            log3.setText( null);
                            log4.setText( null);
                            log5.setText( null);
                      btnConnect.setBackground(new Color(255, 51, 0));
                      btnConnect.setText("Not Connected" );
                     JOptionPane. showMessageDialog(null, "Could Not Connect To Server." +"\r\n" + "\r\n"+"\r\n" +"Ipaddress or Password Is Not Valid." +"\r\n" +"                -----OR-----     "+ "\r\n"+"SSH Service Is Not Running " +"\r\n" +"                -----OR-----     "+ "\r\n"+ "Root Access Is Disabled." );
                   e.printStackTrace();
              
              
              
              
       }
              
              
       
       }
}

Like to come up with simple solutions to complex issues encountered during testing security products/applications and to share my knowledge thru blogs. Working as Technical leader in network security domain.

Leave a Reply