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 018package org.apache.log4j.net; 019 020import java.io.File; 021import java.net.InetAddress; 022import java.net.ServerSocket; 023import java.net.Socket; 024import java.util.Hashtable; 025 026import org.apache.log4j.Hierarchy; 027import org.apache.log4j.Level; 028import org.apache.log4j.LogManager; 029import org.apache.log4j.Logger; 030import org.apache.log4j.PropertyConfigurator; 031import org.apache.log4j.spi.LoggerRepository; 032import org.apache.log4j.spi.RootLogger; 033 034 035/** 036 A {@link SocketNode} based server that uses a different hierarchy 037 for each client. 038 039 <pre> 040 <b>Usage:</b> java org.apache.log4j.net.SocketServer port configFile configDir 041 042 where <b>port</b> is a part number where the server listens, 043 <b>configFile</b> is a configuration file fed to the {@link PropertyConfigurator} and 044 <b>configDir</b> is a path to a directory containing configuration files, possibly one for each client host. 045 </pre> 046 047 <p>The <code>configFile</code> is used to configure the log4j 048 default hierarchy that the <code>SocketServer</code> will use to 049 report on its actions. 050 051 <p>When a new connection is opened from a previously unknown 052 host, say <code>foo.bar.net</code>, then the 053 <code>SocketServer</code> will search for a configuration file 054 called <code>foo.bar.net.lcf</code> under the directory 055 <code>configDir</code> that was passed as the third argument. If 056 the file can be found, then a new hierarchy is instantiated and 057 configured using the configuration file 058 <code>foo.bar.net.lcf</code>. If and when the host 059 <code>foo.bar.net</code> opens another connection to the server, 060 then the previously configured hierarchy is used. 061 062 <p>In case there is no file called <code>foo.bar.net.lcf</code> 063 under the directory <code>configDir</code>, then the 064 <em>generic</em> hierarchy is used. The generic hierarchy is 065 configured using a configuration file called 066 <code>generic.lcf</code> under the <code>configDir</code> 067 directory. If no such file exists, then the generic hierarchy will be 068 identical to the log4j default hierarchy. 069 070 <p>Having different client hosts log using different hierarchies 071 ensures the total independence of the clients with respect to 072 their logging settings. 073 074 <p>Currently, the hierarchy that will be used for a given request 075 depends on the IP address of the client host. For example, two 076 separate applicatons running on the same host and logging to the 077 same server will share the same hierarchy. This is perfectly safe 078 except that it might not provide the right amount of independence 079 between applications. The <code>SocketServer</code> is intended 080 as an example to be enhanced in order to implement more elaborate 081 policies. 082 083 084 @author Ceki Gülcü 085 086 @since 1.0 */ 087 088public class SocketServer { 089 090 static String GENERIC = "generic"; 091 static String CONFIG_FILE_EXT = ".lcf"; 092 093 static Logger cat = Logger.getLogger(SocketServer.class); 094 static SocketServer server; 095 static int port; 096 097 // key=inetAddress, value=hierarchy 098 Hashtable hierarchyMap; 099 LoggerRepository genericHierarchy; 100 File dir; 101 102 public 103 static 104 void main(String argv[]) { 105 if(argv.length == 3) 106 init(argv[0], argv[1], argv[2]); 107 else 108 usage("Wrong number of arguments."); 109 110 try { 111 cat.info("Listening on port " + port); 112 ServerSocket serverSocket = new ServerSocket(port); 113 while(true) { 114 cat.info("Waiting to accept a new client."); 115 Socket socket = serverSocket.accept(); 116 InetAddress inetAddress = socket.getInetAddress(); 117 cat.info("Connected to client at " + inetAddress); 118 119 LoggerRepository h = (LoggerRepository) server.hierarchyMap.get(inetAddress); 120 if(h == null) { 121 h = server.configureHierarchy(inetAddress); 122 } 123 124 cat.info("Starting new socket node."); 125 new Thread(new SocketNode(socket, h)).start(); 126 } 127 } 128 catch(Exception e) { 129 e.printStackTrace(); 130 } 131 } 132 133 134 static 135 void usage(String msg) { 136 System.err.println(msg); 137 System.err.println( 138 "Usage: java " +SocketServer.class.getName() + " port configFile directory"); 139 System.exit(1); 140 } 141 142 static 143 void init(String portStr, String configFile, String dirStr) { 144 try { 145 port = Integer.parseInt(portStr); 146 } 147 catch(java.lang.NumberFormatException e) { 148 e.printStackTrace(); 149 usage("Could not interpret port number ["+ portStr +"]."); 150 } 151 152 PropertyConfigurator.configure(configFile); 153 154 File dir = new File(dirStr); 155 if(!dir.isDirectory()) { 156 usage("["+dirStr+"] is not a directory."); 157 } 158 server = new SocketServer(dir); 159 } 160 161 162 public 163 SocketServer(File directory) { 164 this.dir = directory; 165 hierarchyMap = new Hashtable(11); 166 } 167 168 // This method assumes that there is no hiearchy for inetAddress 169 // yet. It will configure one and return it. 170 LoggerRepository configureHierarchy(InetAddress inetAddress) { 171 cat.info("Locating configuration file for "+inetAddress); 172 // We assume that the toSting method of InetAddress returns is in 173 // the format hostname/d1.d2.d3.d4 e.g. torino/192.168.1.1 174 String s = inetAddress.toString(); 175 int i = s.indexOf("/"); 176 if(i == -1) { 177 cat.warn("Could not parse the inetAddress ["+inetAddress+ 178 "]. Using default hierarchy."); 179 return genericHierarchy(); 180 } else { 181 String key = s.substring(0, i); 182 183 File configFile = new File(dir, key+CONFIG_FILE_EXT); 184 if(configFile.exists()) { 185 Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG)); 186 hierarchyMap.put(inetAddress, h); 187 188 new PropertyConfigurator().doConfigure(configFile.getAbsolutePath(), h); 189 190 return h; 191 } else { 192 cat.warn("Could not find config file ["+configFile+"]."); 193 return genericHierarchy(); 194 } 195 } 196 } 197 198 LoggerRepository genericHierarchy() { 199 if(genericHierarchy == null) { 200 File f = new File(dir, GENERIC+CONFIG_FILE_EXT); 201 if(f.exists()) { 202 genericHierarchy = new Hierarchy(new RootLogger(Level.DEBUG)); 203 new PropertyConfigurator().doConfigure(f.getAbsolutePath(), genericHierarchy); 204 } else { 205 cat.warn("Could not find config file ["+f+ 206 "]. Will use the default hierarchy."); 207 genericHierarchy = LogManager.getLoggerRepository(); 208 } 209 } 210 return genericHierarchy; 211 } 212}