This commit is contained in:
gotoeasy 2023-04-25 12:52:54 +08:00
parent b03ff7b39a
commit ec5b41977f
3 changed files with 665 additions and 0 deletions

View File

@ -0,0 +1,181 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>top.gotoeasy</groupId>
<artifactId>glc-logback-appender</artifactId>
<version>0.9.0</version>
<description>logback appender for glogcenter</description>
<repositories>
<repository>
<id>aliyun-central</id>
<name>aliyun-central Repository</name>
<url>https://maven.aliyun.com/repository/central</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>sonatype-snapshot</id>
<name>sonatype-snapshot Repository</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
<name>gotoeasy-aop</name>
<url>https://gitlab.gotoeasy.top/gotoeasy/framework.git</url>
<licenses>
<license>
<name>GNU Lesser General Public Licence</name>
<url>http://www.gnu.org/licenses/lgpl.txt</url>
</license>
</licenses>
<developers>
<developer>
<name>青松</name>
<email>gotoeasy@163.com</email>
<organization>top.gotoeasy</organization>
</developer>
</developers>
<scm>
<connection>scm:https://gitlab.gotoeasy.top/gotoeasy/framework.git</connection>
<developerConnection>scm:https://gitlab.gotoeasy.top/gotoeasy/framework.git</developerConnection>
<url>https://gitlab.gotoeasy.top/gotoeasy/framework.git</url>
<tag>${project.version}</tag>
</scm>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java-version>1.8</java-version>
</properties>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.15.0</version>
</dependency>
</dependencies>
<profiles>
<profile>
<id>release</id>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/*.klass</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources/META-INF</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>**/*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<!-- Source -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Javadoc -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<distributionManagement>
<snapshotRepository>
<id>sonatype</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>sonatype</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
</profile>
</profiles>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,264 @@
package top.gotoeasy.framework.glc.logback.appender;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import ch.qos.logback.core.Layout;
/**
* GLC是glogcenter缩写一个golang实现的日志中心<br>
* GlcAmqpAppender提供一种发送日志数据到RabbitMQ的方式GLC则从RabbitMQ接收日志适用于使用logback做日志管理的java项目
*/
public class GlcAmqpAppender extends AppenderBase<ILoggingEvent> {
// 自定义配置需Getter和Setter方法
private String amqpHost;
private int amqpPort;
private String amqpUser;
private String amqpPassword;
private String system;
private Layout<ILoggingEvent> layout;
private int cnt = 0;
private boolean enableGlc = true;
private ExecutorService executor = Executors.newSingleThreadExecutor();
protected Connection connection = null;
protected Channel channel = null;
protected synchronized void initConnectionChannel() throws Exception {
if (this.channel != null) {
return;
}
Connection conn = null;
Channel chan = null;
try {
ConnectionFactory factory = new ConnectionFactory(); // 创建一个连接工厂
factory.setHost(amqpHost); // 工厂ip 连接rabbitmq的队列
factory.setPort(amqpPort); // 端口
factory.setUsername(amqpUser); // 用户名
factory.setPassword(amqpPassword); // 密码
conn = factory.newConnection(); // 创建连接
chan = conn.createChannel(); // 获取信道
// 对列名称
// durable 是否持久化数据
// exclusive 排他性权限私有
// autoDelete 是否自动删除
// arguments
chan.queueDeclare("glc-log-queue", false, false, false, null);
} finally {
this.connection = conn;
this.channel = chan;
}
}
@Override
protected void append(ILoggingEvent event) {
if (!enableGlc) {
return; // 未启用时跳过
}
if (event == null || !isStarted()) {
if (cnt++ < 10) {
System.err.println("日志事件为空或该Appender未被初始化");
}
return;
}
// 异步发送日志
executor.execute(() -> {
sendToRabbitMQ(layout.doLayout(event));
});
}
/**
* 发送日志到RabbitMQ<br>
*
* @param text 日志
*/
protected void sendToRabbitMQ(String text) {
try {
if (channel == null) {
initConnectionChannel();
}
String body = "{" + encodeStr("text") + ":" + encodeStr(text.trim());
body += "," + encodeStr("date") + ":" + encodeStr(getDateString());
body += "," + encodeStr("system") + ":" + encodeStr(getSystem());
body += "}";
channel.basicPublish("", "glc-log-queue", null, body.getBytes("utf-8"));
} catch (Exception e) {
if (cnt++ < 10) {
e.printStackTrace();
}
resetConnectionChannel();
}
}
@Override
public void start() {
if (this.layout == null) {
System.err.println("Layout未被初始化");
}
super.start();
// 优先使用环境变量设定
String enable = System.getenv("GLC_ENABLE");
if ("false".equalsIgnoreCase(enable) || "0".equals(enable)) {
enableGlc = false;
}
String host = System.getenv("GLC_AMQP_HOST");
if (host != null) {
host = host.trim();
if (!"".equals(host)) {
setAmqpHost(host);
}
}
String port = System.getenv("GLC_AMQP_PORT");
if (port != null) {
port = port.trim();
if (!"".equals(port)) {
setAmqpPort(Integer.valueOf(port));
}
}
String user = System.getenv("GLC_AMQP_USER");
if (user != null) {
user = user.trim();
if (!"".equals(user)) {
setAmqpUser(user);
}
}
String password = System.getenv("GLC_AMQP_PASSWORD");
if (password != null) {
password = password.trim();
if (!"".equals(password)) {
setAmqpPassword(password);
}
}
String system = System.getenv("GLC_SYSTEM");
if (system != null) {
system = system.trim();
if (!"".equals(system)) {
setSystem(system);
}
}
}
@Override
public void stop() {
if (!isStarted()) {
return;
}
super.stop();
}
public String getAmqpHost() {
return amqpHost;
}
public void setAmqpHost(String amqpHost) {
this.amqpHost = amqpHost;
}
public int getAmqpPort() {
return amqpPort;
}
public void setAmqpPort(int amqpPort) {
this.amqpPort = amqpPort;
}
public String getAmqpUser() {
return amqpUser;
}
public void setAmqpUser(String amqpUser) {
this.amqpUser = amqpUser;
}
public String getAmqpPassword() {
return amqpPassword;
}
public void setAmqpPassword(String amqpPassword) {
this.amqpPassword = amqpPassword;
}
public Layout<ILoggingEvent> getLayout() {
return layout;
}
public void setLayout(Layout<ILoggingEvent> layout) {
this.layout = layout;
}
public void setSystem(String system) {
this.system = system;
}
public String getSystem() {
return system == null ? "" : system;
}
protected synchronized void resetConnectionChannel() {
try {
if (channel != null) {
channel.close();
}
} catch (Exception ex) {
// ignore
} finally {
this.channel = null;
}
try {
if (connection != null) {
connection.close();
}
} catch (Exception ex) {
// ignore
} finally {
this.connection = null;
}
}
private String encodeStr(String str) {
return "\"" + str.replaceAll("\"", "\\\\\"").replaceAll("\t", "\\\\t").replaceAll("\r", "\\\\r")
.replaceAll("\n", "\\\\n") + "\"";
}
private static String getDateString() {
SimpleDateFormat sdf = getSimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
return sdf.format(new Date());
}
private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>();
private static Object lockObject = new Object();
private static SimpleDateFormat getSimpleDateFormat(String format) {
SimpleDateFormat simpleDateFormat = threadLocal.get();
if (simpleDateFormat == null) {
synchronized (lockObject) {
if (simpleDateFormat == null) {
simpleDateFormat = new SimpleDateFormat(format);
simpleDateFormat.setLenient(false);
threadLocal.set(simpleDateFormat);
}
}
}
simpleDateFormat.applyPattern(format);
return simpleDateFormat;
}
}

View File

@ -0,0 +1,220 @@
package top.gotoeasy.framework.glc.logback.appender;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import ch.qos.logback.core.Layout;
/**
* GLC是glogcenter缩写一个golang实现的日志中心<br>
* GlcHttpJsonAppender提供一种http提交Json数据的方式发送日志到GLC适用于使用logback做日志管理的java项目<br>
* 仅简易实现为主若是性能要求高日志量大的场景应选用其他比如消息队列之类的Appender
*/
public class GlcHttpJsonAppender extends AppenderBase<ILoggingEvent> {
// 自定义配置需Getter和Setter方法
private String glcApiUrl;
private String glcApiKey;
private String system = "default";
private Layout<ILoggingEvent> layout;
private String headerKey;
private String headerVal;
private int cnt = 0;
private boolean enableGlc = true;
private ExecutorService executor = Executors.newSingleThreadExecutor();
@Override
protected void append(ILoggingEvent event) {
if (!enableGlc) {
return; // 未启用时跳过
}
if (event == null || !isStarted()) {
if (cnt++ < 10) {
System.err.println("日志事件为空或该Appender未被初始化");
}
return;
}
// 异步发送日志到GLC
executor.execute(() -> {
submitToGlogCenter(layout.doLayout(event));
});
}
/**
* 发送日志到GLC<br>
* 为不依赖第三方包仅作java原生包简单实现性能较差<br>
* 实际使用时若有性能问题可继承重写实现
*
* @param text 日志
*/
protected void submitToGlogCenter(String text) {
DataOutputStream dos = null;
try {
String body = "{" + encodeStr("text") + ":" + encodeStr(text.trim());
body += "," + encodeStr("date") + ":" + encodeStr(getDateString());
body += "," + encodeStr("system") + ":" + encodeStr(getSystem());
body += "}";
URL url = new URL(glcApiUrl);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
// 设置header
if (headerKey != null && !"".equals(headerVal)) {
connection.setRequestProperty(headerKey, headerVal);
}
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
// 发送日志数据
connection.connect();
dos = new DataOutputStream(connection.getOutputStream());
dos.write(body.getBytes("utf-8"));
dos.flush();
// 接收响应内筒
connection.getContent();
connection.disconnect();
} catch (Exception e) {
if (cnt++ < 10) {
System.err.println("[GLC日志发送异常][地址:" + glcApiUrl + "][异常信息:" + e.getMessage() + "]");
}
} finally {
try {
if (dos != null) {
dos.close();
}
} catch (Exception e) {
// ignore
}
}
}
@Override
public void start() {
if (this.layout == null) {
System.err.println("Layout未被初始化");
}
super.start();
// 优先使用环境变量设定
String enable = System.getenv("GLC_ENABLE");
if ("false".equalsIgnoreCase(enable) || "0".equals(enable)) {
enableGlc = false;
}
String apiUrl = System.getenv("GLC_API_URL");
if (apiUrl != null) {
apiUrl = apiUrl.trim();
if (!"".equals(apiUrl)) {
setGlcApiUrl(apiUrl);
}
}
String apiKey = System.getenv("GLC_API_KEY");
if (apiKey != null) {
apiKey = apiKey.trim();
if (!"".equals(apiKey)) {
setGlcApiKey(apiKey);
}
}
String system = System.getenv("GLC_SYSTEM");
if (system != null) {
system = system.trim();
if (!"".equals(system)) {
setSystem(system);
}
}
}
@Override
public void stop() {
if (!isStarted()) {
return;
}
super.stop();
}
public Layout<ILoggingEvent> getLayout() {
return layout;
}
public void setLayout(Layout<ILoggingEvent> layout) {
this.layout = layout;
}
public String GetGlcApiUrl() {
return glcApiUrl;
}
public void setGlcApiUrl(String glcApiUrl) {
this.glcApiUrl = glcApiUrl;
}
public String GetGlcApiKey() {
return glcApiKey;
}
public void setSystem(String system) {
this.system = system;
}
public String getSystem() {
return system == null ? "" : system;
}
public void setGlcApiKey(String glcApiKey) {
this.glcApiKey = glcApiKey;
String key = glcApiKey.split(":")[0];
String value = glcApiKey.substring(key.length() + 1).trim();
key = key.trim();
if (!"".equals(key)) {
headerKey = key;
headerVal = value;
}
}
private String encodeStr(String str) {
return "\"" + str.replaceAll("\"", "\\\\\"").replaceAll("\t", "\\\\t").replaceAll("\r", "\\\\r")
.replaceAll("\n", "\\\\n") + "\"";
}
private static String getDateString() {
SimpleDateFormat sdf = getSimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
return sdf.format(new Date());
}
private static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>();
private static Object lockObject = new Object();
private static SimpleDateFormat getSimpleDateFormat(String format) {
SimpleDateFormat simpleDateFormat = threadLocal.get();
if (simpleDateFormat == null) {
synchronized (lockObject) {
if (simpleDateFormat == null) {
simpleDateFormat = new SimpleDateFormat(format);
simpleDateFormat.setLenient(false);
threadLocal.set(simpleDateFormat);
}
}
}
simpleDateFormat.applyPattern(format);
return simpleDateFormat;
}
}