
# 日志管理
GaussDB JDBC驱动程序支持使用日志记录来帮助解决在应用程序中使用GaussDB JDBC驱动程序时的问题。GaussDB JDBC支持如下两种日志管理方式：
- 对接应用程序使用的SLF4J日志框架。
- 对接应用程序使用的JdkLogger日志框架。
SLF4J和JdkLogger是业界Java应用程序日志管理的主流框架，描述应用程序如何使用这些框架超出了本文范围，用户请参考对应的官方文档（SLF4J：<https://www.slf4j.org/manual.html>，JdkLogger：<https://docs.oracle.com/javase/8/docs/technotes/guides/logging/overview.html>）。
#### 方式一：对接应用程序的SLF4J日志框架
在建立连接时，url需要配置logger=Slf4JLogger。此方式支持日志管控，SLF4J可通过文件中的相关配置实现强大的日志管控功能，建议使用此方式进行日志管理。
可采用Log4j或Log4j2来实现SLF4J。当采用Log4j实现SLF4J，需要添加如下jar包（\*区分版本）：log4j-\*.jar、slf4j-api-\*.jar、slf4j-log4\*-\*.jar。同时指定log4j.properties配置文件的路径（相对路径或者绝对路径均可）。
若采用Log4j2实现SLF4J，需要添加如下jar包（\*区分版本）：log4j-api-\*.jar、log4j-core-\*.jar、log4j-slf4j18-impl-\*.jar、slf4j-api-\*-alpha1.jar。同时指定log4j2.xml配置文件的路径（相对路径或者绝对路径均可）。
![](https://support.huaweicloud.com/distributed-devg-v8-gaussdb/public_sys-resources/notice_3.0-zh-cn.png)
此方式依赖slf4j的通用API接口，如org.slf4j.LoggerFactory.getLogger(String name)、org.slf4j.Logger.debug(String var1)、org.slf4j.Logger.info(String var1)、org.slf4j.Logger.warn(String warn)、org.slf4j.Logger.warn(String warn)等，若以上接口发生变更，日志将无法打印。
示例：
```
//注意：调用时需要指定log4j.properties或者log4j2.xml配置文件路径。
//使用log4j.properties文件来配置日志记录器的属性，例如日志级别、输出目标等。通过调用PropertyConfigurator的configure方法并传递log4j.properties文件的路径，将这些属性加载到应用程序中。
//PropertyConfigurator.configure("log4j.properties");
//创建Log4j2配置源，指定配置文件log4j2.xml的路径。并初始化日志系统。
//ConfigurationSource source = new ConfigurationSource(new FileInputStream("log4j2.xml"));
//Configurator.initialize(null, source);
public static Connection GetConnection(String username, String passwd){
 
    String sourceURL = "jdbc:gaussdb://$ip:$port/database?logger=Slf4JLogger";
    Connection conn = null;
 
    try{
        //创建连接。
        conn = DriverManager.getConnection(sourceURL,username,passwd);
        System.out.println("Connection succeed!");
    }catch (Exception e){
        e.printStackTrace();
        return null;
    }
    return conn;
}
```
log4j.properties配置文件：
```
log4j.logger.com.huawei.gaussdb.jdbc=ALL, log_gsjdbc
# 默认文件输出配置。
log4j.appender.log_gsjdbc=org.apache.log4j.RollingFileAppender
log4j.appender.log_gsjdbc.Append=true
log4j.appender.log_gsjdbc.File=gsjdbc.log
log4j.appender.log_gsjdbc.Threshold=TRACE
log4j.appender.log_gsjdbc.MaxFileSize=10MB
log4j.appender.log_gsjdbc.MaxBackupIndex=5
log4j.appender.log_gsjdbc.layout=org.apache.log4j.PatternLayout
log4j.appender.log_gsjdbc.layout.ConversionPattern=%d %p %t %c - %m%n
log4j.appender.log_gsjdbc.File.Encoding = UTF-8
```
log4j2.xml配置文件：
```
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
    <appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d %p %t %c - %m%n"/>
        </Console>
        <File name="FileTest" fileName="test.log">
            <PatternLayout pattern="%d %p %t %c - %m%n"/>
        </File>
        <!--JDBC Driver日志文件输出配置，支持日志回卷，设定日志大小超过10MB时，创建新的文件，新文件的命名格式为：年-月-日-文件编号-->
        <RollingFile name="RollingFileJdbc" fileName="gsjdbc.log" filePattern="%d{yyyy-MM-dd}-%i.log">
            <PatternLayout pattern="%d %p %t %c - %m%n"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
        </RollingFile>
    </appenders>
    <loggers>
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="FileTest"/>
        </root>
        <!--指定JDBC Driver日志，级别为all，可查看所有日志，输出到gsjdbc.log文件中--> 
        <logger name="com.huawei.gaussdb.jdbc" level="all" additivity="false">
            <appender-ref ref="RollingFileJdbc"/>
        </logger>
    </loggers>
</configuration>
```
![](https://support.huaweicloud.com/distributed-devg-v8-gaussdb/public_sys-resources/notice_3.0-zh-cn.png)
- 在配置文件中，可以指定JDBC与上层业务的配置路径，将JDBC日志与业务日志分别存储到不同文件。
- 同一个项目中配置多个GaussDB数据源时，如果存在部分业务配置了logger=Slf4JLogger，部分业务未配置，会互相影响日志配置。建议这种情况下连接串统一配置。
 
#### 方式二：对接应用程序使用的JdkLogger日志框架
两种方式：采用logging.properties配置文件、直接在示例代码中进行配置。
- 默认的Java日志记录框架将其配置存储在名为logging.properties的文件中。Java会在Java安装目录的文件夹中安装全局配置文件。logging.properties文件也可以创建并与单个项目一起存储。 logging.properties配置文件：
  ```
  # 指定处理程序为文件。
  handlers= java.util.logging.FileHandler
  # 指定默认全局日志级别。
  .level= ALL
  # 指定日志输出管控标准。
  java.util.logging.FileHandler.level=ALL
  java.util.logging.FileHandler.pattern = gsjdbc.log
  java.util.logging.FileHandler.limit = 500000
  java.util.logging.FileHandler.count = 30
  java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
  java.util.logging.FileHandler.append=false
  ```
  
- 直接在示例代码中进行配置：
  ```
  System.setProperty("java.util.logging.FileHandler.pattern","jdbc.log");
  FileHandler fileHandler = new FileHandler(System.getProperty("java.util.logging.FileHandler.pattern"));
  Formatter formatter = new SimpleFormatter();
  fileHandler.setFormatter(formatter);
  Logger logger = Logger.getLogger("com.huawei.gaussdb.jdbc");
  logger.addHandler(fileHandler);
  logger.setLevel(Level.ALL);
  logger.setUseParentHandlers(false);
  ```
  
 
#### 链路跟踪功能
GaussDB JDBC驱动程序提供了应用到数据库的链路跟踪功能，用于将数据库端离散的SQL和应用程序的请求关联起来。该功能需要应用开发者实现com.huawei.gaussdb.jdbc.log.Tracer接口类，并在url中指定接口实现类的全类名。
url示例：
```
String url = "jdbc:gaussdb://$ip:$port/database?traceInterfaceClass=xxx.xxx.xxx.gaussdbTraceImpl";
```
com.huawei.gaussdb.jdbc.log.Tracer接口类定义如下：
```
public interface Tracer {
// Retrieves the value of traceId.
String getTraceId();
}
```
com.huawei.gaussdb.jdbc.log.Tracer接口实现类示例：
```
import com.huawei.gaussdb.jdbc.log.Tracer;
public class gaussdbTraceImpl implements Tracer {
    private static MDC mdc = new MDC();
    private final String TRACE_ID_KEY = "traceId";
    public void set(String traceId) {
        mdc.put(TRACE_ID_KEY, traceId);
    }
    public void reset() {
        mdc.clear();
    }
    @Override
    public String getTraceId() {
        return mdc.get(TRACE_ID_KEY);
    }
}
```
上下文映射示例，用于存放不同请求生成的traceId。
```
import java.util.HashMap;
public class MDC {
    static final private ThreadLocal<HashMap<String, String>> threadLocal = new ThreadLocal<>();
    public void put(String key, String val) {
        if (key == null || val == null) {
            throw new IllegalArgumentException("key or val cannot be null");
        } else {
            if (threadLocal.get() == null) {
                threadLocal.set(new HashMap<>());
            }
            threadLocal.get().put(key, val);
        }
    }
    public String get(String key) {
        if (key == null) {
            throw new IllegalArgumentException("key cannot be null");
        } else if (threadLocal.get() == null) {
            return null;
        } else {
            return threadLocal.get().get(key);
        }
    }
    public void clear() {
        if (threadLocal.get() == null) {
            return;
        } else {
            threadLocal.get().clear();
        }
    }
}
```
业务使用traceId示例，前置条件需要先建表test_trace_id (id int,name varchar(20))。
```
String traceId = UUID.randomUUID().toString().replaceAll("-", "");
gaussdbTraceImpl gaussdbTrace = new gaussdbTraceImpl();
gaussdbTrace.set(traceId);
Connection con = DriverManager.getConnection(url, user, password);
pstm = con.prepareStatement("SELECT * FROM test_trace_id WHERE id = ?");
pstm.setInt(1, 1);
pstm.execute();
pstm = con.prepareStatement("INSERT INTO test_trace_id VALUES(?,?)");
pstm.setInt(1, 2);
pstm.setString(2, "test");
pstm.execute();
gaussdbTrace.reset();
```
![](https://support.huaweicloud.com/distributed-devg-v8-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
- 使用链路跟踪功能时，应用层链路功能由业务保证。
- 应用必须向JDBC暴露获取traceId的接口，并将该接口实现类配置到JDBC连接字符串中。
- 同一请求的不同SQL使用的traceId须相同。
- 应用传入的traceId不能超过32字节，否则多余字节将被截断。
 
