Spring Boot中使用log4j實現http請求日誌入mongodb
阿新 • • 發佈:2018-12-19
一 問題提出
當我們在叢集中部署應用之後,應用請求的日誌被分散記錄在了不同應用伺服器的檔案系統上,這樣分散的儲存並不利於我們對日誌內容的檢索。解決日誌分散問題的方案多種多樣,本篇的解決方案是:擴充套件log4j實現將日誌寫入MongoDB。
二 實戰
1 新建依賴
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mongodb</groupId> <artifactId>mongodb-driver</artifactId> <version>3.2.2</version> </dependency> </dependencies>
2 自定義appender
log4j提供的輸出器實現自Appender介面,要自定義appender輸出到MongoDB,只需要繼承AppenderSkeleton類,並實現幾個方法即可完成。
package com.didispace.log; import com.mongodb.BasicDBObject; import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.spi.LoggingEvent; public class MongoAppender extends AppenderSkeleton { // 定義MongoDB的連線和操作物件,根據log4j.properties配置的引數初始化 private MongoClient mongoClient; // mongodb的連線客戶端 private MongoDatabase mongoDatabase; // 記錄日誌的資料庫 private MongoCollection<BasicDBObject> logsCollection; // 記錄日誌的集合 // 定義MongoDB的配置引數,可通過log4j.properties配置 private String connectionUrl; // 連線mongodb的串 private String databaseName; // 資料庫名 private String collectionName; // 集合名 /* 根據log4j.properties中的配置建立mongodb連線 LoggingEvent提供getMessage()函式來獲取日誌訊息 往配置的記錄日誌的collection中插入日誌訊息 */ // 重寫append函式 @Override protected void append(LoggingEvent loggingEvent) { if(mongoDatabase == null) { MongoClientURI connectionString = new MongoClientURI(connectionUrl); mongoClient = new MongoClient(connectionString); mongoDatabase = mongoClient.getDatabase(databaseName); logsCollection = mongoDatabase.getCollection(collectionName, BasicDBObject.class); } logsCollection.insertOne((BasicDBObject) loggingEvent.getMessage()); } // 重寫close函式:關閉mongodb @Override public void close() { if(mongoClient != null) { mongoClient.close(); } } @Override public boolean requiresLayout() { return false; } public String getConnectionUrl() { return connectionUrl; } public void setConnectionUrl(String connectionUrl) { this.connectionUrl = connectionUrl; } public String getDatabaseName() { return databaseName; } public void setDatabaseName(String databaseName) { this.databaseName = databaseName; } public String getCollectionName() { return collectionName; } public void setCollectionName(String collectionName) { this.collectionName = collectionName; } }
3 配置log4j.properties
# LOG4J配置 log4j.rootCategory=INFO, stdout log4j.logger.mongodb=INFO, mongodb # 控制檯輸出 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L - %m%n # mongodb輸出 log4j.appender.mongodb=com.didispace.log.MongoAppender log4j.appender.mongodb.connectionUrl=mongodb://localhost:27017 log4j.appender.mongodb.databaseName=logs log4j.appender.mongodb.collectionName=logs_request
4 Web層日誌切面定義
package com.didispace.aspect;
import com.mongodb.BasicDBObject;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
@Aspect
@Order(1)
@Component
public class WebLogAspect {
//logger取名為mongodb
private Logger logger = Logger.getLogger("mongodb");
@Pointcut("execution(public * com.didispace.web..*.*(..))")
public void webLog(){}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 獲取HttpServletRequest
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 獲取要記錄的日誌內容
BasicDBObject logInfo = getBasicDBObject(request, joinPoint);
//輸出BasicDBObject物件的資訊到mongodb
logger.info(logInfo);
}
//通過getBasicDBObject函式從HttpServletRequest和JoinPoint物件中獲取請求資訊,並組裝成BasicDBObject
private BasicDBObject getBasicDBObject(HttpServletRequest request, JoinPoint joinPoint) {
// 基本資訊
BasicDBObject r = new BasicDBObject();
r.append("requestURL", request.getRequestURL().toString());
r.append("requestURI", request.getRequestURI());
r.append("queryString", request.getQueryString());
r.append("remoteAddr", request.getRemoteAddr());
r.append("remoteHost", request.getRemoteHost());
r.append("remotePort", request.getRemotePort());
r.append("localAddr", request.getLocalAddr());
r.append("localName", request.getLocalName());
r.append("method", request.getMethod());
r.append("headers", getHeadersInfo(request));
r.append("parameters", request.getParameterMap());
r.append("classMethod", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
r.append("args", Arrays.toString(joinPoint.getArgs()));
return r;
}
/**
* getHeadersInfo函式從HttpServletRequest中獲取header資訊
*
* @param request
* @return
*/
private Map<String, String> getHeadersInfo(HttpServletRequest request) {
Map<String, String> map = new HashMap<>();
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = (String) headerNames.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
return map;
}
}
5 定義啟動類
package com.didispace;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
6 控制器
package com.didispace.web;
import org.springframework.web.bind.annotation.*;
@RestController
public class HelloController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
@ResponseBody
public String hello(@RequestParam String name) {
return "Hello " + name;
}
}
三 測試
1 瀏覽器輸入
2 控制檯輸出
package com.didispace.web;
import org.springframework.web.bind.annotation.*;
@RestController
public class HelloController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
@ResponseBody
public String hello(@RequestParam String name) {
return "Hello " + name;
}
}
3 mongodb產生的資料