1 /*
2 * Copyright (c) 1998-2005 The Jgroup Team.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 */
18 package jgroup.util.log;
19
20 import java.io.File;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.ObjectOutputStream;
24
25 import jgroup.util.Network;
26 import jgroup.util.Util;
27
28 import org.apache.log4j.Logger;
29
30 /**
31 * The event logger class provides methods used for logging specific events
32 * to a disk file. It uses a simple memory based buffer, which is flushed
33 * to disk after a given number of log events, or on demand. Also, it should
34 * flush and close the file on "clean" system exits, using the shutdown hook
35 * mechanism.
36 *
37 * @author Rune Vestvik
38 * @author Hein Meling
39 * @since Jgroup 3.0
40 */
41 public class Eventlogger
42 {
43
44 ////////////////////////////////////////////////////////////////////////////////////////////
45 // Logger
46 ////////////////////////////////////////////////////////////////////////////////////////////
47
48 /** Obtain logger for this class */
49 private static final Logger log = Logger.getLogger(Eventlogger.class);
50
51
52 ////////////////////////////////////////////////////////////////////////////////////////////
53 // Constants
54 ////////////////////////////////////////////////////////////////////////////////////////////
55
56 /** The filename prefix for the event logs */
57 public static final String PREFIX = "eventlog-";
58
59 /** The number of event objects to store in memory before flushing */
60 private static final int MEM_SIZE = 10000;
61
62 /** True if the measurement function should be activated. */
63 public static final boolean ENABLED;
64
65 /** Initialize static variables */
66 static {
67 ENABLED = Boolean.getBoolean("jgroup.log.measurements");
68 if (log.isDebugEnabled()) {
69 log.debug("Measurement logging " + (ENABLED ? "enabled" : "disabled"));
70 }
71 }
72
73
74 ////////////////////////////////////////////////////////////////////////////////////////////
75 // Fields
76 ////////////////////////////////////////////////////////////////////////////////////////////
77
78 /**
79 * Holds true only if no events has yet been logged;
80 * used to determine if a file needs to be created.
81 * If the file is closed, this is reset to true to allow
82 * creation of a new file.
83 */
84 private static boolean firstEvent = true;
85
86 /** The object output stream to which events (objects) from this JVM should be logged */
87 private static ObjectOutputStream out;
88
89 /** Index into the log memory array */
90 private static int index = 0;
91
92 /** The log memory array */
93 private static final Event[] memLog = new Event[MEM_SIZE];
94
95 /** The shutdown thread */
96 private static Thread shutdownThread;
97
98 /** The log file currently active */
99 private static String logFile;
100
101
102 ////////////////////////////////////////////////////////////////////////////////////////////
103 // Private methods
104 ////////////////////////////////////////////////////////////////////////////////////////////
105
106 private static void onShutdown()
107 {
108 if (shutdownThread == null) {
109 shutdownThread = new Thread("EventLogger-ShutdownHook") {
110 public void run()
111 {
112 if (log.isDebugEnabled())
113 log.debug("Running EventLogger-ShutdownHook");
114 close();
115 }
116 };
117 Runtime.getRuntime().addShutdownHook(shutdownThread);
118 }
119 }
120
121 private static void initLog()
122 {
123 // Set to false to avoid creating multiple log files unecessarily
124 firstEvent = false;
125 onShutdown();
126
127 String logDir = System.getProperty("jgroup.log.dir");
128 int ran = (int) (Math.random() * 99);
129 StringBuilder buf = new StringBuilder(logDir);
130 buf.append(File.separator);
131 buf.append(PREFIX);
132 buf.append(Network.getLocalMachineName());
133 buf.append("-");
134 buf.append(Util.getTimeString());
135 buf.append("-");
136 buf.append(ran);
137 logFile = buf.toString();
138 if (log.isDebugEnabled())
139 log.debug("Creating log file: " + logFile);
140 try {
141 out = new ObjectOutputStream(new FileOutputStream(logFile));
142 } catch (Exception e) {
143 log.warn("Failed to create: " + logFile, e);
144 }
145 }
146
147
148 ////////////////////////////////////////////////////////////////////////////////////////////
149 // Public logger methods
150 ////////////////////////////////////////////////////////////////////////////////////////////
151
152 /**
153 * Log simple event.
154 */
155 public static void logEvent(String description)
156 {
157 logEvent(null, description);
158 }
159
160 /**
161 * Log event with both type and description.
162 */
163 public static void logEvent(EventType type, String description)
164 {
165 logEvent(new Event(type, description));
166 }
167
168 /**
169 * Log any event type.
170 */
171 public static void logEvent(Event event)
172 {
173 if (firstEvent)
174 initLog();
175 memLog[index++] = event;
176 if (index == MEM_SIZE)
177 flushMem();
178 }
179
180 /**
181 * Log simple event and flush to disk.
182 */
183 public static void logEventFlush(String description)
184 {
185 logEventFlush(null, description);
186 }
187
188 /**
189 * Log event with both type and description and flush to disk.
190 */
191 public static void logEventFlush(EventType type, String description)
192 {
193 logEventFlush(new Event(type, description));
194 }
195
196 /**
197 * Log any event type and flush to disk.
198 */
199 public static void logEventFlush(Event event)
200 {
201 if (firstEvent)
202 initLog();
203 memLog[index++] = event;
204 flushMem();
205 }
206
207 /**
208 * Log the given event, ignoring system time and group identifier.
209 */
210 public static void logEventNoSysTime(String description)
211 {
212 logEvent(new Event(0, null, description));
213 }
214
215 public static void flushMem()
216 {
217 if (out == null) {
218 log.warn("Failed to flush memory; file already closed: " + logFile);
219 return;
220 }
221 try {
222 synchronized (Eventlogger.class) {
223 for (int i = 0; i < index; i++) {
224 out.writeObject(memLog[i]);
225 }
226 out.flush();
227 }
228 } catch (IOException e) {
229 log.warn("Failed to flush memory: " + logFile, e);
230 }
231 index = 0;
232 if (log.isDebugEnabled())
233 log.debug("Flushed the log file: " + logFile);
234 }
235
236 public static void close()
237 {
238 // Ignore duplicate calls
239 if (out == null)
240 return;
241 flushMem();
242 if (log.isDebugEnabled())
243 log.debug("Closing log file: " + logFile);
244 synchronized (Eventlogger.class) {
245 try {
246 out.close();
247 } catch (IOException e) {
248 // Ignore, may fail if the file was already closed from another thread.
249 }
250 out = null;
251 // Ensure that a new file can be created after a file has been closed
252 firstEvent = true;
253 }
254 }
255
256 } // END EventLogger