日志框架(logging framework)是一种软件模块,用于收集、存储和输出应用程序的日志信息。日志框架提供了一种标准化的方式来管理日志,使得开发者可以轻松地配置和控制日志输出。常见的日志框架包括Log4j、Logback、Java Util Logging等,它们提供了丰富的功能,如多级别日志记录、日志滚动、异步记录等。使用日志框架可以帮助开发者更好地理解应用程序的运行情况,从而更快地定位和解决问题。
Java中的日志技术旨在帮助开发者更好地追踪和调试应用程序,并且是一种记录重要事件和运行时信息的重要手段。Java自带了一个日志框架,即JUL(Java Util Logging),也支持其他开源日志框架如Log4j、Logback等。
Java中的日志技术有以下优势:
使用Java的日志技术是一种优秀的表现,能够让开发者更好地了解应用程序的运行状态,更快地定位和解决问题。
Logback是Java应用程序中的一种日志框架,它是 log4j 项目的设计师 Ceki Gülcü 在他对 log4j 进行发布、出版和使用后的经验教训的基础上,设计和实现的一种日志框架。Logback 可以代替log4j框架,它不仅具有与log4j兼容的API,而且也改进了一些缺陷。与log4j相比,Logback具有更高的性能和更好的灵活性,并且可以根据不同的日志级别进行日志记录。它支持不同的输出格式,如控制台、文件、流、Syslog等。它还提供了简单的配置文件和动态日志级别更改功能,这些功能可以方便地调试应用程序和诊断问题。Logback是由Apache许可证v2.0授权的自由软件。
官方网站:https://logback.qos.ch/index.html
Logback 主要分为三个技术模块:
Logback中有三个核心组件:Logger、Appender、Layout。
Logger负责记录日志信息,Appender负责输出日志信息,Layout负责格式化输出日志信息。三个组件共同协作,实现了高效、灵活、易用的日志管理。
logback读取配置文件的顺序如下:
如果以上所有文件都不存在,则使用Logback的默认配置。
Logback是一种灵活性高、可定制性强的日志记录框架,支持多种日志记录方式,包括控制台输出、文件输出、邮件发送等。
以下是Logback快速入门的步骤:
在Maven项目中,可以在pom.xml中添加以下依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
在src/main/resources目录下创建logback.xml文件,用于配置Logback的行为。
以下是一个简单的logback.xml配置文件的例子:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
CONSOLE :表示当前的日志信息是可以输出到控制台的。
-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--输出流对象 默认 System.out 改为 System.err-->
<target>System.out</target>
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度
%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread] : %msg%n</pattern>
</encoder>
</appender>
<!-- File是输出的方向通向文件的 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!--日志输出路径-->
<file>C:/code/itheima-dlei-data.log</file>
<!--指定日志文件拆分和压缩规则-->
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--通过指定压缩文件名称,来确定分割文件方式-->
<fileNamePattern>C:/code/itheima-data2-%d{yyyy-MMdd}.log%i.gz</fileNamePattern>
<!--文件拆分大小-->
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
</appender>
<!--
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
, 默认debug
<root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。
-->
<root level="all">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE" />
</root>
</configuration>
这个配置文件定义了一个名为"STDOUT"的控制台输出appender,使用一个pattern定义了输出格式,并将它附加到了根logger上。
在代码中使用Logback非常简单,只需创建一个Logger实例即可。例如:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyClass {
private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);
public void doSomething() {
LOGGER.debug("Some debug message.");
LOGGER.info("Some info message.");
LOGGER.warn("Some warning message.");
LOGGER.error("Some error message.");
}
}
上述代码中,通过org.slf4j.LoggerFactory类获取Logger实例,然后可以使用debug()、info()、warn()和error()方法记录日志。
现在,可以运行代码,并查看输出结果。在控制台上,应该会看到类似下面的输出:
11:08:21.136 [main] DEBUG MyClass - Some debug message.
11:08:21.136 [main] INFO MyClass - Some info message.
11:08:21.136 [main] WARN MyClass - Some warning message.
11:08:21.136 [main] ERROR MyClass - Some error message.
在实际的项目中,还可以结合使用Logback的其他功能,如异步输出、动态配置等。
Logback是一款可扩展的日志框架,允许以非常灵活的方式管理日志记录。下面是Logback的配置详解:
Logback会自动查找并加载名为“logback.xml”或“logback.groovy”的配置文件,如果没有找到这些文件,则会使用默认配置。
Logback的配置文件结构分为三个部分:configuration、appender、logger。其中,configuration定义了整个配置文件的根节点;appender定义了日志输出的目的地;logger定义了日志记录器及其输出级别等信息。
下面是一个较为完整的Logback配置文件示例:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-5level [%thread] %logger - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/var/log/myapp/myapp.log</file>
<encoder>
<pattern>%date %level [%thread] %logger - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.mycompany.myapp" level="DEBUG">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</logger>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>
(1)日志级别控制:
Logback支持5种日志级别:TRACE、DEBUG、INFO、WARN、ERROR。可以通过logger节点的level属性来设置日志记录器的级别,也可以在root节点中设置默认日志级别。
(2)日志格式控制:
可以通过encoder节点来设置日志的输出格式,常用的占位符有:%date、%level、%logger、%msg等。
(3)日志输出控制:
可以通过appender节点来配置日志的输出方式,包括文件输出、控制台输出等。
(4)日志分割:
可以通过TimeBasedRollingPolicy来设置日志的按时间分割,即每天/每小时/每分钟生成一个新的日志文件。
(5)异步日志:
可以通过AsyncAppender来实现异步输出日志,提高程序的性能。
以上是Logback配置的一些常用配置项,可以根据实际需求进行设置。
package com.itheima;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
商品(封装属性信息)
*/
public class Article {
private String name;
private double price;
private String type;
private int storage;
private String desc;
private Date createDate; // 上架时间
public Article(){
}
public Article(String name, double price, String type, int storage, String desc, Date createDate) {
this.name = name;
this.price = price;
this.type = type;
this.storage = storage;
this.desc = desc;
this.createDate = createDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getStorage() {
return storage;
}
public void setStorage(int storage) {
this.storage = storage;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getCreateDate() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss EEE a");
return sdf.format(createDate);
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
}
package com.itheima;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/**
需求:商品管理系统的模拟开发。
*/
public class ArticleManagerSystem {
/**
1、定义一个静态的Map集合存储全部店家和其商品橱柜
*/
public static Map<String, List<Article>> allArticles = new HashMap<>();
/**
3、静态代码块初始化一些测试数据
*/
static {
// 创建商家对象的橱柜
List<Article> articles1 = new ArrayList<>();
articles1.add(new Article("《干就对了-Java入门》", 100.5,"书籍",1000000,"作者:Dlei", new Date()));
articles1.add(new Article("《MySQL避免删库到跑路》", 9.9,"书籍",10,"作者:阿毛", new Date()));
allArticles.put("图书直营店", articles1);
List<Article> articles2 = new ArrayList<>();
articles2.add(new Article("育发液", 10.5,"药品",11000,"秃顶必备", new Date()));
articles2.add(new Article("防脱液", 19.9,"药品",110,"每天脱发100根就要用", new Date()));
allArticles.put("生发专营店", articles2);
}
/**
2、定义日志对象
*/
public static Logger logger = LoggerFactory.getLogger(ArticleManagerSystem.class);
public static Scanner sysSc = new Scanner(System.in);
public static void main(String[] args) {
logger.debug("有人进入系统~~");
while (true) {
System.out.println("=============欢迎进入商城=============");
System.out.println("1、查询全部商品信息");
System.out.println("2、上架商品");
System.out.println("3、下架商品");
System.out.println("4、修改商品");
System.out.println("5、根据条件查询商品");
System.out.println("6、退出系统");
System.out.println("请您输入操作的命令:");
int command = sysSc.nextInt();
switch (command) {
case 1:
// 查询商品信息(独立功能独立成方法)
showAllArticles();
break;
case 2:
// 上架商品
addArticle();
break;
case 3:
// 下架商品
deleteArticle();
break;
case 4:
// 修改商品信息
break;
case 5:
// 根据条件查询商品信息
break;
case 6:
// 退出系统
System.out.println("欢迎下次再来哦~~");
return;
default:
System.out.println("您输入的命令有毛病~~");
}
}
}
/**
商品下架:Map<String, List<Article>> allArticles = {店家名=[a1,a2] , 店家名2=[a1,a2] , 新店铺=[a1],...}
*/
public static void deleteArticle() {
System.out.println("===================下架商品======================");
// 1、判断是否有商品存在。
if(allArticles.size() == 0){
System.out.println("当前系统没有任何商品,不能下架了!");
return;
}
while (true) {
// 2、存在商品需要下架。
System.out.println("请您输入店铺名称:");
String storeName = sysSc.next();
// 2、判断这个店铺是否存在,根据店铺找橱柜。
List<Article> articles = allArticles.get(storeName);
if(articles != null) {
// 店铺存在,橱柜拿到了
// 3、请输入的商品名称,根据商品名称查询该店铺的该商品对象。再删除它
while (true) {
System.out.println("请您输入商品名称:");
String name = sysSc.next();
Article article = getArticleByName(articles,name);
// 4、判断这个商品对象是否存在
if(article != null){
// 定位到需要下架的商品对象,删除它
articles.remove(article);
System.out.println("下架成功~~~");
return;
}else {
System.out.println("您在当前店家下没有找到该商品,不存在该商品名称~~");
}
}
}else {
// 店铺不存在,没有这个橱柜
System.out.println("您输入的店铺压根没有~~~");
}
}
}
/**
去某个橱柜下,找出某个商品对象
* @param articles
* @param name
* @return
*/
public static Article getArticleByName(List<Article> articles, String name){
for (Article article : articles) {
if(article.getName().equals(name)) {
return article;
}
}
return null;
}
/**
添加商品信息:上架商品 Map<String, List<Article>> allArticles = {店家名=[a1,a2] , 店家名2=[a1,a2] , 新店铺=[]...}
*/
public static void addArticle() {
System.out.println("===================展示系统全部商品信息======================");
// 1、请您输入店铺名称
System.out.println("请您输入店铺名称:");
String storeName = sysSc.next();
List<Article> articles = null; // 先定义一个变量用于后期指向橱柜
// 2、判断这个店铺是否存在
if(allArticles.containsKey(storeName)) {
// 店铺已经存在,获取其橱柜
articles = allArticles.get(storeName);
}else {
// 这是一个新店铺,为它创建一个新橱柜
articles = new ArrayList<>();
// 新店铺要加入到系统中去
allArticles.put(storeName, articles);
}
// 3、创建一个新的商品对象封装数据
Article article = new Article();
System.out.println("请您输入商品名称:");
String name = sysSc.next();
article.setName(name);
while (true) {
Scanner sc = new Scanner(System.in);
try {
System.out.println("请您输入商品的价格:");
double price = sc.nextDouble();
if(price >= 0){
article.setPrice(price);
break;
}else {
System.out.println("价格必须是正数~~");
}
} catch (Exception e) {
System.out.println("价格数据不可以随便写,必须是数值~~");
}
}
System.out.println("请您输入商品类型:");
String type = sysSc.next();
article.setType(type);
System.out.println("请您输入商品描述:");
String desc = sysSc.next();
article.setDesc(desc);
while (true) {
Scanner sc = new Scanner(System.in);
try {
System.out.println("请您输入商品的库存:");
int storage = sc.nextInt();
if(storage >= 0){
article.setStorage(storage);
break;
}else {
System.out.println("库存必须是正数~~");
}
} catch (Exception e) {
System.out.println("库存数据不可以随便写,必须是数值~~");
}
}
// 上架时间
article.setCreateDate(new Date());
// 5、把商品对象加入到橱柜
articles.add(article);
System.out.println("恭喜你,商品上架成功了~~");
}
/**
展示商品 Map<String, List<Article>> allArticles = {店家名=[a1,a2] , ...}
*/
public static void showAllArticles() {
System.out.println("===================展示系统全部商品信息======================");
allArticles.forEach((name, articles) -> {
System.out.println("店铺:" + name);
for (Article article : articles) {
System.out.println("\t\t商品名称:" + article.getName());
System.out.println("\t\t商品价格:" + article.getPrice());
System.out.println("\t\t商品类型:" + article.getType());
System.out.println("\t\t商品库存:" + article.getStorage());
System.out.println("\t\t商品描述:" + article.getDesc());
System.out.println("\t\t商品上架时间:" + article.getCreateDate());
System.out.println("\t\t---------------------------");
}
});
}
}