# logback ###### tags: `java` `log` `logback` `logs` ## Appenders Logback delegates the task of writing a logging event to components called appenders. Appenders must implement the ch.qos.logback.core.Appender interface. ### ConsoleAppender (待續) ### FileAppender (待續) #### RollingFileAppender (待續) ### SMTPAppender (待續) #### SMTPAppender configuration for Gmail (SSL) (待續) #### SMTPAppender for Gmail (STARTTLS) (待續) #### SMTPAppender with MDCDiscriminator (待續) ### DBAppender 把 logging events 存到以下三個資料表: * `logging_event` 資料表 MySQL/MariaDB 的 DDL: ``` CREATE TABLE `logging_event` ( `event_id` BIGINT AUTO_INCREMENT PRIMARY KEY, -- 官方的設計是將此欄位放在最後 `occurred` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 這是我自己新增的欄位 `timestmp` BIGINT NOT NULL, `formatted_message` TEXT NOT NULL, `logger_name` VARCHAR(256) NOT NULL, `level_string` VARCHAR(256) NOT NULL, `thread_name` VARCHAR(256), `reference_flag` SMALLINT, `arg0` VARCHAR(256), `arg1` VARCHAR(256), `arg2` VARCHAR(256), `arg3` VARCHAR(256), `caller_filename` VARCHAR(256) NOT NULL, `caller_class` VARCHAR(256) NOT NULL, `caller_method` VARCHAR(256) NOT NULL, `caller_line` CHAR(4) NOT NULL )COLLATE=utf8mb4_general_ci; ``` PostgreSQL 的 DDL: ``` CREATE TABLE "logging_event" ( "event_id" serial8 PRIMARY KEY, -- 官方的設計是將此欄位放在最後 "occurred" timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 這是我自己新增的欄位 "timestmp" int8 NOT NULL, "formatted_message" text NOT NULL, "logger_name" varchar NOT NULL, "level_string" varchar NOT NULL, "thread_name" varchar, "reference_flag" int2, "arg0" varchar, "arg1" varchar, "arg2" varchar, "arg3" varchar, "caller_filename" varchar NOT NULL, "caller_class" varchar NOT NULL, "caller_method" varchar NOT NULL, "caller_line" varchar NOT NULL ); ``` * `logging_event_property` 資料表 MySQL/MariaDB 的 DDL: ``` CREATE TABLE `logging_event_exception` ( `event_id` BIGINT NOT NULL, `i` SMALLINT NOT NULL, `trace_line` VARCHAR(256) NOT NULL, PRIMARY KEY(`event_id`, `i`), FOREIGN KEY (`event_id`) REFERENCES `logging_event`(`event_id`) )COLLATE=utf8mb4_general_ci; ``` PostgreSQL 的 DDL: ``` CREATE TABLE "logging_event_exception" ( "event_id" int8 NOT NULL, "i" int2 NOT NULL, "trace_line" varchar NOT NULL, PRIMARY KEY("event_id", "i"), FOREIGN KEY ("event_id") REFERENCES "logging_event"("event_id") ); ``` * `logging_event_exception` 資料表 MySQL/MariaDB 的 DDL: ``` CREATE TABLE `logging_event_property` ( `event_id` BIGINT NOT NULL, `mapped_key` VARCHAR(256) NOT NULL, `mapped_value` TEXT, PRIMARY KEY(`event_id`, `mapped_key`), FOREIGN KEY (`event_id`) REFERENCES `logging_event`(`event_id`) )COLLATE=utf8mb4_general_ci; ``` PostgreSQL 的 DDL: ``` CREATE TABLE "logging_event_property" ( "event_id" int8 NOT NULL, "mapped_key" varchar NOT NULL, "mapped_value" text, PRIMARY KEY("event_id", "mapped_key"), FOREIGN KEY ("event_id") REFERENCES "logging_event"("event_id") ); ``` #### `ch.qos.logback.core.db.ConnectionSource`<sup>[![JavaDoc](https://i.imgur.com/7EkdyXl.png)](http://logback.qos.ch/apidocs/ch/qos/logback/core/db/ConnectionSource.html "JavaDoc")</sup> interface 建立 JDBC 資料庫連結`java.sql.Connection`<sup>[![JavaDoc](https://i.imgur.com/7EkdyXl.png)](https://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html "JavaDoc")</sup>;有以下三種實作:`ch.qos.logback.core.db.DriverManagerConnectionSource`<sup>[![JavaDoc](https://i.imgur.com/7EkdyXl.png)](http://logback.qos.ch/apidocs/ch/qos/logback/core/db/DriverManagerConnectionSource.html "JavaDoc")</sup>、`ch.qos.logback.core.db.DataSourceConnectionSource`<sup>[![JavaDoc](https://i.imgur.com/7EkdyXl.png)](http://logback.qos.ch/apidocs/ch/qos/logback/core/db/DataSourceConnectionSource.html "JavaDoc")</sup>、`ch.qos.logback.core.db.JNDIConnectionSource`<sup>[![JavaDoc](https://i.imgur.com/7EkdyXl.png)](http://logback.qos.ch/apidocs/ch/qos/logback/core/db/JNDIConnectionSource.html "JavaDoc")</sup>。 ##### 傳統的`DriverManagerConnectionSource`<sup>[![官方手冊](https://i.imgur.com/S2PaE8S.png)](http://logback.qos.ch/manual/appenders.html#append-toMySQL-with-driverManager "官方手冊")</sup> 以`java.sql.DriverManager`<sup>[![JavaDoc](https://i.imgur.com/7EkdyXl.png)](https://docs.oracle.com/javase/8/docs/api/java/sql/DriverManager.html "JavaDoc")</sup>取得`Connection`;此方式的缺點是每次呼叫`getConnection()`都會重新開啟並在使用過後關閉 connection,這樣會造成效能上的瓶頸。 ``` <configuration> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"> <driverClass>com.mysql.jdbc.Driver</driverClass> <url>jdbc:mysql://${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_CATALOG}</url> <user>${DATABASE_USERNAME}</user> <password>${DATABASE_HOST}</password> </connectionSource> </appender> <root level="DEBUG"> <appender-ref ref="DB"/> </root> </configuration> ``` 附上坊間常見的 JDBC drivers: | | `<groupId/>` | `<artifactId/>` | class 的 FQN<sup>[![Fully Qualified Name](https://i.imgur.com/S2PaE8S.png)](https://en.wikipedia.org/wiki/Fully_qualified_name "Fully Qualified Name")</sup> | | -------- | -------- | -------- | -------- | | MariaDB | `org.mariadb.jdbc` | `mariadb-java-client` | `org.mariadb.jdbc.Driver` | | MySQL | `mysql` | `mysql-connector-java` | `com.mysql.cj.jdbc.Driver` | | PostgreSQL | `org.postgresql` | `postgresql` | `org.postgresql.Driver` | ##### 建議使用`DataSourceConnectionSource`<sup>[![官方手冊](https://i.imgur.com/S2PaE8S.png)](http://logback.qos.ch/manual/appenders.html#append-with-datasource "官方手冊")</sup> ``` <configuration debug="true"> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"> <dataSource class="${dataSourceClass}"> <!-- Joran cannot substitute variables that are not attribute values. Therefore, we cannot declare the next parameter like the others. --> <param name="${url-key:-url}" value="${url_value}"/> <serverName>${DATABASE_HOST}</serverName> <databaseName>${DATABASE_CATALOG}</databaseName> </dataSource> <user>${DATABASE_USERNAME}</user> <password>${DATABASE_PASSWORD}</password> </connectionSource> </appender> <root level="INFO"> <appender-ref ref="DB"/> </root> </configuration> ``` 以下為支援 connection pool 的設定範例: ``` <configuration> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"> <dataSource class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"> <serverName>${DATABASE_HOST}</serverName> <port>${DATABASE_PORT}</port> <databaseName>${DATABASE_CATALOG}</databaseName> <user>${DATABASE_USERNAME}</user> <password>${DATABASE_PASSWORD}</password> </dataSource> </connectionSource> </appender> <root level="DEBUG"> <appender-ref ref="DB"/> </root> </configuration> ``` 附上坊間常見的 connection pools: | | `<groupId/>` | `<artifactId/>` | class 的 FQN<sup>[![Fully Qualified Name](https://i.imgur.com/S2PaE8S.png)](https://en.wikipedia.org/wiki/Fully_qualified_name "Fully Qualified Name")</sup> | | -------- | -------- | -------- | -------- | -------- | | Text | Text | Text | Text | Text | | Text | Text | Text | Text | Text | | Text | Text | Text | Text | com.mysql.jdbc.jdbc2.optional.MysqlDataSource | | Text | Text | Text | Text | Text | | Text | Text | Text | Text | Text | | Text | Text | Text | Text | Text | | Text | Text | Text | Text | Text | | Text | Text | Text | Text | Text | | Text | Text | Text | Text | Text | | Text | Text | Text | Text | Text | ##### 使用 servlet container 或 application server 時請愛用`JNDIConnectionSource`<sup>[![官方手冊](https://i.imgur.com/S2PaE8S.png)](http://logback.qos.ch/manual/appenders.html#JNDIConnectionSource "官方手冊")</sup> ``` <configuration debug="true"> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.JNDIConnectionSource"> <!-- 記得加上 "java:comp/env/" 前綴 --> <jndiLocation>java:comp/env/jdbc/logging</jndiLocation> </connectionSource> </appender> <root level="INFO"> <appender-ref ref="DB"/> </root> </configuration> ```