# 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>[](http://logback.qos.ch/apidocs/ch/qos/logback/core/db/ConnectionSource.html "JavaDoc")</sup> interface
建立 JDBC 資料庫連結`java.sql.Connection`<sup>[](https://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html "JavaDoc")</sup>;有以下三種實作:`ch.qos.logback.core.db.DriverManagerConnectionSource`<sup>[](http://logback.qos.ch/apidocs/ch/qos/logback/core/db/DriverManagerConnectionSource.html "JavaDoc")</sup>、`ch.qos.logback.core.db.DataSourceConnectionSource`<sup>[](http://logback.qos.ch/apidocs/ch/qos/logback/core/db/DataSourceConnectionSource.html "JavaDoc")</sup>、`ch.qos.logback.core.db.JNDIConnectionSource`<sup>[](http://logback.qos.ch/apidocs/ch/qos/logback/core/db/JNDIConnectionSource.html "JavaDoc")</sup>。
##### 傳統的`DriverManagerConnectionSource`<sup>[](http://logback.qos.ch/manual/appenders.html#append-toMySQL-with-driverManager "官方手冊")</sup>
以`java.sql.DriverManager`<sup>[](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>[](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>[](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>[](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>[](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>
```