- 爲了處理不同事件類型的日誌,我們將不同業務含義的日誌抽象成了日誌事件,具體如下:
public enum EventType { normal(Constants.EVENT_TYPE_NORMAL, "正常入庫日誌"), invoke_interface(Constants.EVENT_TYPE_INVOKE_INTERFACE, "api調用"), middleware_opt(Constants.EVENT_TYPE_MIDDLEWARE_OPT, "中間件操作"), job_execute(Constants.EVENT_TYPE_JOB_EXECUTE, "job執行狀態"), custom_log(Constants.EVENT_TYPE_CUSTOM_LOG, "自定義埋點日誌"), thirdparty_call(Constants.EVENT_TYPE_THIRDPARTY_CALL, "第三方系統調用"); private String symbol; private String label; private EventType(String symbol, String label) { this.symbol = symbol; this.label = label; } public String symbol() { return this.symbol; } public String label() { return this.label; } }
正常入庫日誌:沒有監控報警業務含義的日誌,僅僅是每個系統自己LOGGER.info(msg)打印出來的日誌
api調用:調用接口產生的日誌
中間件操作:操作hbase或者mongo的日誌
job執行狀態:mr和spark等任務的執行結果狀態日誌
自定義埋點日誌:對接方自己想要後期批量或者實時流處理的日誌,協助存儲,方便各個業務系統自己處理
第三方系統調用:調用客戶系統產生的日誌
具體抽象出來的對象如下:public class EventLog { // 事件日誌成功還是失敗 public static final String MONITOR_STATUS_SUCCESS = "success"; public static final String MONITOR_STATUS_FAILED = "failed"; // 日誌事件類型 protected EventType eventType; // 日誌事件名稱 protected String uniqueName; // 需要計算耗時的日誌設置耗時, 毫秒 protected long cost; // 狀態 protected String status; // 具體日誌內容 protected String log; /** * 不可主動new */ protected EventLog() { } /** * 創建eventlog * @param eventType * @param log * @return */ public static EventLog buildEventLog(EventType eventType, String uniqueName, long cost, String status, String log) { EventLog eventLog = new EventLog(); eventLog.setEventType(eventType); eventLog.setUniqueName(uniqueName); eventLog.setCost(cost); eventLog.setStatus(status); eventLog.setLog(log); return eventLog; } /** * 根據一條日誌的內容解析出該條日誌 * @param line * @return */ public static EventType parseEventType(String line) { if (line.indexOf(Constants.VERTICAL_LINE) == -1) { // log中不包含|, 說明肯定是normal日誌 return EventType.normal; } else { // 首先判斷是否是用戶自己的日誌中包含| String[] detail = line.split(Constants.VERTICAL_LINE_SPLIT); try { return EventType.valueOf(detail[0]); } catch (Exception e) { return EventType.normal; } } } /** * 根據字符串解析成EventLog * @param line * @return */ public static EventLog parseEventLog(String line) { String[] detail = line.split(Constants.VERTICAL_LINE_SPLIT); return buildEventLog(EventType.valueOf(detail[0]), detail[1], Long.parseLong(detail[2]), detail[3], detail[4]); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(this.eventType.symbol()); sb.append(Constants.VERTICAL_LINE); sb.append(this.uniqueName); sb.append(Constants.VERTICAL_LINE); sb.append(this.cost); sb.append(Constants.VERTICAL_LINE); sb.append(this.status); sb.append(Constants.VERTICAL_LINE); sb.append(this.log); return sb.toString(); } public EventType getEventType() { return eventType; } public void setEventType(EventType eventType) { this.eventType = eventType; } public String getUniqueName() { return uniqueName; } public void setUniqueName(String uniqueName) { this.uniqueName = uniqueName; } public long getCost() { return cost; } public void setCost(long cost) { this.cost = cost; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getLog() { return log; } public void setLog(String log) { this.log = log; } }
以上是大部分日誌事件能夠抽象出來的dto,特殊的如下:public class ApiLog extends EventLog { // 具體請求api的賬戶 private String account; /** * 不可主動new */ private ApiLog() { } public static ApiLog buildApiLog(EventType eventType, String uniqueName, String account, long cost, String status, String log) { ApiLog apiLog = new ApiLog(); apiLog.setEventType(eventType); apiLog.setUniqueName(uniqueName); apiLog.setCost(cost); apiLog.setStatus(status); apiLog.setLog(log); apiLog.setAccount(account); return apiLog; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(this.eventType.symbol()); sb.append(Constants.VERTICAL_LINE); sb.append(this.uniqueName); sb.append(Constants.VERTICAL_LINE); sb.append(this.account); sb.append(Constants.VERTICAL_LINE); sb.append(this.cost); sb.append(Constants.VERTICAL_LINE); sb.append(this.status); sb.append(Constants.VERTICAL_LINE); sb.append(this.log); return sb.toString(); } /** * 根據字符串解析成EventLog * @param line * @return */ public static ApiLog parseEventLog(String line) { String[] detail = line.split(Constants.VERTICAL_LINE_SPLIT); return buildApiLog(EventType.valueOf(detail[0]), detail[1], detail[2], Long.parseLong(detail[3]), detail[4], detail[5]); } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } }
- 埋點設計
對於我們的業務,我們抽象了上一章的幾個日誌事件即可,自己可以根據自己的業務需求再定義;當我們的業務系統發生了特定的事件,我們可以構造相應的日誌調用LOGGER.info(xxx)來傳遞到下游處理
如:LOGGER.info(ApiLog.buildApiLog(EventType.invoke_interface, "/app/status", "800001", 100, EventLog.MONITOR_STATUS_SUCCESS, "我是mock api成功日誌").toString());
- 如何對接
- logback
- 依賴
compile ("monitor-center:pugna:0.0.1") { exclude group: 'log4j', module: 'log4j' }
- 配置
在logback.xml中加入一個kafkaAppender,並在properties文件中設置好相關的值,如下:<property name="APP_NAME" value="your-app-name" /> <!-- kafka appender --> <appender name="kafkaAppender" class="com.unionpaysmart.pugna.kafka.logback.KafkaAppender"> <encoder class="com.unionpaysmart.pugna.kafka.logback.encoder.KafkaLayoutEncoder"> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS};${CONTEXT_NAME};${HOSTNAME};%thread;%-5level;%logger{96};%line;%msg%n</pattern> </layout> </encoder> <topic>${kafka.topic}</topic> <zkServers>${zookeeper.servers}</zkServers> <mail>${mail}</mail> <keyBuilder class="com.unionpaysmart.pugna.kafka.partitioner.AppHostKeyBuilder" /> <config>bootstrap.servers=${kafka.bootstrap.servers}</config> <config>acks=0</config> <config>linger.ms=100</config> <config>max.block.ms=5000</config> <config>client.id=${CONTEXT_NAME}-${HOSTNAME}-logback</config> </appender>
- 依賴
- log4j
- 依賴
compile ("monitor-center:pugna:0.0.1") { exclude group: 'ch.qos.logback', module: 'logback-classic' }
- 配置
在log4j.xml中加入一個kafkaAppender,如下:<appender name="kafkaAppender" class="com.unionpaysmart.pugna.kafka.log4j.KafkaAppender"> <param name="topic" value="${kafka.topic}"/> <param name="zkServers" value="${zookeeper.servers}"/> <param name="app" value="apollo-generater"/> <param name="mail" value="${mail}"/> <param name="bootstrapServers" value="${kafka.bootstrap.servers}"/> <param name="acks" value="0"/> <param name="maxBlockMs" value="5000"/> <param name="lingerMs" value="100"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS};APP_NAME;HOSTNAME;%t;%p;%c;%L;%m%n"/> </layout> </appender>
- 依賴
- 注意點
公司大部分使用的logback版本爲1.1.7,該版本結合kafka有bug,具體見:http://jira.qos.ch/browse/LOGBACK-1158
- logback
- 使用
- 正常日誌
LOGGER.info("我是測試日誌打印")
- api日誌
// 參數依次爲EventType(事件類型)、api、賬號、請求耗時、成功還是失敗、具體自定義的日誌內容 LOGGER.info(ApiLog.buildApiLog(EventType.invoke_interface, "/app/status", "800001", 100, EventLog.MONITOR_STATUS_SUCCESS, "我是mock api成功日誌").toString()); LOGGER.info(ApiLog.buildApiLog(EventType.invoke_interface, "/app/status", "800001", 10, EventLog.MONITOR_STATUS_FAILED, "我是mock api失敗日誌").toString());
- 中間件日誌
// 參數依次爲EventType(事件類型)、MiddleWare(中間件名稱)、操作耗時、成功還是失敗、具體自定義的日誌內容 LOGGER.info(EventLog.buildEventLog(EventType.middleware_opt, MiddleWare.HBASE.symbol(), 100, EventLog.MONITOR_STATUS_SUCCESS, "我是mock middle ware成功日誌").toString()); LOGGER.info(EventLog.buildEventLog(EventType.middleware_opt, MiddleWare.MONGO.symbol(), 10, EventLog.MONITOR_STATUS_FAILED, "我是mock middle ware失敗日誌").toString());
- job執行日誌
// job執行僅僅處理失敗的日誌(成功的不做處理,所以只需要構造失敗的日誌), 參數依次爲EventType(事件類型)、job 的id號、操作耗時、失敗、具體自定義的日誌內容 LOGGER.info(EventLog.buildEventLog(EventType.job_execute, "application_1477705439920_0544", 10, EventLog.MONITOR_STATUS_FAILED, "我是mock job exec失敗日誌").toString());
- 第三方請求日誌
// 參數依次爲EventType(事件類型)、第三方名稱、操作耗時、成功還是失敗、具體自定義的日誌內容 LOGGER.info(EventLog.buildEventLog(EventType.thirdparty_call, "name1", 100, EventLog.MONITOR_STATUS_FAILED, "我是mock third 失敗日誌").toString()); LOGGER.info(EventLog.buildEventLog(EventType.thirdparty_call, "name1", 100, EventLog.MONITOR_STATUS_SUCCESS, "我是mock third 成功日誌").toString()); LOGGER.info(EventLog.buildEventLog(EventType.thirdparty_call, "name2", 100, EventLog.MONITOR_STATUS_SUCCESS, "我是mock third 成功日誌").toString()); LOGGER.info(EventLog.buildEventLog(EventType.thirdparty_call, "name2", 100, EventLog.MONITOR_STATUS_FAILED, "我是mock third 失敗日誌").toString());
- 正常日誌
從零到日誌採集索引可視化、監控報警、rpc trace跟蹤-日誌事件、埋點設計及對接
這篇主要來介紹下如何設計不同業務含義的日誌的抽象設計、如何埋點、以及我們寫的這個項目如何對接。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.