【Syslog】采用Syslog4j发送总线日志

简介

Syslog4j是一个实现Syslog(RFC3164)协议的Java开源类库包括客户端与服务器端。通过 UDP/IP, TCP/IP, TCP/IPover SSL/TLS, Unix Syslog, 和 Unix Socket 等协议进行日志记录。
本文使用UDP协议发送日志给总线(这里说的总线只是我们公司有个平台专门管理这些日志)

Maven依赖

		<!-- 具体版本可以自己去Maven中央仓库去寻找 -->
 		<dependency>
            <groupId>org.graylog2</groupId>
            <artifactId>syslog4j</artifactId>
            <version>0.9.60</version>
        </dependency>

UDP 模式

搭建日志接收服务端

import org.graylog2.syslog4j.SyslogConstants;
import org.graylog2.syslog4j.server.*;

import java.net.SocketAddress;

public class MySyslogServer {
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 32376;

    private void receiveSyslogMessage() throws InterruptedException {
        SyslogServerIF server = SyslogServer.getInstance(SyslogConstants.UDP);
        SyslogServerConfigIF config = server.getConfig();
        config.setHost(HOST);
        config.setPort(PORT);
        config.addEventHandler(new SyslogServerSessionEventHandlerIF() {
            @Override
            public Object sessionOpened(SyslogServerIF syslogServerIF, SocketAddress socketAddress) {
                return null;
            }

            @Override
            public void event(Object o, SyslogServerIF syslogServerIF, SocketAddress socketAddress, SyslogServerEventIF syslogServerEventIF) {
                System.out.println("receive from:" + socketAddress + "message" + syslogServerEventIF.getMessage());
            }

            @Override
            public void exception(Object o, SyslogServerIF syslogServerIF, SocketAddress socketAddress, Exception e) {
            }

            @Override
            public void sessionClosed(Object o, SyslogServerIF syslogServerIF, SocketAddress socketAddress, boolean b) {
            }

            @Override
            public void initialize(SyslogServerIF syslogServerIF) {
            }

            @Override
            public void destroy(SyslogServerIF syslogServerIF) {
            }
        });
        SyslogServer.getThreadedInstance(SyslogConstants.UDP);
        Thread.sleep(100000);
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Syslog Server is start.");
        new MySyslogServer().receiveSyslogMessage();
    }
}

搭建日志发送客户端

package cn.com.mcd.data.syslog;

import com.alibaba.fastjson.JSONObject;
import org.graylog2.syslog4j.Syslog;
import org.graylog2.syslog4j.SyslogConstants;
import org.graylog2.syslog4j.SyslogIF;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

public class MySyslogClient {
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 32376;

    public void generate() {
        SyslogIF syslog = Syslog.getInstance(SyslogConstants.UDP);
        syslog.getConfig().setHost(HOST);
        syslog.getConfig().setPort(PORT);
        try {
        	// 这里就是发送日志,当前它的API接口有很多种,都可以尝试一样
        	// 参数: 10  ----> 表示日志等级  当一个单体应用中,存在多个任务发送日志,这时候级别的作用就体现出现了
        	// 		   URLDecoder.decode("测试发送"+ System.currentTimeMillis(), "UTF-8")  表示发送内容以及编码格式
            syslog.log(10, URLDecoder.decode("测试发送"+ System.currentTimeMillis(), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            System.out.println("generate log get exception " + e);
        }
    }

    public static void main(String[] args) {
        new MySyslogClient().generate();
    }
}

在这里插入图片描述

TCP 模式

搭建日志接收服务端

import org.graylog2.syslog4j.SyslogConstants;
import org.graylog2.syslog4j.server.*;

import java.net.SocketAddress;

public class MySyslogServer {
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 32376;

    private void receiveSyslogMessage() throws InterruptedException {
        SyslogServerIF server = SyslogServer.getInstance(SyslogConstants.TCP);
        SyslogServerConfigIF config = server.getConfig();
        config.setHost(HOST);
        config.setPort(PORT);
        config.addEventHandler(new SyslogServerSessionEventHandlerIF() {
            @Override
            public Object sessionOpened(SyslogServerIF syslogServerIF, SocketAddress socketAddress) {
                return null;
            }

            @Override
            public void event(Object o, SyslogServerIF syslogServerIF, SocketAddress socketAddress, SyslogServerEventIF syslogServerEventIF) {
                System.out.println("receive from:" + socketAddress + "message" + syslogServerEventIF.getMessage());
            }

            @Override
            public void exception(Object o, SyslogServerIF syslogServerIF, SocketAddress socketAddress, Exception e) {
            }

            @Override
            public void sessionClosed(Object o, SyslogServerIF syslogServerIF, SocketAddress socketAddress, boolean b) {
            }

            @Override
            public void initialize(SyslogServerIF syslogServerIF) {
            }

            @Override
            public void destroy(SyslogServerIF syslogServerIF) {
            }
        });
        SyslogServer.getThreadedInstance(SyslogConstants.TCP);
        Thread.sleep(100000);
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Syslog Server is start.");
        new MySyslogServer().receiveSyslogMessage();
    }
}

搭建日志发送客户端

import com.alibaba.fastjson.JSONObject;
import org.graylog2.syslog4j.Syslog;
import org.graylog2.syslog4j.SyslogConstants;
import org.graylog2.syslog4j.SyslogIF;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

public class MySyslogClient {
    private static final String HOST = "127.0.0.1";
    private static final int PORT = 32376;

    public void generate() {
        SyslogIF syslog = Syslog.getInstance(SyslogConstants.TCP);
        syslog.getConfig().setHost(HOST);
        syslog.getConfig().setPort(PORT);
        try {
        	// 这里就是发送日志,当前它的API接口有很多种,都可以尝试一样
        	// 参数: 10  ----> 表示日志等级  当一个单体应用中,存在多个任务发送日志,这时候级别的作用就体现出现了
        	// 		   URLDecoder.decode("测试发送"+ System.currentTimeMillis(), "UTF-8")  表示发送内容以及编码格式
            syslog.log(10, URLDecoder.decode("测试发送"+ System.currentTimeMillis(), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            System.out.println("generate log get exception " + e);
        }
        // 切记这里要加上,因为TCP协议的长链接(也就是握手特点)
        Thread.sleep(10000)
    }

    public static void main(String[] args) {
        new MySyslogClient().generate();
    }
}

使用注意事项

UDP 发送设置报文大小问题

UDP发送信息,默认数据体不能超过1k,不然就会被默认拆包,因为本公司业务场景中会把特定时间的哪些用户执行哪些SQL(SQL估摸着至少4000个字符串)发送给总线,这时候就会出现以下情况:
在这里插入图片描述
4kb的数据被拆分为了5个包,会导致接收端出现拼接符号…
在这里插入图片描述
这里给出的解决方案设置报文大小

// 实例化Syslog的时候
SyslogIF syslog = Syslog.getInstance(SyslogConstants.UDP);
syslog.getConfig().setHost(HOST);
syslog.getConfig().setPort(PORT);
syslog.getConfig().setMaxMessageLength(65507); // 64KB

因为安全规则以及职业道德,这里就不展示修改为50MB发送日志的成功场景,大家可以依据自己的业务范围设置大小发送。

乱码问题

第一玩着这个功能的时候,没有设置编码格式,导致中文字符(GBK)到服务器端接收日志的时候,出现乱码

URLDecoder.decode(String var)

使用以上方法可以规避这些问题

后续问题会持续录入

MaxMessageLength 有限制吗?

有,64KB就是最大限度,这个限制不是Syslog4j做的限制,是底层DatagramPacket.java的限制。超过64KB就会拆包,也就截断报文分多次发送。如果你直接给个50MB的限制,底层就会抛出异常,最恶心的就是Syslog4j捕获了异常,但是没有跑出来,并且出现异常后会重新实例化发送。
正常来说,假设你的内容是70kb,但是你想一次性全部发出去,你把MaxMessageLength设置到大于64KB,底层会出现这个异常
在这里插入图片描述

TCP发送拆分发送

在这里插入图片描述
这里的报文分行分送,后来发现我发送的文本就是分行的,因此,我们只要对读出字符流String,通过正则替换成整行,就可以直接一个包发过去了,前提也是需要配置MaxMessageLength的属性值大小

// 无论你是读文件,还是JSON序列化对象,只要是字符串就可以了,但是保不齐的来源存在换行符 因此需要借助下面的正则来替换成单行
String modelStr  = ""; 
String reg = "\\s+";
modelStr = modelStr.replaceAll(reg, " ");

总结

建议大家在使用这个工具的时候,下载一个WireShark这一类抓包软件,可以进行本地抓包,用来判断是否发送出去了,以及发送的时候出现的问题,例如UDP报文大小,我一开始也是不知道,后来总线那边的同事提醒我了,我也观察到了那些省略号分布很规律,就意识到大事不妙了,但是控制台打印的日志是完整的,也就说在传输的过程中肯定是出了什么问题,无论是TCP还是UDP都已经在计算机的传输层了,我们是看不到它们的流程的,这时候抓包工具就至关重要了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我的钱包空指针了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值