解决eclipse 代码报错

问题描述

我用eclipse打开书上的随书源码时,log4j.xml文件的<!DOCTYPE>这行代码有错误:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration
    xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender
        name="STDOUT"
        class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param
                name="ConversionPattern"
                value="%5p [%t] %m%n"/>
        </layout>
    </appender>
    <logger name="mapper.EmployeeMapper">
        <level value="DEBUG"/>
    </logger>
    <root>
        <level value="ERROR"/>
        <appender-ref ref="STDOUT"/>
    </root>
</log4j:configuration>

错误提示信息如下:

1
2
Cannot find DTD 'file:///E:/workspace_web2/MyDynamicSQLTest/src/log4j.dtd'.
Create the DTD file or configure an XML catalog for this DTD.

图片

解决方案1

创建一个log4j2.xml文件,然后复制里面的!DOCTYPE元素,替换log4j.xml文件中的!DOCTYPE元素。

通过DTD file创建log4j2.xml文件

src目录上右键,然后选择New,Others...
图片
Wizards搜索框中输入xml,然后选择XML File,接着点击Next
图片
输入文件名log4j2.xml,点击Next
图片
选择Create XML file from a DTD file,点击Next
图片
选择Select XML Catalog entry,然后选择-//LOG4J//DTD LOG4J//EN,点击Next
图片
最后点击Finish
图片

创建好的log4j2.xml文件源码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//LOG4J//DTD LOG4J//EN" "https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd" >
<log4j:configuration></log4j:configuration>

用log4j2.xml的DOCTYPE替换log4j.xml的DOCTYPE

log4j2.xml文件中的

1
<!DOCTYPE log4j:configuration PUBLIC "-//LOG4J//DTD LOG4J//EN" "https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd" >

这行代码复制,替换log4j.xml中的相同语句即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//LOG4J//DTD LOG4J//EN" "https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd" >
<log4j:configuration
xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender
name="STDOUT"
class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param
name="ConversionPattern"
value="%5p [%t] %m%n"/>
</layout>
</appender>
<logger name="mapper.EmployeeMapper">
<level value="DEBUG"/>
</logger>
<root>
<level value="ERROR"/>
<appender-ref ref="STDOUT"/>
</root>
</log4j:configuration>

然后删除掉无用的log4j2.xml

解决方案2

取出log4j-xxx.jar文件中的log4j.dtd文件

eclipse安装目录下创建一个myconfig文件夹

用解压软件,打开log4jjar文件,然后依次进入org\apache\log4j\xml\目录下,将log4j.dtd复制出来
图片

粘贴到上面创建的myconfig文件夹中.
图片

创建Catalog Entry

eclipse中点击Windows,Preferences,XML,XML Catalog,Add按钮,
图片

填写Location文本框

点击Catalog Entry,然后点击Location文本框下面的File System按钮,选择刚才保存到myconfig文件夹下的log4j.dtd文件。
图片

填写Key type选择框

然后在Key type选择框中选择System ID

填写key文本框

然后在key文本框中填写如下URL:

1
https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd

图片

在Design标签页 修改DOCTYPE

点击log4j.xml文件下方的Design标签页,然后在表格的DOCTYPE这一行上右键,选择Edit DOCTYPE:
图片

  • 点击Public ID右边的Browse按钮,然后选择-//LOG4J//DTD LOG4J//EN这个选择项
    图片
  • 点击System ID右边的Browse按钮,然后选择Select XML Catalog entry这个单选项,然后在下面的选择框选择
    1
    https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd
    这个key
    图片

查看修改后的log4j.xml源码

点击文件下方的Source查看修改后的效果
修改后的log4j.xml文件如下所示

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//LOG4J//DTD LOG4J//EN" "https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<log4j:configuration
    xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="STDOUT"
        class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%5p [%t] %m%n" />
        </layout>
    </appender>
    <logger name="mapper.EmployeeMapper">
        <level value="DEBUG" />
    </logger>
    <root>
        <level value="ERROR" />
        <appender-ref ref="STDOUT" />
    </root>
</log4j:configuration>

错误提示

Exception in thread "main" org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): mapper.EmployeeMapper.selectEmployeeLikeName
    at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:225)
    at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:48)
    at org.apache.ibatis.binding.MapperProxy.cachedMapperMethod(MapperProxy.java:65)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:58)
    at com.sun.proxy.$Proxy2.selectEmployeeLikeName(Unknown Source)
    at test.BindTest.main(BindTest.java:19)

分析

拼写错误:
Mapper.xml文件中select语句的id和Mapper接口中的方法名不相同

EmployeeMapper.xml

<!-- 测试bind属性 -->
<select
    id="seletEmployeeLikeName"
    resultType="domain.Employee">
    <!-- 创建OGNL表达式并绑定到上下文的pattern属性中 -->
    <!-- _parameter表示传递的Employee对象 -->
    <!-- _parameter.getName()表示调用参数(Employee对象)的getName方法 -->
    <!-- 两个下划线`__`是like子句的通配符,一个下划线可以匹配一个字符,两个下划线表示可以匹配两个字符 -->
    <!-- like子句还有一个通配符%(百分号)这个通配符可以匹配任意多个字符 -->
    <!-- 整个表达式表示匹配以_parameter.getName()获取到的员工名字开通,并且后面还有两个任意字符的字符串 -->
    <bind
        name="pattern"
        value="_parameter.getName()+'__'"/>
    <!-- 通过mybatis表达式获取上下文中的pattern属性值 -->
    select * from tb_employee where loginname like #{pattern}
</select>

EmployeeMapper.java

package mapper;
......
public interface EmployeeMapper {
    ......
    List<Employee> selectEmployeeLikeName(Employee employee);
    ......
}

解决方案

使Mapper接口中的方法名和Mapper.xml中的select方法的id属性值
相同即可.

错误提示

Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: java.sql.SQLException: SQL String cannot be empty
### The error may exist in mapper/UserMapper.xml
### The error may involve mapper.UserMapper.selectUserById
### The error occurred while executing a query
### SQL: 
### Cause: java.sql.SQLException: SQL String cannot be empty
    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:149)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:76)
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:87)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:57)
    at com.sun.proxy.$Proxy5.selectUserById(Unknown Source)
    at test.ManyToManyTest2.main(ManyToManyTest2.java:16)
    ......

分析

mapper/UserMapper.xml文件中id为mapper.UserMapper.selectUserById的这条select语句,忘了写SQL语句:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.UserMapper">
    <select id="selectUserById" parameterType="int"
        resultMap="userMap">
    </select>
    <resultMap type="domain.User" id="userMap">
        <id property="id" column="id" />
        <result property="userName" column="username" />
        <result property="loginName" column="loginname" />
        <result property="password" column="password" />
        <result property="phone" column="phone" />
        <result property="address" column="address" />
        <collection property="orders" javaType="ArrayList"
            ofType="domain.Order" column="id" select="mapper.OrderMapper.selectOrdersByUserId">
            <id property="id" column="id" />
            <result property="code" column="code" />
            <result property="total" column="total" />
        </collection>
    </resultMap>
</mapper>

解决方案

SQL语句写上即可:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.UserMapper">
    <select id="selectUserById" parameterType="int"
        resultMap="userMap">
        select * from tb_user where id=#{id}
    </select>
    <resultMap type="domain.User" id="userMap">
        <id property="id" column="id" />
        <result property="userName" column="username" />
        <result property="loginName" column="loginname" />
        <result property="password" column="password" />
        <result property="phone" column="phone" />
        <result property="address" column="address" />
        <collection property="orders" javaType="ArrayList"
            ofType="domain.Order" column="id" select="mapper.OrderMapper.selectOrdersByUserId">
            <id property="id" column="id" />
            <result property="code" column="code" />
            <result property="total" column="total" />
        </collection>
    </resultMap>
</mapper>

错误提示

展开/折叠
org.apache.ibatis.binding.BindingException: Type interface mapper.StudentMapper is not known to the MapperRegistry.
    at org.apache.ibatis.binding.MapperRegistry.getMapper(MapperRegistry.java:47)
    at org.apache.ibatis.session.Configuration.getMapper(Configuration.java:779)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.getMapper(DefaultSqlSession.java:291)
    at test.OneToManyTest3.main(OneToManyTest3.java:14)

原因

没有在mybatis根配置文件中注册mapper文件

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>
<properties resource="db.properties">
</properties>
<settings>
<setting name="logImpl" value="LOG4J" />
</settings>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
</configuration>

错误提示

Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: org.apache.ibatis.executor.ExecutorException:A query was run and no Result Maps were found for the Mapped Statement 'mapper.StudentMapper.selectStudentsByClazzId'.  It's likely that neither a Result Type nor a Result Map was specified.
### The error may exist in mapper/StudentMapper.xml
### The error may involve mapper.StudentMapper.selectStudentsByClazzId

分析原因

mapper.StudentMapper.selectStudentsByClazzId这条语句没有设置参数和返回值

错误代码

/OneToManyTest3/src/mapper/StudentMapper.xml
1
2
3
4
<select id="selectStudentsByClazzId">
select * from tb_student
<where>clazz_id=#{clazz_id}</where>
</select>

解决方案

给selectStudentsByClazzId语句加上参数和返回值声明:

<select id="selectStudentsByClazzId" parameterType="int"
    resultMap="studentMap">
    select * from tb_student
    <where>clazz_id=#{clazz_id}</where>
</select>

Log4j Debug记录

log4j:WARN No appenders could be found for logger

错误提示信息

1
2
3
log4j:WARN No appenders could be found for logger (demo.Log4jExample).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

配置文件内容

/Log4jDemo/src/log4j.properties
1
2
3
4
5
6
7
8
9
10
11
# Define the root logger with appender file
Log4j.rootLogger = DEBUG, FILE

# Define the file appender
Log4j.appender.FILE=org.apache.Log4j.FileAppender
Log4j.appender.FILE.File=htmlLayout.html

# Define the layout for file appender
Log4j.appender.FILE.layout=org.apache.Log4j.HTMLLayout
Log4j.appender.FILE.layout.Title=HTML Layout Example
Log4j.appender.FILE.layout.LocationInfo=true

解决方案

配置文件写错了,包名应该小写开头,将上述的Log4j改成log4j:

# Define the root logger with appender file
log4j.rootLogger = DEBUG, FILE

# Define the file appender
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=htmlLayout.html

# Define the layout for file appender
log4j.appender.FILE.layout=org.apache.log4j.HTMLLayout
log4j.appender.FILE.layout.Title=HTML Layout Example
log4j.appender.FILE.layout.LocationInfo=true

Log4j PatternLayout

如果您希望基于某种模式生成特定格式的日志信息,可使用org.apache.log4j.PatternLayout格式化您的日志信息。

PatternLayout继承自抽象类org.apache.log4j.Layout,覆盖了其format()方法,通过提供的模式,来格式化日志信息。

PatternLayout是一个简单的Layout对象,提供了如下属性,该属性可通过配置文件更改:

PatternLayout属性 描述
ConversionPattern 设置转换模式,默认为 %r [%t] %p %c %x - %m%n

TTCC

https://zh.wikipedia.org/wiki/Log4j#TTCC
TTCClog4j使用的消息格式。TTCCTime Thread Category Component(时间、线程、类别、组件)的缩写。例如,采用以下模式(pattern):
%r [%t] %-5p %c %x - %m%n

模式参数 描述
%r 用于输出从layout(布局)的构建到日志事件创建所经过的毫秒数。
%t 用来输出生成该日志事件的线程的名称
%p 用于输出日志事件的优先级
%c 用于输出日志事件的category(类别)。
%x 用于输出与产生该日志事件的线程相关联的NDC(嵌套诊断上下文,nested diagnostic context)。
%X{key} 用于输出与产生指定的key的日志事件的线程相关联的MDC(映射诊断上下文,mapped diagnostic context)[4]
%m 用于输出与日志记录事件相关联的应用程序提供的消息
%n 用来输出所在的特定平台的换行字符

模式转换字符

下面的表格解释了上面模式中用到的字符,以及所有定制模式时能用到的字符:

转换字符 含义
c 使用它为输出的日志事件分类,比如对于分类 “a.b.c”,模式 %c{2} 会输出 “b.c” 。
C 使用它输出发起记录日志请求的类的全名。比如对于类 “org.apache.xyz.SomeClass”,模式 %C{1} 会输出 “SomeClass”。
d 使用它输出记录日志的日期,比如 %d{HH:mm:ss,SSS}%d{dd MMM yyyy HH:mm:ss,SSS}
F 在记录日志时,使用它输出文件名。
l 用它输出生成日志的调用者的地域信息
L 使用它输出发起日志请求的行号
m 使用它输出和日志事件关联的,由应用提供的信息
M 使用它输出发起日志请求的方法名
n 输出平台相关的换行符
p 输出日志事件的优先级
r 使用它输出从构建布局到生成日志事件所花费的时间,以毫秒为单位
t 输出生成日志事件的线程名
x 输出和生成日志事件线程相关的 NDC (嵌套诊断上下文)。
X 该字符后跟 MDC 键,比如 X{clientIP} 会输出保存在 MDC 中键 clientIP 对应的值。
% 百分号, %%会输出一个%

格式修饰符

缺省情况下,信息保持原样输出。但是借助格式修饰符的帮助,就可调整最小列宽、最大列宽以及对齐。

下面的表格涵盖了各种修饰符:

格式修饰符 左对齐 最小宽度 最大宽度 注释
%20c 20 如果列名少于 20 个字符,左边使用空格补齐。
%-20c 20 如果列名少于 20 个字符,右边使用空格补齐。
%.30c 不适用 30 如果列名长于 30 个字符,从开头剪除。
%20.30c 20 30 如果列名少于 20 个字符,左边使用空格补齐,如果列名长于 30 个字符,从开头剪除。
%-20.30c 20 30 如果列名少于 20 个字符,右边使用空格补齐,如果列名长于 30 个字符,从开头剪除。

PatternLayout示例

下面是为PatternLayout编写的一个简单配置:

/Log4jDemo/Log4jConfig/PatternLayout/log4j.properties
1
2
3
4
5
6
7
8
9
10
11
12
# Define the root logger with appender file
#log = /usr/home/Log4j
Log4j.rootLogger = DEBUG, FILE

# Define the file appender
Log4j.appender.FILE=org.apache.Log4j.FileAppender
#Log4j.appender.FILE.File=${log}/log.out
Log4j.appender.FILE.File=log.out

# Define the layout for file appender
Log4j.appender.FILE.layout=org.apache.Log4j.PatternLayout
Log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd}-%t-%x-%-5p-%-10c:%m%n

下面是生成日志信息的Java程序:

/Log4jDemo/src/demo/Log4jExample.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package demo;

import java.io.IOException;
import java.sql.SQLException;
import org.apache.log4j.Logger;

public class Log4jExample
{
/* Get actual class name to be printed on */
static Logger log = Logger.getLogger(Log4jExample.class.getName());

public static void main(String[] args) throws IOException, SQLException
{
log.debug("Hello-this-is-a-debug-message");
log.info("Hello this is an info message");
}
}

编译并运行上述程序,会在项目目录下生成一个名为log.out的文件,该文件包含如下日志信息:

1
2
3
2020-08-06-main--DEBUG-demo.Log4jExample:Hello-this-is-a-debug-message
2020-08-06-main--INFO -demo.Log4jExample:Hello this is an info message

Log4j HTMLLayout

如果您希望以HTML格式输出日志文件,可使用org.apache.log4j.HTMLLayout格式化日志信息。

HTMLLayout继承自抽象类org.apache.log4j.Layout,覆盖了其format()方法来提供HTML格式。

它提供了如下信息以供显示:

  • 从应用启动到日志产生之间经过的时间。
  • 发起记录日志请求的线程名字。
  • 和记录日志请求关联的级别。
  • logger的名字和日志信息。
  • 程序文件的位置信息和触发日志的行号,该信息是可选的。

HTMLLayout方法

HTMLLayout是一种非常简单的Layout对象,它提供了如下方法:

HTMLLayout方法 描述
setContentType(String) 设置HTML的内容类型,默认为text/html
setLocationInfo(String) 设置日志事件的地域信息。
setTitle(String) 设置HTML文件的标题,默认为Log4j Log Messages

HTMLLayout示例

下面是为HTMLLayout编写的一个简单配置文件:

/Log4jDemo/Log4jConfig/HTMLLayout/log4j.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Define the root logger with appender file
#log = /usr/home/log4j
#log = E:/workspacne_JDK8Tomcat8.5/log4jDemo
log4j.rootLogger = DEBUG, FILE

# Define the file appender
log4j.appender.FILE=org.apache.log4j.FileAppender
#log4j.appender.FILE.File=${log}/htmlLayout.html
log4j.appender.FILE.File=htmlLayout.html

# Define the layout for file appender
log4j.appender.FILE.layout=org.apache.log4j.HTMLLayout
log4j.appender.FILE.layout.Title=HTML Layout Example
log4j.appender.FILE.layout.LocationInfo=true

Log4jExample.java

下面的Java程序会生成日志信息:

/Log4jDemo/src/demo/Log4jExample.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package demo;

import java.io.IOException;
import java.sql.SQLException;
import org.apache.log4j.Logger;

public class Log4jExample
{
/* Get actual class name to be printed on */
static Logger log = Logger.getLogger(Log4jExample.class.getName());

public static void main(String[] args) throws IOException, SQLException
{
log.debug("Hello-this-is-a-debug-message");
log.info("Hello this is an info message");
}
}

运行结果

编译和运行上述程序,在项目根路径下,会生成一个名为htmlLayout.html的文件,该文件包含如下日志信息:
图片
您可以使用浏览器打开htmlLayout.html文件。需要注意的是末尾缺失了</body></html>标签。但是并不影响浏览器解析。

将日志格式化为HTML的一个优势在于可将其发布成一个Web页面,便于远程浏览。

Log4j 使用数据库记录日志

Log4j API提供了org.apache.log4j.jdbc.JDBCAppender对象,该对象可将日志信息记录到特定的数据库之中。

JDBCAppender 配置

属性 描述
bufferSize 设置缓冲区大小,缺省为 1。
driver 以字符串形式设置驱动类,如果未设置,缺省为sun.jdbc.odbc.JdbcOdbcDriver
layout 设置layout,缺省为org.apache.log4j.PatternLayout
password 设置数据库密码。
sql 设置每次日志事件触发时需要执行的SQL语句,该语句可以是INSERTUPDATEDELETE
URL 设置JDBC URL.
user 设置数据库用户名。

日志表的配置

在使用基于JDBC的日志之前,先要创建一张表以保存所有日志信息,下面是用来创建LOGS表的SQL语句:

/Log4jDemo/src/logs.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建数据库
drop database if exists dbname;
create database dbname default character set utf8;
# 进入数据库
use dbname;
# 创建表
create table logs(
user_id varchar(20) not null,
date_time datetime not null,
logger varchar(50) not null,
level varchar(10) not null,
message varchar(1000) not null
);

示例配置文件

下面是一个为JDBCAppender编写的Log4j.properties的示例配置文件,使用该对象将日志信息记录到LOGS表中。

/Log4jDemo/Log4jConfig/JDBCAppender/log4j.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Define the root logger with appender DB and STDOUT
log4j.rootLogger = DEBUG, DB,STDOUT

# Define the DB appender
log4j.appender.DB=org.apache.log4j.jdbc.JDBCAppender

# Set JDBC URL
#log4j.appender.DB.URL=jdbc:mysql://localhost/DBNAME
log4j.appender.DB.URL=jdbc:mysql://localhost:3306/DBNAME?serverTimezone=UTC&characterEncoding=utf-8

# Set Database Driver
#log4j.appender.DB.driver=com.mysql.jdbc.Driver
# mysql8.0驱动
log4j.appender.DB.driver=com.mysql.cj.jdbc.Driver

# Set database user name and password
#log4j.appender.DB.user=user_name
log4j.appender.DB.user=root
#log4j.appender.DB.password=password
log4j.appender.DB.password=root

# Set the SQL statement to be executed.
#log4j.appender.DB.sql=INSERT INTO LOGS VALUES('%x','%d{yyyy-MM-dd HH:mm:ss}','%C','%p','%m')
log4j.appender.DB.sql=insert into logs(user_id,date_time,logger,level,message) values ('%x','%d{yyyy-MM-dd HH:mm:ss}','%C','%p','%m')

# Define the layout for file appender
log4j.appender.DB.layout=org.apache.log4j.PatternLayout

log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.Target=System.out
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern=user_id=%x,date_time=%d{yyyy-MM-dd HH:mm:ss},logger=%C,LEVEL=%p,message=%m%n

如果使用MySQL数据库,需要使用真实的DBNAME、用户名和密码,就是刚才用来创建LOGS表的那些属性。SQL语句执行INSERT语句,为LOGS表插入具体数值。
JDBCAppender不需要显示定义layout,传入的SQL语句会使用PatternLayout

使用XML配置文件

如果您需要和上述Log4j.properties文件等价的XML配置文件,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration
PUBLIC "-//LOG4J//DTD LOG4J//EN"
"https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd" >
<log4j:configuration>
<appender name="DB"
class="org.apache.log4j.jdbc.JDBCAppender">
<param name="driver" value="com.mysql.cj.jdbc.Driver" />
<param name="URL"
value="jdbc:mysql://localhost:3306/DBNAME?serverTimezone=UTC&amp;characterEncoding=UTF-8" />
<param name="user" value="root" />
<param name="password" value="root" />
<!-- <param name="sql" value="insert into logs(user_id,date_time,logger,level,message)
values ('%x','%d','%C','%p','%m')" /> -->

<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="insert into logs(user_id,date_time,logger,level,message) values ('%x','%d{yyyy-MM-dd HH:mm:ss}','%C','%p','%m')" />
</layout>
</appender>

<root>
<level value="DEBUG" />
<appender-ref ref="DB" />
</root>
</log4j:configuration>

log4j.xml把SQL写到layout的ConversionPattern属性

经过我的测试,把sql写在layout的ConversionPattern属性中,程序可以运行.
而使用appender的sql属性中的sql语句无法正常运行。

log4j.properties把SQL写在 appender的sql属性

在log4j.properties配置文件中,SQL既可以写在appender的sql属性中,也可以写在layout的ConversionPattern属性中.

1
2
3
4
5
log4j.appender.DB.sql=insert into logs(user_id,date_time,logger,level,message) values ('%x','%d{yyyy-MM-dd HH:mm:ss}','%C','%p','%m')

# Define the layout for file appender
log4j.appender.DB.layout=org.apache.log4j.PatternLayout
# log4j.appender.DB.layout.ConversionPattern=insert into logs(user_id,date_time,logger,level,message) values ('%x','%d{yyyy-MM-dd HH:mm:ss}','%C','%p','%m')

1
2
3
4
5
#log4j.appender.DB.sql=insert into logs(user_id,date_time,logger,level,message) values ('%x','%d{yyyy-MM-dd HH:mm:ss}','%C','%p','%m')

# Define the layout for file appender
log4j.appender.DB.layout=org.apache.log4j.PatternLayout
log4j.appender.DB.layout.ConversionPattern=insert into logs(user_id,date_time,logger,level,message) values ('%x','%d{yyyy-MM-dd HH:mm:ss}','%C','%p','%m')

这两种方式都是可以的。

示例程序

下述Java类是一个非常简单的例子,该类在Java应用中初始化并使用了Log4j类库。

/Log4jDemo/src/demo/Log4jExample.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package demo;

import java.io.IOException;
import java.sql.SQLException;
import org.apache.log4j.Logger;

public class Log4jExample
{
/* Get actual class name to be printed on */
static Logger log = Logger.getLogger(Log4jExample.class.getName());

public static void main(String[] args) throws IOException, SQLException
{
log.debug("Hello this is a debug message");
log.info("Hello this is an info message");
}
}

运行Log4jExample.java,然后查询DBNAME`数据库中的LOGS`表,会发现如下条目:

1
2
3
4
5
6
7
8
mysql> select * from LOGS;
+---------+---------------------+-------------------+-------+-------------------------------+
| user_id | date_time | logger | level | message |
+---------+---------------------+-------------------+-------+-------------------------------+
| | 2020-08-05 18:22:49 | demo.Log4jExample | DEBUG | Hello-this-is-a-debug-message |
| | 2020-08-05 18:22:51 | demo.Log4jExample | INFO | Hello this is an info message |
+---------+---------------------+-------------------+-------+-------------------------------+
2 rows in set (0.02 sec)

b站网页版的画中画模式没有弹幕

到今天为止,画中画模式确实没有弹幕.

一边看视频一边看评论

如果想要一遍看视频,一边看评论.则可以打开mini模式.

一边看视频一边做其他事情

网页全屏,然后使用分栏模式.浏览器占半个屏幕,其他软件占半个屏幕

使用插件

悬浮画中画播放器:
https://chrome.google.com/webstore/detail/%E6%82%AC%E6%B5%AE%E7%94%BB%E4%B8%AD%E7%94%BB%E6%92%AD%E6%94%BE%E5%99%A8/gdcfkpenohoihodlddbcgpdjhmdjepnb
B站的画中画功能,只是在当前网页中显示,无法一直显示在最顶层.