Skip to content

Commit ec6fb65

Browse files
Support "RFC 6587 - Transmission of Syslog Messages over TCP"
1 parent 887fa43 commit ec6fb65

File tree

5 files changed

+487
-0
lines changed

5 files changed

+487
-0
lines changed

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# Syslog Java Client
22

3+
4+
## Sample UDP sender using RFC 3614
5+
6+
```java
7+
8+
// Initialise sender
9+
UdpSyslogMessageSender messageSender = new UdpSyslogMessageSender();
10+
messageSender.setDefaultMessageHostName("myhostname"); // some syslog cloud services may use this field to transmit a secret key
11+
messageSender.setDefaultAppName("myapp");
12+
messageSender.setDefaultFacility(Facility.USER);
13+
messageSender.setDefaultSeverity(Severity.INFORMATIONAL);
14+
messageSender.setSyslogServerHostname("127.0.0.1");
15+
messageSender.setSyslogServerPort(1234);
16+
messageSender.setMessageFormat(MessageFormat.RFC_3164); // optional, default is RFC 3164
17+
18+
19+
// send a Syslog message
20+
messageSender.sendMessage("This is a test message");
21+
```
22+
23+
## Sample UDP sender using RFC 3614
24+
325
```java
426

527
// Initialise sender
@@ -10,6 +32,45 @@ messageSender.setDefaultFacility(Facility.USER);
1032
messageSender.setDefaultSeverity(Severity.INFORMATIONAL);
1133
messageSender.setSyslogServerHostname("127.0.0.1");
1234
messageSender.setSyslogServerPort(1234);
35+
messageSender.setMessageFormat(MessageFormat.RFC_5424);
36+
37+
// send a Syslog message
38+
messageSender.sendMessage("This is a test message");
39+
```
40+
41+
## Sample TCP sender using RFC 3614
42+
43+
```java
44+
45+
// Initialise sender
46+
TcpSyslogMessageSender messageSender = new TcpSyslogMessageSender();
47+
messageSender.setDefaultMessageHostName("myhostname"); // some syslog cloud services may use this field to transmit a secret key
48+
messageSender.setDefaultAppName("myapp");
49+
messageSender.setDefaultFacility(Facility.USER);
50+
messageSender.setDefaultSeverity(Severity.INFORMATIONAL);
51+
messageSender.setSyslogServerHostname("127.0.0.1");
52+
messageSender.setSyslogServerPort(1234);
53+
messageSender.setMessageFormat(MessageFormat.RFC_3614); // optional, default is RFC 3164
54+
messageSender.setSsl(false);
55+
56+
// send a Syslog message
57+
messageSender.sendMessage("This is a test message");
58+
```
59+
60+
## Sample TCP over SSL sender using RFC 3614
61+
62+
```java
63+
64+
// Initialise sender
65+
TcpSyslogMessageSender messageSender = new TcpSyslogMessageSender();
66+
messageSender.setDefaultMessageHostName("myhostname"); // some syslog cloud services may use this field to transmit a secret key
67+
messageSender.setDefaultAppName("myapp");
68+
messageSender.setDefaultFacility(Facility.USER);
69+
messageSender.setDefaultSeverity(Severity.INFORMATIONAL);
70+
messageSender.setSyslogServerHostname("127.0.0.1");
71+
messageSender.setSyslogServerPort(1234);
72+
messageSender.setMessageFormat(MessageFormat.RFC_3614); // optional, default is RFC 3164
73+
messageSender.setSsl(true);
1374

1475
// send a Syslog message
1576
messageSender.sendMessage("This is a test message");
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
* Copyright 2010-2014, CloudBees Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.cloudbees.syslog.sender;
17+
18+
import com.cloudbees.syslog.SyslogMessage;
19+
import com.cloudbees.syslog.util.CachingReference;
20+
import com.cloudbees.syslog.util.IoUtils;
21+
22+
import javax.annotation.Nonnull;
23+
import javax.annotation.Nullable;
24+
import javax.net.SocketFactory;
25+
import javax.net.ssl.SSLSession;
26+
import javax.net.ssl.SSLSocket;
27+
import javax.net.ssl.SSLSocketFactory;
28+
import java.io.BufferedWriter;
29+
import java.io.IOException;
30+
import java.io.OutputStreamWriter;
31+
import java.io.Writer;
32+
import java.math.BigInteger;
33+
import java.net.*;
34+
import java.security.cert.Certificate;
35+
import java.security.cert.X509Certificate;
36+
import java.util.logging.Level;
37+
38+
/**
39+
* See <a href="http://tools.ietf.org/html/rfc6587">RFC 6587 - Transmission of Syslog Messages over TCP</a>
40+
*
41+
* @author <a href="mailto:[email protected]">Cyrille Le Clerc</a>
42+
*/
43+
public class TcpSyslogMessageSender extends AbstractSyslogMessageSender {
44+
public final static int SETTING_SOCKET_CONNECT_TIMEOUT_IN_MILLIS_DEFAULT_VALUE = 500;
45+
46+
/**
47+
* {@link java.net.InetAddress InetAddress} of the remote Syslog Server.
48+
* <p/>
49+
* The {@code InetAddress} is refreshed regularly to handle DNS changes (default {@link #DEFAULT_INET_ADDRESS_TTL_IN_MILLIS})
50+
* <p/>
51+
* Default value: {@link #DEFAULT_SYSLOG_HOST}
52+
*/
53+
protected CachingReference<InetAddress> syslogServerHostnameReference;
54+
/**
55+
* Listen port of the remote Syslog server.
56+
* <p/>
57+
* Default: {@link #DEFAULT_SYSLOG_PORT}
58+
*/
59+
protected int syslogServerPort = DEFAULT_SYSLOG_PORT;
60+
61+
private Socket socket;
62+
private Writer writer;
63+
private int socketConnectTimeoutInMillis = SETTING_SOCKET_CONNECT_TIMEOUT_IN_MILLIS_DEFAULT_VALUE;
64+
private boolean ssl;
65+
66+
67+
@Override
68+
public synchronized void sendMessage(@Nonnull SyslogMessage message) throws IOException {
69+
ensureSyslogServerConnection();
70+
sendCounter.incrementAndGet();
71+
long nanosBefore = System.nanoTime();
72+
73+
try {
74+
if (logger.isLoggable(Level.FINEST)) {
75+
logger.finest("Send syslog message " + message.toSyslogMessage(messageFormat));
76+
}
77+
message.toSyslogMessage(messageFormat, writer);
78+
// use the CR LF non transparent framing as described in "3.4.2. Non-Transparent-Framing"
79+
writer.write("\r\n");
80+
writer.flush();
81+
} catch (IOException e) {
82+
sendErrorCounter.incrementAndGet();
83+
throw e;
84+
} catch (RuntimeException e) {
85+
sendErrorCounter.incrementAndGet();
86+
throw e;
87+
} finally {
88+
sendDurationInNanosCounter.addAndGet(System.nanoTime() - nanosBefore);
89+
}
90+
}
91+
92+
private synchronized void ensureSyslogServerConnection() throws IOException {
93+
InetAddress inetAddress = syslogServerHostnameReference.get();
94+
if (socket != null && !socket.getInetAddress().equals(inetAddress)) {
95+
logger.info("InetAddress of the Syslog Server have changed, create a new connection. " +
96+
"Before=" + socket.getInetAddress() + ", new=" + inetAddress);
97+
IoUtils.closeQuietly(socket, writer);
98+
writer = null;
99+
socket = null;
100+
}
101+
boolean socketIsValid;
102+
try {
103+
socketIsValid = socket != null &&
104+
socket.isConnected()
105+
&& socket.isBound()
106+
&& !socket.isClosed()
107+
&& !socket.isInputShutdown()
108+
&& !socket.isOutputShutdown();
109+
} catch (Exception e) {
110+
socketIsValid = false;
111+
}
112+
if (!socketIsValid) {
113+
writer = null;
114+
try {
115+
if (ssl) {
116+
socket = SSLSocketFactory.getDefault().createSocket();
117+
} else {
118+
socket = SocketFactory.getDefault().createSocket();
119+
}
120+
socket.setKeepAlive(true);
121+
socket.connect(
122+
new InetSocketAddress(inetAddress, syslogServerPort),
123+
socketConnectTimeoutInMillis);
124+
125+
if (socket instanceof SSLSocket && logger.isLoggable(Level.FINER)) {
126+
try {
127+
SSLSocket sslSocket = (SSLSocket) socket;
128+
SSLSession session = sslSocket.getSession();
129+
logger.finer("The Certificates used by peer");
130+
for (Certificate certificate : session.getPeerCertificates()) {
131+
if (certificate instanceof X509Certificate) {
132+
X509Certificate x509Certificate = (X509Certificate) certificate;
133+
logger.finer("" + x509Certificate.getSubjectDN());
134+
} else {
135+
logger.finer("" + certificate);
136+
}
137+
}
138+
logger.finer("Peer host is " + session.getPeerHost());
139+
logger.finer("Cipher is " + session.getCipherSuite());
140+
logger.finer("Protocol is " + session.getProtocol());
141+
logger.finer("ID is " + new BigInteger(session.getId()));
142+
logger.finer("Session created in " + session.getCreationTime());
143+
logger.finer("Session accessed in " + session.getLastAccessedTime());
144+
} catch (Exception e) {
145+
logger.warn("Exception dumping debug info for " + socket, e);
146+
}
147+
}
148+
} catch (IOException e) {
149+
ConnectException ce = new ConnectException("Exception connecting to " + inetAddress + ":" + syslogServerPort);
150+
ce.initCause(e);
151+
throw ce;
152+
}
153+
}
154+
if (writer == null) {
155+
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), UTF_8));
156+
}
157+
}
158+
159+
public void setSyslogServerHostname(final String syslogServerHostname) {
160+
this.syslogServerHostnameReference = new CachingReference<InetAddress>(DEFAULT_INET_ADDRESS_TTL_IN_NANOS) {
161+
@Nullable
162+
@Override
163+
protected InetAddress newObject() {
164+
try {
165+
return InetAddress.getByName(syslogServerHostname);
166+
} catch (UnknownHostException e) {
167+
throw new IllegalStateException(e);
168+
}
169+
}
170+
};
171+
}
172+
173+
public void setSyslogServerPort(int syslogServerPort) {
174+
this.syslogServerPort = syslogServerPort;
175+
}
176+
177+
public String getSyslogServerHostname() {
178+
return syslogServerHostnameReference.get().getHostName();
179+
}
180+
181+
public int getSyslogServerPort() {
182+
return syslogServerPort;
183+
}
184+
185+
public boolean isSsl() {
186+
return ssl;
187+
}
188+
189+
public void setSsl(boolean ssl) {
190+
this.ssl = ssl;
191+
}
192+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2010-2014, CloudBees Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.cloudbees.syslog.util;
17+
18+
import javax.annotation.Nullable;
19+
import java.io.IOException;
20+
import java.io.Writer;
21+
import java.net.Socket;
22+
23+
/**
24+
* @author <a href="mailto:[email protected]">Cyrille Le Clerc</a>
25+
*/
26+
public class IoUtils {
27+
private IoUtils() {
28+
29+
}
30+
31+
public static void closeQuietly(@Nullable Socket socket) {
32+
try {
33+
if (socket != null && !socket.isClosed()) {
34+
socket.close();
35+
}
36+
} catch (Exception e) {
37+
}
38+
}
39+
40+
/**
41+
* Note: does not {@link java.io.Writer#flush()} before closing.
42+
*
43+
* @param socket
44+
* @param writer
45+
*/
46+
public static void closeQuietly(@Nullable Socket socket, @Nullable Writer writer) {
47+
if (writer != null) {
48+
try {
49+
writer.close();
50+
} catch (IOException e) {
51+
52+
}
53+
}
54+
closeQuietly(socket);
55+
}
56+
}

0 commit comments

Comments
 (0)