001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.core.jmx; 018 019 import java.awt.BorderLayout; 020 import java.awt.Color; 021 import java.awt.Font; 022 import java.io.IOException; 023 import java.io.PrintWriter; 024 import java.io.StringWriter; 025 import java.util.HashMap; 026 import java.util.Map; 027 028 import javax.management.InstanceNotFoundException; 029 import javax.management.MalformedObjectNameException; 030 import javax.management.Notification; 031 import javax.management.NotificationFilterSupport; 032 import javax.management.NotificationListener; 033 import javax.management.ObjectName; 034 import javax.management.remote.JMXConnector; 035 import javax.management.remote.JMXConnectorFactory; 036 import javax.management.remote.JMXServiceURL; 037 import javax.swing.JFrame; 038 import javax.swing.JOptionPane; 039 import javax.swing.JPanel; 040 import javax.swing.JScrollPane; 041 import javax.swing.JTabbedPane; 042 import javax.swing.JTextArea; 043 import javax.swing.SwingUtilities; 044 import javax.swing.UIManager; 045 import javax.swing.UIManager.LookAndFeelInfo; 046 047 /** 048 * GUI that connects to a Java process via JMX and allows the user to view and 049 * modify the log4j2 configuration, as well as monitor status logs. 050 * 051 * @see http 052 * ://docs.oracle.com/javase/6/docs/technotes/guides/management/jconsole 053 * .html 054 */ 055 public class ClientGUI extends JPanel implements NotificationListener { 056 private static final long serialVersionUID = -253621277232291174L; 057 private Client client; 058 private JTextArea statusLogTextArea; 059 private JTabbedPane tabbedPane; 060 061 public ClientGUI(Client client) throws InstanceNotFoundException, 062 MalformedObjectNameException, IOException { 063 this.client = Assert.isNotNull(client, "client"); 064 createWidgets(); 065 populateWidgets(); 066 registerListeners(); 067 } 068 069 private void createWidgets() { 070 statusLogTextArea = new JTextArea(); 071 statusLogTextArea.setEditable(false); 072 statusLogTextArea.setBackground(this.getBackground()); 073 statusLogTextArea.setForeground(Color.black); 074 statusLogTextArea.setFont(new Font("Monospaced", Font.PLAIN, 075 statusLogTextArea.getFont().getSize())); 076 JScrollPane scrollStatusLog = new JScrollPane(statusLogTextArea); 077 078 tabbedPane = new JTabbedPane(); 079 tabbedPane.addTab("StatusLogger", scrollStatusLog); 080 081 this.setLayout(new BorderLayout()); 082 this.add(tabbedPane, BorderLayout.CENTER); 083 } 084 085 private void populateWidgets() { 086 087 StatusLoggerAdminMBean statusAdmin = client.getStatusLoggerAdmin(); 088 String[] messages = statusAdmin.getStatusDataHistory(); 089 for (String message : messages) { 090 statusLogTextArea.append(message + "\n"); 091 } 092 093 for (LoggerContextAdminMBean ctx : client.getLoggerContextAdmins()) { 094 ClientEditConfigPanel editor = new ClientEditConfigPanel(ctx); 095 tabbedPane.addTab("LoggerContext: " + ctx.getName(), editor); 096 } 097 } 098 099 private void registerListeners() throws InstanceNotFoundException, 100 MalformedObjectNameException, IOException { 101 NotificationFilterSupport filter = new NotificationFilterSupport(); 102 filter.enableType(StatusLoggerAdminMBean.NOTIF_TYPE_MESSAGE); 103 ObjectName objName = new ObjectName(StatusLoggerAdminMBean.NAME); 104 client.getConnection().addNotificationListener(objName, this, filter, 105 null); 106 } 107 108 @Override 109 public void handleNotification(Notification notif, Object paramObject) { 110 if (StatusLoggerAdminMBean.NOTIF_TYPE_MESSAGE.equals(notif.getType())) { 111 statusLogTextArea.append(notif.getMessage() + "\n"); 112 } 113 } 114 115 /** 116 * Connects to the specified location and shows this panel in a window. 117 * <p> 118 * Useful links: 119 * http://www.componative.com/content/controller/developer/insights 120 * /jconsole3/ 121 * 122 * @param args 123 * must have at least one parameter, which specifies the location 124 * to connect to. Must be of the form {@code host:port} or 125 * {@code service:jmx:rmi:///jndi/rmi://<host>:<port>/jmxrmi} or 126 * {@code service:jmx:rmi://<host>:<port>/jndi/rmi://<host>:<port>/jmxrmi} 127 */ 128 public static void main(String[] args) throws Exception { 129 if (args.length < 1) { 130 usage(); 131 return; 132 } 133 String serviceUrl = args[0]; 134 if (!serviceUrl.startsWith("service:jmx")) { 135 serviceUrl = "service:jmx:rmi:///jndi/rmi://" + args[0] + "/jmxrmi"; 136 } 137 JMXServiceURL url = new JMXServiceURL(serviceUrl); 138 Map<String, String> paramMap = new HashMap<String, String>(); 139 for (Object objKey : System.getProperties().keySet()) { 140 String key = (String) objKey; 141 paramMap.put(key, System.getProperties().getProperty(key)); 142 } 143 JMXConnector connector = JMXConnectorFactory.connect(url, paramMap); 144 final Client client = new Client(connector); 145 final String title = "Log4J2 JMX Client - " + url; 146 147 SwingUtilities.invokeLater(new Runnable() { 148 @Override 149 public void run() { 150 installLookAndFeel(); 151 try { 152 ClientGUI gui = new ClientGUI(client); 153 JFrame frame = new JFrame(title); 154 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 155 frame.getContentPane().add(gui, BorderLayout.CENTER); 156 frame.pack(); 157 frame.setVisible(true); 158 } catch (Exception ex) { 159 // if console is visible, print error so that 160 // the stack trace remains visible after error dialog is 161 // closed 162 ex.printStackTrace(); 163 164 // show error in dialog: there may not be a console window 165 // visible 166 StringWriter sr = new StringWriter(); 167 ex.printStackTrace(new PrintWriter(sr)); 168 JOptionPane.showMessageDialog(null, sr.toString(), "Error", 169 JOptionPane.ERROR_MESSAGE); 170 } 171 } 172 }); 173 } 174 175 private static void usage() { 176 String me = ClientGUI.class.getName(); 177 System.err.println("Usage: java " + me + " <host>:<port>"); 178 System.err.println(" or: java " + me 179 + " service:jmx:rmi:///jndi/rmi://<host>:<port>/jmxrmi"); 180 String longAdr = " service:jmx:rmi://<host>:<port>/jndi/rmi://<host>:<port>/jmxrmi"; 181 System.err.println(" or: java " + me + longAdr); 182 } 183 184 private static void installLookAndFeel() { 185 try { 186 for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { 187 if ("Nimbus".equals(info.getName())) { 188 UIManager.setLookAndFeel(info.getClassName()); 189 return; 190 } 191 } 192 } catch (Exception ex) { 193 ex.printStackTrace(); 194 } 195 try { 196 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 197 } catch (Exception e) { 198 e.printStackTrace(); 199 } 200 } 201 }