2 * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
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.
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
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
18 package org
.lttng
.ust
.jul
;
20 import java
.util
.concurrent
.Semaphore
;
21 import java
.nio
.ByteBuffer
;
22 import java
.nio
.ByteOrder
;
23 import java
.lang
.Integer
;
24 import java
.io
.IOException
;
25 import java
.io
.BufferedOutputStream
;
26 import java
.io
.ByteArrayOutputStream
;
27 import java
.io
.DataOutputStream
;
28 import java
.io
.DataInputStream
;
30 import java
.lang
.management
.ManagementFactory
;
31 import java
.util
.ArrayList
;
32 import java
.util
.HashMap
;
33 import java
.util
.HashSet
;
34 import java
.util
.Iterator
;
35 import java
.util
.List
;
37 import java
.util
.Timer
;
38 import java
.util
.TimerTask
;
39 import java
.util
.logging
.Logger
;
40 import java
.util
.Collections
;
42 class USTRegisterMsg
{
43 public static int pid
;
46 public class LTTngTCPSessiondClient
{
47 /* Command header from the session deamon. */
48 private LTTngSessiondCmd2_4
.sessiond_hdr headerCmd
=
49 new LTTngSessiondCmd2_4
.sessiond_hdr();
51 private final String sessiondHost
;
52 private final int sessiondPort
;
53 private Socket sessiondSock
;
54 private boolean quit
= false;
56 private DataInputStream inFromSessiond
;
57 private DataOutputStream outToSessiond
;
59 private LTTngLogHandler handler
;
61 private Semaphore registerSem
;
63 private Timer eventTimer
;
64 private Set
<LTTngEvent
> enabledEventSet
=
65 Collections
.synchronizedSet(new HashSet
<LTTngEvent
>());
67 * Map of Logger objects that have been enabled. They are indexed by name.
69 private HashMap
<String
, Logger
> enabledLoggers
= new HashMap
<String
, Logger
>();
70 /* Timer delay at each 5 seconds. */
71 private final static long timerDelay
= 5 * 1000;
72 private static boolean timerInitialized
;
74 public LTTngTCPSessiondClient(String host
, int port
, Semaphore sem
) {
75 this.sessiondHost
= host
;
76 this.sessiondPort
= port
;
77 this.registerSem
= sem
;
78 this.eventTimer
= new Timer();
79 this.timerInitialized
= false;
82 private void setupEventTimer() {
83 if (this.timerInitialized
) {
87 this.eventTimer
.scheduleAtFixedRate(new TimerTask() {
90 synchronized (enabledEventSet
) {
91 LTTngSessiondCmd2_4
.sessiond_enable_handler enableCmd
= new
92 LTTngSessiondCmd2_4
.sessiond_enable_handler();
94 * Modifying events in a Set will raise a
95 * ConcurrentModificationException. Thus, we remove an event
96 * and add its modified version to modifiedEvents when a
97 * modification is necessary.
99 Set
<LTTngEvent
> modifiedEvents
= new HashSet
<LTTngEvent
>();
100 Iterator
<LTTngEvent
> it
= enabledEventSet
.iterator();
102 while (it
.hasNext()) {
105 LTTngEvent event
= it
.next();
108 * Check if this Logger name has been enabled already. Note
109 * that in the case of "*", it's never added in that hash
110 * table thus the enable command does a lookup for each
111 * logger name in that hash table for the * case in order
112 * to make sure we don't enable twice the same logger
113 * because JUL apparently accepts that the *same*
114 * LogHandler can be added twice on a Logger object...
117 logger
= enabledLoggers
.get(event
.name
);
118 if (logger
!= null) {
123 * Set to one means that the enable all event has been seen
124 * thus event from that point on must use loglevel for all
125 * events. Else the object has its own loglevel.
127 if (handler
.logLevelUseAll
== 1) {
129 event
.logLevel
.level
= handler
.logLevelAll
;
130 event
.logLevel
.type
= handler
.logLevelTypeAll
;
131 modifiedEvents
.add(event
);
135 * The all event is a special case since we have to iterate
136 * over every Logger to see which one was not enabled.
138 if (event
.name
.equals("*")) {
139 enableCmd
.name
= event
.name
;
140 enableCmd
.lttngLogLevel
= event
.logLevel
.level
;
141 enableCmd
.lttngLogLevelType
= event
.logLevel
.type
;
143 * The return value is irrelevant since the * event is
144 * always kept in the set.
146 enableCmd
.execute(handler
, enabledLoggers
);
150 ret
= enableCmd
.enableLogger(handler
, event
, enabledLoggers
);
152 /* Enabled so remove the event from the set. */
153 if (!modifiedEvents
.remove(event
)) {
155 * event can only be present in one of
162 enabledEventSet
.addAll(modifiedEvents
);
166 }, this.timerDelay
, this.timerDelay
);
168 this.timerInitialized
= true;
171 public void init(LTTngLogHandler handler
) throws InterruptedException
{
172 this.handler
= handler
;
182 * Connect to the session daemon before anything else.
187 * Register to the session daemon as the Java component of the
190 registerToSessiond();
191 this.registerSem
.release();
196 * Block on socket receive and wait for command from the
197 * session daemon. This will return if and only if there is a
198 * fatal error or the socket closes.
201 } catch (UnknownHostException uhe
) {
202 this.registerSem
.release();
203 System
.out
.println(uhe
);
204 } catch (IOException ioe
) {
205 this.registerSem
.release();
207 } catch (Exception e
) {
208 this.registerSem
.release();
214 public void destroy() {
216 this.eventTimer
.cancel();
219 if (this.sessiondSock
!= null) {
220 this.sessiondSock
.close();
222 } catch (Exception e
) {
228 * Receive header data from the session daemon using the LTTng command
229 * static buffer of the right size.
231 private void recvHeader() throws Exception
{
233 byte data
[] = new byte[this.headerCmd
.SIZE
];
235 read_len
= this.inFromSessiond
.read(data
, 0, data
.length
);
236 if (read_len
!= data
.length
) {
237 throw new IOException();
239 this.headerCmd
.populate(data
);
243 * Receive payload from the session daemon. This MUST be done after a
244 * recvHeader() so the header value of a command are known.
246 * The caller SHOULD use isPayload() before which returns true if a payload
247 * is expected after the header.
249 private byte[] recvPayload() throws Exception
{
250 byte payload
[] = new byte[(int) this.headerCmd
.data_size
];
252 /* Failsafe check so we don't waste our time reading 0 bytes. */
253 if (payload
.length
== 0) {
257 this.inFromSessiond
.read(payload
, 0, payload
.length
);
262 * Handle session command from the session daemon.
264 private void handleSessiondCmd() throws Exception
{
269 /* Get header from session daemon. */
272 if (headerCmd
.data_size
> 0) {
273 data
= recvPayload();
276 switch (headerCmd
.cmd
) {
279 LTTngSessiondCmd2_4
.sessiond_list_logger listLoggerCmd
=
280 new LTTngSessiondCmd2_4
.sessiond_list_logger();
281 listLoggerCmd
.execute(this.handler
);
282 data
= listLoggerCmd
.getBytes();
288 LTTngSessiondCmd2_4
.sessiond_enable_handler enableCmd
=
289 new LTTngSessiondCmd2_4
.sessiond_enable_handler();
291 enableCmd
.code
= LTTngSessiondCmd2_4
.lttng_jul_ret_code
.CODE_INVALID_CMD
;
294 enableCmd
.populate(data
);
295 event
= enableCmd
.execute(this.handler
, this.enabledLoggers
);
298 * Add the event to the set so it can be enabled if
299 * the logger appears at some point in time.
301 enabledEventSet
.add(event
);
303 data
= enableCmd
.getBytes();
308 LTTngSessiondCmd2_4
.sessiond_disable_handler disableCmd
=
309 new LTTngSessiondCmd2_4
.sessiond_disable_handler();
311 disableCmd
.code
= LTTngSessiondCmd2_4
.lttng_jul_ret_code
.CODE_INVALID_CMD
;
314 disableCmd
.populate(data
);
315 disableCmd
.execute(this.handler
);
316 data
= disableCmd
.getBytes();
322 ByteBuffer buf
= ByteBuffer
.wrap(data
);
323 buf
.order(ByteOrder
.BIG_ENDIAN
);
324 LTTngSessiondCmd2_4
.lttng_jul_ret_code code
=
325 LTTngSessiondCmd2_4
.lttng_jul_ret_code
.CODE_INVALID_CMD
;
326 buf
.putInt(code
.getCode());
331 /* Send payload to session daemon. */
332 this.outToSessiond
.write(data
, 0, data
.length
);
333 this.outToSessiond
.flush();
337 private void connectToSessiond() throws Exception
{
338 this.sessiondSock
= new Socket(this.sessiondHost
, this.sessiondPort
);
339 this.inFromSessiond
= new DataInputStream(
340 sessiondSock
.getInputStream());
341 this.outToSessiond
= new DataOutputStream(
342 sessiondSock
.getOutputStream());
345 private void registerToSessiond() throws Exception
{
346 byte data
[] = new byte[4];
347 ByteBuffer buf
= ByteBuffer
.wrap(data
);
348 String pid
= ManagementFactory
.getRuntimeMXBean().getName().split("@")[0];
350 buf
.putInt(Integer
.parseInt(pid
));
351 this.outToSessiond
.write(data
, 0, data
.length
);
352 this.outToSessiond
.flush();