Fix: add LTTngEvent class to fix delayed logger
[lttng-ust.git] / liblttng-ust-jul / org / lttng / ust / jul / LTTngTCPSessiondClient.java
CommitLineData
43e5396b
DG
1/*
2 * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
3 *
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, version 2.1 only,
6 * as published by the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18package org.lttng.ust.jul;
19
20import java.util.concurrent.Semaphore;
21import java.nio.ByteBuffer;
22import java.nio.ByteOrder;
23import java.lang.Integer;
24import java.io.IOException;
25import java.io.BufferedOutputStream;
26import java.io.ByteArrayOutputStream;
27import java.io.DataOutputStream;
28import java.io.DataInputStream;
29import java.net.*;
30import java.lang.management.ManagementFactory;
31import java.util.ArrayList;
529e6def 32import java.util.HashMap;
43e5396b
DG
33import java.util.List;
34import java.util.Timer;
35import java.util.TimerTask;
529e6def 36import java.util.logging.Logger;
43e5396b
DG
37
38class USTRegisterMsg {
39 public static int pid;
40}
41
42public class LTTngTCPSessiondClient {
43 /* Command header from the session deamon. */
44 private LTTngSessiondCmd2_4.sessiond_hdr headerCmd =
45 new LTTngSessiondCmd2_4.sessiond_hdr();
46
47 private final String sessiondHost;
48 private final int sessiondPort;
49 private Socket sessiondSock;
50 private boolean quit = false;
51
52 private DataInputStream inFromSessiond;
53 private DataOutputStream outToSessiond;
54
55 private LTTngLogHandler handler;
56
57 private Semaphore registerSem;
58
59 private Timer eventTimer;
5b5ffa03 60 private List<LTTngEvent> enabledEventList = new ArrayList<LTTngEvent>();
529e6def
DG
61 /*
62 * Map of Logger objects that have been enabled. They are indexed by name.
63 */
64 private HashMap<String, Logger> enabledLoggers = new HashMap<String, Logger>();
43e5396b
DG
65 /* Timer delay at each 5 seconds. */
66 private final static long timerDelay = 5 * 1000;
67 private static boolean timerInitialized;
68
69 public LTTngTCPSessiondClient(String host, int port, Semaphore sem) {
70 this.sessiondHost = host;
71 this.sessiondPort = port;
72 this.registerSem = sem;
73 this.eventTimer = new Timer();
74 this.timerInitialized = false;
75 }
76
77 private void setupEventTimer() {
78 if (this.timerInitialized) {
79 return;
80 }
81
82 this.eventTimer.scheduleAtFixedRate(new TimerTask() {
83 @Override
84 public void run() {
85 /*
86 * We have to make a copy here since it is possible that the
87 * enabled event list is changed during an iteration on it.
88 */
5b5ffa03 89 List<LTTngEvent> tmpList = new ArrayList<LTTngEvent>(enabledEventList);
43e5396b
DG
90
91 LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd = new
92 LTTngSessiondCmd2_4.sessiond_enable_handler();
5b5ffa03
DG
93 for (LTTngEvent event: tmpList) {
94 int ret;
95 Logger logger;
96
529e6def
DG
97 /*
98 * Check if this Logger name has been enabled already. Note
99 * that in the case of "*", it's never added in that hash
100 * table thus the enable command does a lookup for each
101 * logger name in that hash table for the * case in order
102 * to make sure we don't enable twice the same logger
103 * because JUL apparently accepts that the *same*
104 * LogHandler can be added twice on a Logger object...
105 * don't ask...
106 */
5b5ffa03
DG
107 logger = enabledLoggers.get(event.name);
108 if (logger != null) {
109 continue;
110 }
111
112 /*
113 * Set to one means that the enable all event has been seen
114 * thus event from that point on must use loglevel for all
115 * events. Else the object has its own loglevel.
116 */
117 if (handler.logLevelUseAll == 1) {
118 event.logLevel.level = handler.logLevelAll;
119 event.logLevel.type = handler.logLevelTypeAll;
120 }
121
122 /*
123 * The all event is a special case since we have to iterate
124 * over every Logger to see which one was not enabled.
125 */
126 if (event.name.equals("*")) {
127 enableCmd.name = event.name;
128 enableCmd.lttngLogLevel = event.logLevel.level;
129 enableCmd.lttngLogLevelType = event.logLevel.type;
130 /*
131 * The return value is irrelevant since the * event is
132 * always kept in the list.
133 */
134 enableCmd.execute(handler, enabledLoggers);
529e6def
DG
135 continue;
136 }
137
5b5ffa03
DG
138 ret = enableCmd.enableLogger(handler, event, enabledLoggers);
139 if (ret == 1) {
140 /* Enabled so remove the event from the list. */
141 enabledEventList.remove(event);
43e5396b
DG
142 }
143 }
144 }
145 }, this.timerDelay, this.timerDelay);
146
147 this.timerInitialized = true;
148 }
149
150 public void init(LTTngLogHandler handler) throws InterruptedException {
151 this.handler = handler;
152
153 for (;;) {
154 if (this.quit) {
155 break;
156 }
157
158 try {
159
160 /*
161 * Connect to the session daemon before anything else.
162 */
163 connectToSessiond();
164
165 /*
166 * Register to the session daemon as the Java component of the
167 * UST application.
168 */
169 registerToSessiond();
170 this.registerSem.release();
171
172 setupEventTimer();
173
174 /*
175 * Block on socket receive and wait for command from the
176 * session daemon. This will return if and only if there is a
177 * fatal error or the socket closes.
178 */
179 handleSessiondCmd();
180 } catch (UnknownHostException uhe) {
181 this.registerSem.release();
182 System.out.println(uhe);
183 } catch (IOException ioe) {
184 this.registerSem.release();
185 Thread.sleep(3000);
186 } catch (Exception e) {
187 this.registerSem.release();
188 e.printStackTrace();
189 }
190 }
191 }
192
193 public void destroy() {
194 this.quit = true;
195 this.eventTimer.cancel();
196
197 try {
198 if (this.sessiondSock != null) {
199 this.sessiondSock.close();
200 }
201 } catch (Exception e) {
202 e.printStackTrace();
203 }
204 }
205
206 /*
207 * Receive header data from the session daemon using the LTTng command
208 * static buffer of the right size.
209 */
210 private void recvHeader() throws Exception {
211 int read_len;
212 byte data[] = new byte[this.headerCmd.SIZE];
213
214 read_len = this.inFromSessiond.read(data, 0, data.length);
215 if (read_len != data.length) {
216 throw new IOException();
217 }
218 this.headerCmd.populate(data);
219 }
220
221 /*
222 * Receive payload from the session daemon. This MUST be done after a
223 * recvHeader() so the header value of a command are known.
224 *
225 * The caller SHOULD use isPayload() before which returns true if a payload
226 * is expected after the header.
227 */
228 private byte[] recvPayload() throws Exception {
229 byte payload[] = new byte[(int) this.headerCmd.data_size];
230
231 /* Failsafe check so we don't waste our time reading 0 bytes. */
232 if (payload.length == 0) {
233 return null;
234 }
235
236 this.inFromSessiond.read(payload, 0, payload.length);
237 return payload;
238 }
239
240 /*
241 * Handle session command from the session daemon.
242 */
243 private void handleSessiondCmd() throws Exception {
244 int ret_code;
245 byte data[] = null;
246
247 while (true) {
248 /* Get header from session daemon. */
249 recvHeader();
250
251 if (headerCmd.data_size > 0) {
252 data = recvPayload();
253 }
254
255 switch (headerCmd.cmd) {
256 case CMD_LIST:
257 {
258 LTTngSessiondCmd2_4.sessiond_list_logger listLoggerCmd =
259 new LTTngSessiondCmd2_4.sessiond_list_logger();
260 listLoggerCmd.execute(this.handler);
261 data = listLoggerCmd.getBytes();
262 break;
263 }
264 case CMD_ENABLE:
265 {
5b5ffa03 266 LTTngEvent event;
43e5396b
DG
267 LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd =
268 new LTTngSessiondCmd2_4.sessiond_enable_handler();
269 if (data == null) {
270 enableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD;
271 break;
272 }
273 enableCmd.populate(data);
5b5ffa03
DG
274 event = enableCmd.execute(this.handler, this.enabledLoggers);
275 if (event != null) {
43e5396b
DG
276 /*
277 * Add the event to the list so it can be enabled if
278 * the logger appears at some point in time.
279 */
5b5ffa03
DG
280 if (enabledEventList.contains(event) == false) {
281 enabledEventList.add(event);
529e6def 282 }
43e5396b
DG
283 }
284 data = enableCmd.getBytes();
285 break;
286 }
287 case CMD_DISABLE:
288 {
289 LTTngSessiondCmd2_4.sessiond_disable_handler disableCmd =
290 new LTTngSessiondCmd2_4.sessiond_disable_handler();
291 if (data == null) {
292 disableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD;
293 break;
294 }
295 disableCmd.populate(data);
296 disableCmd.execute(this.handler);
297 data = disableCmd.getBytes();
298 break;
299 }
300 default:
301 {
302 data = new byte[4];
303 ByteBuffer buf = ByteBuffer.wrap(data);
304 buf.order(ByteOrder.BIG_ENDIAN);
305 LTTngSessiondCmd2_4.lttng_jul_ret_code code =
306 LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD;
307 buf.putInt(code.getCode());
308 break;
309 }
310 }
311
312 /* Send payload to session daemon. */
313 this.outToSessiond.write(data, 0, data.length);
314 this.outToSessiond.flush();
315 }
316 }
317
318 private void connectToSessiond() throws Exception {
319 this.sessiondSock = new Socket(this.sessiondHost, this.sessiondPort);
320 this.inFromSessiond = new DataInputStream(
321 sessiondSock.getInputStream());
322 this.outToSessiond = new DataOutputStream(
323 sessiondSock.getOutputStream());
324 }
325
326 private void registerToSessiond() throws Exception {
327 byte data[] = new byte[4];
328 ByteBuffer buf = ByteBuffer.wrap(data);
329 String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
330
331 buf.putInt(Integer.parseInt(pid));
332 this.outToSessiond.write(data, 0, data.length);
333 this.outToSessiond.flush();
334 }
335}
This page took 0.03559 seconds and 4 git commands to generate.