Java日志系统框架的设计与实现
java软件架构设计方案

基础结构层(Infrastructure Layer) :该层为应用程序的数据存取提供服务,它可以是应用程 序本身的持久化机制,也可以是外部系统提供的数据访问的 Web Service 等。提供了能被其 它各层访问的通用技术框架,比如异常捕获与处理、日志、认证、授权、验证、跟踪、监 视、缓存等等。这些操作通常会横向散布在应用程序的各个层面,面向方面编程(AOP) 关注的就是如何在不影响对象本身处理逻辑的基础上来实现这些横切的却又必不可少的功 能点。
3.3 技术应用
3.3.1 数据库动态生成技术和 ORM 框架(Entity Framework) 通过使用使用 Hibernate+ant+xdoclet 技术,从而实现 hbm 文件和数据库从代码生成,这大大提高了 在开发阶段数据库应对业务变化的能力。 同时采用 ORM 框架,可以隐藏数据访问的细节,使得与数据库交互变得简单易行,并且完全不用考 虑具体的 SQL 语句,从而实现快速开发,也不会因开发人员的 T-SQL 水平而出现各种人为的性能问题。
2
设计优缺点
2.1 优点
2.1.1 提高系统的可测试性 多层(N-Layer)架构,层与层之间是低耦合的,增加各层的独立性,从而也提高了可测试性,这样 开发出来的系统才更加健壮。 2.1.2 对解决方案的维护和管理变得更加简单 层内高内聚、层间低耦合的结构,使得系统实现与分层组织方式变得非常灵活方便,维护和管理将 非常直接,高效。 2.1.3 增加系统的可移植性(模板化) 在企业软件开发中,许多模块都是可通用的,例如日志、异常、缓存、验证模块等等。通过分层, 可以容易的分离出通用的模块,便于迅速应用到其他的项目,实现模板化。 2.1.4 数据库根据编码自动生成 框架 Hibernate 技术优势,融入 ORM 框架,实现了从代码生成数据库的强大功能,在开发测试阶段 数据库可以很容易应对业务的变化,从而大大提高了开发人员的效率。 2.1.5 增强系统的可伸缩性 同样借助于分层的优势以及架构中各部分设计的高内聚性,可以各层就像独立的模块,互相独立, 增删各个独立的模块,不会影响到其他的模块或层的功能,这样就增强了系统的可伸缩性。 2.1.6 实现编码自动化避免人为性能问题 新框架采用 Hibernate 框架实现数据库访问的封装,日志、异常捕获以及 AOP 拦截等常用功能,减 少重复模块编码量的同时,也避免了因人为因素导致的性能问题。
日志监控告警系统的设计与实现

⽇志监控告警系统的设计与实现 基于的⽇志进⾏监控,监控需要⼀定规则,对触发监控规则的⽇志信息进⾏告警,告警的⽅式,是短信和邮件。
log4j---->error,info,debug 应⽤程序程序的⽇志 error级别 TimeOutException ⾓标越界IndexXXXException ......Error com.alibaba.jstorm.daemon.worker.WorkerData]-[INFO] Current worker taskList:[1, 2, 3, 4, 5, 6, 7] String.contains.(" taskList ")-------------->当订单量触发⼀千万时,告警通知,让⼤家庆祝下。
OrdertotalNum:1000万 kafaka⽣成集群的原理、分区 kafka消费者的负载均衡,kfakaSpout Kafka broker(核⼼机制,topic,分⽚,⽂件存储机制) Redis API学习 spout:从外部数据源中读取数据,然后转换为topology 架构图: DataSource:外部数据源 Spout:接收外部数据源的组件,将外部数据源转化成storm内部的数据,以Tuple为基本的传输单元下发给Bolt. Bolt:接受Spout发送的数据,或上游的bolt的发送的数据,根据业务逻辑进⾏处理,发送给下⼀个Bolt或者是存储到某种介质上,例如Redis。
Tuple:Storm内部中数据传输的基本单元,⾥⾯封装了⼀个List对象,⽤来保存数据。
StreamGroup:数据分钟策略,7种,shuffleGrouping,Non Grouping,FieldGrouping,Local or ShuffleGrouping. Nimbus:任务分配 Supervisor:接受任务,并启动worker,worker的数量是根据端⼝号来的。
java中使用日志

java中使用日志在Java中,日志是一种用于记录应用程序运行时信息的重要工具。
它可以帮助开发人员在应用程序中定位问题、调试代码以及监控系统运行情况。
Java提供了多种日志框架和API,最常用的是Java自带的java.util.logging和第三方库Log4j和Logback。
1. 使用java.util.logging:Java自带的日志框架是java.util.logging(JUL),它是在JDK中内置的一个简单的日志系统。
使用JUL,你需要创建一个Logger对象,然后使用不同级别的日志方法(如info、warning、severe)记录日志信息。
可以通过设置日志级别、输出格式和日志处理器来配置JUL。
优点,不需要引入额外的依赖,简单易用。
缺点,功能相对较弱,配置相对繁琐。
2. 使用Log4j:Log4j是一个功能强大且广泛使用的日志框架。
它提供了丰富的配置选项和灵活的日志级别控制。
使用Log4j,你需要在项目中引入Log4j的依赖,然后配置一个log4j.properties或log4j.xml文件,指定日志输出的格式、位置和级别。
在代码中,你可以通过获取Logger对象并调用不同级别的日志方法来记录日志信息。
优点,功能强大、配置灵活、性能较好。
缺点,需要引入额外的依赖。
3. 使用Logback:Logback是Log4j框架的改进版本,也是目前广泛使用的日志框架之一。
它提供了与Log4j类似的功能,但性能更好。
使用Logback,你需要在项目中引入Logback的依赖,然后配置一个logback.xml文件,指定日志输出的格式、位置和级别。
在代码中,你可以通过获取Logger对象并调用不同级别的日志方法来记录日志信息。
优点,性能较好、配置灵活、与Log4j兼容。
缺点,需要引入额外的依赖。
总结:以上是Java中使用日志的常见方式。
选择合适的日志框架取决于你的需求和偏好。
无论你选择哪种方式,良好的日志记录可以帮助你更好地理解应用程序的运行情况,快速定位问题并进行调试。
java 注解实现操作日志详细记录

一、背景介绍在Java开发中,操作日志记录是非常重要的一项功能。
通过记录用户的操作行为,我们可以追踪用户的操作轨迹,了解系统的使用情况,排查问题,甚至进行安全审计。
而注解是Java中非常重要的特性之一,它可以用来给类、方法、字段等元素添加元数据信息,这为我们实现操作日志的详细记录提供了非常便利的方式。
二、注解的定义在我们开始讨论如何利用注解来实现操作日志详细记录之前,首先我们要了解一下注解的定义和使用。
在Java中,注解是元数据的一种形式,它可以用来提供对程序元素的修饰性信息。
在定义注解时,我们需要使用`interface`关键字来进行声明,并且可以在注解中定义一些属性,这些属性可以在使用注解的时候进行赋值。
三、注解的应用场景在实际的开发中,我们可以在很多地方使用注解来实现操作日志的详细记录,比如在Controller层的方法上、Service层的方法上、DAO层的方法上等。
通过在关键的操作点上添加注解,我们可以实现在用户的操作触发时自动记录相关的日志信息。
四、注解实现操作日志详细记录的步骤为了让大家更好地理解如何利用注解来实现操作日志的详细记录,接下来我们将具体介绍一下使用注解来记录操作日志的步骤。
a. 定义注解我们需要定义一个注解来标识需要记录操作日志的方法。
比如我们可以定义一个`OperationLog`注解。
在定义注解的时候,我们可以添加一些属性,比如操作类型、操作描述等。
b. 解析注解接下来,我们需要编写一个注解解析器,用来解析被`OperationLog`注解标注的方法。
在解析器中,我们可以使用Java的反射机制来获取方法上的注解信息,并根据注解的属性来记录日志。
c. 记录日志我们需要编写一个日志记录器,用来实际记录操作日志。
当用户触发了被`OperationLog`注解标注的方法时,我们可以在日志记录器中将相关的操作信息记录下来,比如操作时间、操作人、操作类型、操作描述等。
五、使用案例为了让大家更好地了解如何利用注解来实现操作日志的详细记录,我们接下来通过一个简单的使用案例来进行演示。
日志设计模式记录系统运行日志的设计思路

日志设计模式记录系统运行日志的设计思路设计思路一:使用单例模式创建日志对象为了保证系统中只存在一个日志对象,可以使用单例模式来创建日志对象。
通过单例模式,我们可以确保系统中只有一个日志对象被创建,并且可以在任何地方访问该对象,方便记录系统的运行日志。
设计思路二:使用策略模式定义日志记录方式不同的系统可能有不同的日志记录方式,如文件记录、数据库记录或者控制台输出等,可以使用策略模式来定义不同的记录方式。
通过策略模式,我们可以在运行时动态切换记录方式,便于在不同的环境下灵活地记录系统日志。
设计思路三:使用观察者模式通知日志记录系统的各个模块可能需要记录运行日志,可以使用观察者模式实现模块与日志记录的解耦。
在日志设计中,将日志记录器设置为被观察者,各个模块设置为观察者,当模块发生特定事件时,通过观察者模式通知日志记录器进行相应的记录。
设计思路四:使用装饰器模式扩展日志记录功能日志记录时可能需要额外的功能扩展,如日志文件分割、日志级别控制等,可以使用装饰器模式来扩展日志记录功能。
通过装饰器模式,我们可以在运行时将不同的功能动态地添加到日志记录过程中,满足不同场景下的需求。
设计思路五:使用工厂模式创建日志记录对象为了降低系统的耦合度,可以使用工厂模式来创建日志记录对象。
通过工厂模式,我们可以将具体的日志记录对象的创建逻辑抽象出来,只需通过工厂类即可创建相应的日志记录对象,便于后续的维护和拓展。
综上所述,通过单例模式创建日志对象、使用策略模式定义记录方式、利用观察者模式通知记录、采用装饰器模式扩展功能,以及使用工厂模式创建记录对象等设计思路,可以实现一个灵活、可维护的系统运行日志记录系统。
这些设计模式的综合运用,不仅能满足系统的记录需求,也方便了系统的维护和拓展。
自定义日志框架实现

⾃定义⽇志框架实现⼀:前⾔ 写这个程序主要是⽤来理解⽣产者消费者模型,以及通过这个Demo来理解Redis的单线程取原⼦任务是怎么实现的和巩固⼀下并发相关的知识;这个虽然是个Demo,但是只要稍加改下Appender部分也是可以⽤于项⽬中的,假设项⽬⾥确实不需要log4j/logback之类的⽇志组件的时候;⼆:实现⽅式1.利⽤LinkedList作为MQ(还可以⽤jdk⾃带的LinkedBlockingQueue,不过这个Demo主要是为了更好的理解原理因此写的⽐较底层);2.利⽤⼀个Daemon线程作为消费者从MQ⾥实时获取⽇志对象/⽇志记录,并将它提交给线程池,由线程池再遍历所有的appender并调⽤它们的通知⽅法,这个地⽅还可以根据场景进⾏效率优化,如将循环遍历appender改为将每个appender都再此提交到线程池实现异步通知观察者;3.为⽣产者提供log⽅法作为⽣产⽇志记录的接⼝,⽆论是⽣产⽇志对象还是消费⽇志对象在操作队列时都需要对队列加锁,因为个⼈⽤的是⾮并发包⾥的;4.消费者在获取之前会先判断MQ⾥是否有数据,有则获取并提交给线程池处理,否则wait;5.⽣产者⽣产了⽇志对象后通过notify通知消费者去取,因为只有⼀个消费者,⽽⽣产者是不会wait的因此只需要notify⽽不⽤notifyAll6.剩下的就看代码来说明吧;三:代码1.MyLogger类的实现package me.study.mqlogger.log;import java.io.IOException;import java.io.PrintWriter;import java.io.Writer;import java.text.SimpleDateFormat;import java.util.*;import java.util.concurrent.*;import java.util.concurrent.atomic.AtomicLong;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;import static me.silentdoer.mqlogger.log.MyLogger.LogLevel.DEBUG;import static me.silentdoer.mqlogger.log.MyLogger.LogLevel.ERROR;/*** @author wangsong* @version 1.0* @description 这⾥只是做⼀个简单的logger实现,不提供Appender之类的功能,主要是⽤来学习⽣产者和消费者及MQ的实现原理* @date 9/26/19 6:07 PM*/public class MyLogger{private LogLevel loggerLevel = DEBUG;private String charset = "UTF-8"; // 暂且没⽤,但是当需要序列化时是可能⽤到的;// TODO 也可以直接⽤LinkedQueue,然后⼿动通过ReentrantLock来实现并发时的数据安全(synchronized也可)//private BlockingQueue<LogRecord> queue = new LinkedBlockingQueue<LogRecord>(); // 可以理解为⽀持并发的LinkedList// TODO 想了⼀下既然是要学习原理⼲脆就实现的更底层⼀点private final Queue<LogRecord> records = new LinkedList<LogRecord>();// TODO ⽤于记录⽣产了多少条⽇志,可供外部获取private AtomicLong produceCount = new AtomicLong(0);// TODO ⽤于记录消费了多少条⽇志private AtomicLong consumeCount = new AtomicLong(0);// TODO ⽇志记录的Consumerprivate Thread consumer = new LogDaemon();public MyLogger(){consumer.setDaemon(true);consumer.start();}/*** 对外提供的接⼝,即log⽅法就是⽣产者⽤于⽣产⽇志数据的接⼝* @param msg* @param level*/public void log(String msg, LogLevel level){Date curr = generateCurrDate();log(new LogRecord(level, msg, curr));}/*** 对外提供的接⼝,即log⽅法就是⽣产者⽤于⽣产⽇志数据的接⼝* @param msg*/public void log(String msg){Date curr = generateCurrDate();log(new LogRecord(this.loggerLevel, msg, curr));}/*** 给⽣产者(即调⽤log的⽅法都可以理解为⽣产者在⽣产⽇志对象)提供⽤于⽣产⽇志记录的接⼝* @param record*/public void log(LogRecord record){// ReentrantLock可以替代synchronized,不过当前场景下synchronized已经⾜够synchronized (this.records){ // TODO 如果⽤的是LinkedBlockingQueue是不需要这个的this.records.offer(record);this.produceCount.incrementAndGet();this.records.notify(); // TODO 只有⼀个线程会records.wait(),因此notify()⾜够}}// TODO 类似Redis的那个单线程,⽤于读取命令对象,⽽这⾥则是⽤于读取LogRecord并通过appender将数据写到相应位置private class LogDaemon extends Thread{private volatile boolean valid = true;// 充当appenders的⾓⾊private List<Writer> appenders = null;private ExecutorService threadPool = new ThreadPoolExecutor(1, 3, 180000, LISECONDS, new LinkedBlockingQueue<Runnable>(1024));@Overridepublic void run() {while(this.valid){// TODO 根据最少知道原则,在这⾥不要去想整体⾥是否存在打断此线程的地⽅,你就认为此线程是可能被外界打断的即可,因此需要做⼀定处理 try {synchronized (MyLogger.this.records) {if (MyLogger.this.records.size() <= 0) {MyLogger.this.records.wait();}final LogRecord firstRecord = MyLogger.this.records.poll();MyLogger.this.consumeCount.incrementAndGet();//threadPool.submit()threadPool.execute(() -> MyLogger.this.notifyAppender(this.appenders, firstRecord));}}catch (InterruptedException ex){this.valid = false;ex.printStackTrace();}catch (Throwable t){t.printStackTrace();}}}}private void notifyAppender(final List<Writer> appenders, final LogRecord record) {if(appenders == null){PrintWriter writer = new PrintWriter(record.level == ERROR ? System.err : System.out);writer.append(record.toString());writer.flush();}else{// TODO 这种是同步的⽅式,如果是异步的⽅式可以将每个appender的执⾏都由⼀个Runnable对象包装,然后submit给线程池(或者中间加个中间件) for(Writer writer : appenders){try {writer.append(record.toString());}catch (IOException ex){ex.printStackTrace();}}}}/*** ⽤于产⽣当前时间的模块,防⽌因为并发⽽导致LogRecord的timestamp根实际情况不符*/private Lock currDateLock = new ReentrantLock(); // 直接⽤synchronized亦可private Date generateCurrDate(){currDateLock.lock();Date result = new Date();currDateLock.unlock();return result;}// ⽣产者⽣产的数据对象public static class LogRecord{private LogLevel level;private String msg;private Date timestamp;private static final SimpleDateFormat DEFAULT_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); private SimpleDateFormat dateFormat = DEFAULT_DATE_FORMAT;/*public LogRecord(){this(INFO, "");}*/public LogRecord(LogLevel level, String msg){this(level, msg, new Date()); // 还是最好由外界设置timestamp,否则⾼并发下会⽐较不准}// TODO 最好⽤这个,不然⾼并发下timestamp容易出现顺序不准确的情况。
java web 个人笔记系统首页模块的设计和实验

设计和实验一个Java Web个人笔记系统首页模块通常涉及前端和后端的开发。
以下是一个基本的设计和实验流程,分为几个关键步骤:1. 需求分析首先,明确首页模块的功能需求。
个人笔记系统的首页通常需要展示以下内容:用户登录/注册状态笔记列表(可能包括最新笔记、热门笔记等)搜索框(用于搜索笔记)导航链接(如:创建新笔记、查看个人资料等)2. 技术选型选择适合的技术栈。
对于Java Web开发,常见的选择包括:后端:Spring Boot, Spring MVC, Hibernate/JPA等前端:HTML, CSS, JavaScript, Thymeleaf, Bootstrap等数据库:MySQL, PostgreSQL, H2等3. 数据库设计设计数据库模式以存储笔记和其他相关数据。
例如,你可能需要以下表格:users表:存储用户信息(如id, username, password等)notes表:存储笔记信息(如id, title, content, user_id, created_at等)4. 后端开发使用选定的后端技术创建API端点以处理前端请求。
例如:/login:处理用户登录/register:处理用户注册/notes:获取笔记列表/notes/create:创建新笔记5. 前端开发使用HTML, CSS和JavaScript构建用户界面。
利用前端框架(如Bootstrap)来加快开发速度并确保跨浏览器兼容性。
6. 集成和测试将前端和后端集成在一起,并进行测试以确保一切正常工作。
测试可以包括单元测试、集成测试和端到端测试。
7. 部署将应用程序部署到生产环境。
这通常涉及将应用程序打包(如使用Maven 或Gradle)并上传到服务器,然后在服务器上配置和运行应用程序。
实验步骤设置开发环境:安装Java, IDE(如IntelliJ IDEA或Eclipse), 数据库等。
创建项目:使用IDE创建一个新的Java Web项目,并配置所需的依赖项。
运维日志系统的设计与实现

运维日志系统的设计与实现随着互联网时代的到来,IT系统的规模和复杂度不断增长,系统出现故障的概率也越来越高。
而这些故障通常需要通过分析系统运行过程中产生的日志来进行排查和解决。
因此,建立一个高效、可靠的运维日志系统成为了现代企业必不可少的一个重要措施。
本文将从需求分析入手,介绍运维日志系统的设计与实现,探讨如何构建一个可扩展、易于维护的系统。
1.需求分析运维日志系统需要具备以下功能:(1)所有服务器的日志都能够被收集和存储。
(2)能够对日志进行检索、查询、分析和统计。
(3)支持实时监控和告警。
(4)系统需要具备扩展性,支持添加新的数据源、分布式部署等功能。
(5)必须保证数据安全,防止数据泄露或被篡改。
2.架构设计基于需求分析,我们可以采用以下架构设计:(1)日志收集:所有服务器上的日志通过Agent收集,并发送到日志收集服务器。
(2)日志存储:日志收集服务器将收集到的日志存储到数据库中。
(3)日志检索和分析:用户可以通过Web界面对存储在数据库中的日志进行检索、查询、分析和统计。
(4)实时监控和告警:系统可以根据设定的规则对日志进行实时监控,并在出现异常时发出告警。
(5)数据库:由于日志数据量较大,需要选择性能优良的数据库。
可以选择NoSQL数据库,如Elasticsearch等。
3.实现过程(1)安装Agent:在需要收集日志的服务器上安装Agent,并配置Agent的日志收集规则。
(2)日志收集:Agent会按照设定的规则收集指定日志文件,并将收集到的日志发送到日志收集服务器。
(3)日志存储:日志收集服务器将收集到的日志存储到Elasticsearch数据库中。
(4)日志检索和分析:用户可以通过Web界面对Elasticsearch 中的日志进行检索、查询、分析和统计。
(5)实时监控和告警:系统可以根据设定的规则对Elasticsearch中的日志进行实时监控,并在出现异常时发出告警。
4.系统优化为了使运维日志系统更加高效、可靠,我们可以采用以下优化措施:(1)压缩日志文件:对于一些不常被查询的日志文件,可以通过压缩的方式减小存储空间。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Java日志系统框架的设计与实现在Java领域,存在大量的日志组件,open-open收录了21个日志组件。
日志系统作为一种应用程序服务,对于跟踪调试、程序状态记录、崩溃数据恢复都有着重要的作用,我们可以把Java日志系统看作是必不可少的跟踪调试工具。
1.简介日志系统是一种不可或缺的跟踪调试工具,特别是在任何无人职守的后台程序以及那些没有跟踪调试环境的系统中有着广泛的应用。
长期以来,日志系统作为一种应用程序服务,对于跟踪调试、程序状态记录、崩溃数据恢复都有非常现实的意义。
这种服务通常以两种方式存在:1.日志系统作为服务进程存在。
Windows中的的事件日志服务就属于这种类型,该类型的日志系统通常通过消息队列机制将所需要记录的日志由日志发送端发送给日志服务。
日志发送端和日志保存端通常不在同一进程当中,日志的发送是异步过程。
这种日志服务通常用于管理员监控各种系统服务的状态。
2.日志系统作为系统调用存在。
Java世界中的日志系统和Unix环境下诸多守护进程所使用的日志系统都属于这种类型。
日志系统的代码作为系统调用被编译进日志发送端,日志系统的运行和业务代码的运行在同一进程空间。
日志的发送多数属于同步过程。
这种日志服务由于能够同步反映处系统运行状态,通常用于调试跟踪和崩溃恢复。
本文建立的日志系统基本属于第二种类型,但又有所不同。
该日志系统将利用Java线程技术实现一个既能够反映统一线程空间中程序运行状态的同步日志发送过程,又能够提供快速的日志记录服务,还能够提供灵活的日志格式配置和过滤机制。
1.1系统调试的误区在控制台环境上调试Java程序时,此时往控制台或者文本文件输出一段文字是查看程序运行状态最简单的做法,但这种方式并不能解决全部的问题。
有时候,对于一个我们无法实时查看系统输出的系统或者一个确实需要保留我们输出信息的系统,良好的日志系统显得相当必要。
因此,不能随意的输出各种不规范的调试信息,这些随意输出的信息是不可控的,难以清除,可能为后台监控、错误排除和错误恢复带来相当大的阻力。
1.2日志系统框架的基本功能一个完备的日志系统框架通常应当包括如下基本特性:所输出的日志拥有自己的分类:这样在调试时便于针对不同系统的不同模块进行查询,从而快速定位到发生日志事件的代码。
日志按照某种标准分成不同级别:分级以后的日志,可以用于同一分类下的日志筛选。
支持多线程:日志系统通常会在多线程环境中使用,特别是在Java系统当中,因此作为一种系统资源,日志系统应当保证是线程安全的。
支持不同的记录媒介:不同的工程项目往往对日志系统的记录媒介要求不同,因此日志系统必须提供必要的开发接口,以保证能够比较容易的更换记录介质。
高性能:日志系统通常要提供高速的日志记录功能以应对大系统下大请求流量下系统的正常运转。
稳定性:日志系统必须是保持高度的稳定性,不能因为日志系统内部错误导致主要业务代码的崩溃。
1.3常用日志系统简介在Java世界中,以下三种日志框架比较优秀:1)Log4J最早的Java日志框架之一,由Apache基金会发起,提供灵活而强大的日志记录机制。
但是其复杂的配置过程和内部概念往往令使用者望而却步。
2)JDK1.4LoggingFramework继Log4J之后,JDK标准委员会将Log4J的基本思想吸收到JDK当中,在JDK1.4中发布了第一个日志框架接口,并提供了一个简单实现。
3)CommonsLoggingFramwork该框架同样是Apache基金会项目,其出现主要是为了使得Java项目能够在Log4J和JDK1.4lLoggingFramework的使用上随意进行切换,因此该框架提供了统一的调用接口和配置方法。
2.系统设计由于Log4J得到广泛应用,从使用者的角度考虑,本文所设计的框架,采用了部分Log4J 的接口和概念,但内部实现则完全不同。
使用Java实现日志框架,关键的技术在于前面提及的日志框架特性的内部实现,特别是:日志的分类和级别、日志分发框架的设计、日志记录器的设计以及在设计中的高性能和高稳定性的考虑。
2.1系统架构日志系统框架可以分为日志记录模块和日志输出模块两大部分。
日志记录模块负责创建和管理日志记录器(Logger),每一个Logger对象负责按照不同的级别(LoggerLevel)接收各种记录了日志信息的日志对象(LogItem),Logger对象首先获取所有需要记录的日志,并且同步地将日志分派给日志输出模块。
日志输出模块则负责日志输出器(Appender)的创建和管理,以及日志的输出。
系统中允许有多个不同的日志输出器,日志输出器负责将日志记录到存储介质当中。
日志记录器Logger是整个日志系统框架的用户使用接口,程序员可以通过该接口记录日志,为了实现对日志进行分类,系统设计允许存在多个Logger对象,每一个Logger负责一类日志的记录,Logger类同时实现了对其对象本身的管理。
LoggerLevel类定义了整个日志系统的级别,在客户端创建和发送日志时,这些级别会被使用到。
Logger对象在接收到客户端创建和发送的日志消息时,同时将该日志消息包装成日志系统内部所使用的日志对象LogItem,日志对象除了发送端所发送的消息以外,还会包装诸如发送端类名、发送事件、发送方法名、发送行号等等。
这些额外的消息对于系统的跟踪和调试都非常有价值。
包装好的LogItem最终被发送给输出器,由这些输出器负责将日志信息写入最终媒介,输出器的类型和个数均不固定,所有的输出器通过AppenderManager进行管理,通常通过配置文件即可方便扩展出多个输出器。
2.2日志记录部分的设计如前文所述,日志记录部分负责接收日志系统客户端发送来的日志消息、日志对象的管理等工作。
下面详细描述了日志记录部分的设计要点:1.日志记录器的管理系统通过保持多个Logger对象的方式来进行日志记录的分类。
每一个Logger对象代表一类日志分类。
因此,Logger对象的名称属性是其唯一标识,通过名称属性获取一个Logger 对象:1.LoggerLoggerlogger=Logger.getLogger(“LoggerName”);一般的,使用类名来作为日志记录器的名称,这样做的好处在于能够尽量减少日志记录器命名之间的冲突(因为Java类使用包名),同时能够将日志记录分类得尽可能的精细。
因此,假定有一UserManager类需要使用日志服务,则更一般的使用方式为:2.LoggerLoggerlogger=Logger.getLogger(UserManager.class);2.日志分级的实现按照日志目的不同,将日志的级别由低到高分成五个级别:◆DEBUG-表示输出的日志为一个调试信息;◆INFO-表示输出的日志是一个系统提示;◆WARN-表示输出的日志是一个警告信息;◆ERROR-表示输出的日志是一个系统错误;◆FATAL-表示输出的日志是一个导致系统崩溃严重错误。
这些日志级别定义在LoggerLevel接口中,被日志记录器Logger在内部使用。
而对于日志系统客户端则可使用Logger类接口对直接调用并输出这些级别的日志,Logger的这些接口描述如下:3.publicvoiddebug(Stringmsg);//输出调试信息4.publicvoidinfo(Stringmsg);//输出系统提示5.publicvoidwarn(Stringmsg);//输出警告信息6.publicvoidfatal(Stringmsg);//输出系统错误7.publicvoiderror(Stringmsg);//输出严重错误通过对Logger对象上这些接口的调用,直接为日志信息赋予了级别属性,这样为后继的按照不同级别进行输出的工作奠定了基础。
3.日志对象信息的获取日志对象上包含了一条日志所具备的所有信息。
通常这些信息包括:输出日志的时间、Java类、类成员方法、所在行号、日志体、日志级别等等。
在JDK1.4中可以通过在方法中抛出并且捕获住一个异常,则在捕捉到的异常对象中已经由JVM自动填充好了系统调用的堆栈,在JDK1.4中则可以使用ng.StackTraceElement获取到每一个堆栈项的基本信息,通过对日志客户端输出日志方法调用层数的推算,则可以比较容易的获取到StackTraceElement对象,从而获取到输出日志时的Java类、类成员方法、所在行号等信息。
在JDK1.3或者更早的版本中,相应的工作则必须通过将异常的堆栈信息输出到字符串中,并分析该字符串格式得到。
2.3日志输出部分的设计日志输出部分的设计具有一定的难度,在本文设计的日志系统中,日志的输出、多线程的支持、日志系统的扩展性、日志系统的效率等问题都交由日志输出部分进行管理。
1.日志输出器的继承结构在日志的输出部分采用了二层结构,即定义了一个抽象的日志输出器(AbstractLoggerAppender),然后从该抽象类继承出实际的日志输出器。
AbstractLoggerAppender定义了一系列的对日志进行过滤的方法,而具体输出到存储媒介的方法则是一个抽象方法,由子类实现。
在系统中默认实现了控制台输出器和文件输出器两种,其中控制台输出器的实现颇为简单。
2.文件输出器的内部实现在日志记录部分的实现中,并没有考虑多线程、高效率等问题,因此文件输出器必须考虑这些问题的处理。
在文件输出器内部使用ng.Vector定义了一个线程安全的高速缓冲,所有通过日志记录部分分派到文件输出器的日志被直接放置到该高速缓冲当中。
同时在文件输出器内部定义一个工作线程,负责定期将高速缓冲中的内容保存到文件,在保存的过程中同时可以进行日志文件的备份等工作。
由于采用了高速缓冲的结构,很显然日志客户端的调用已经不再是一个同步调用,从而不再会需要等到文件操作后才返回,提高的系统调用的速度。
2.4设计难点通过上述设计,一个具有良好扩展能力的高性能日志系统框架就已经具有了一定的雏形。
在设计过程中几个难点问题需要进一步反思。
一、是否整个系统应当采用完全异步的结构,通过类似于消息机制的方式来进行由日志客户端发送日志给日志系统。
这种方式可以作为日志系统框架另一种运行方式,在后继设计中加以考虑。
二、在文件输出器中可以看到,目前虽然可以扩展多个日志输出器,但是目前提供的抽象类中仅仅提供了对日志的过滤机制,而没有提供的缓存机制,目前的缓存机制被放在文件输出器中实现,因此在未来的进一步设计中,可以将文件输出器中的缓存机制上移到抽象类当中。
2.5设计模式在设计过程中我们特别注意使用了数个经典的设计模式。
如:Logger对象的创建使用了工厂方法模式(FactoryMethod)、由AbstractLoggerAppender和ConsoleAppender以及FileAppender构成了策略模式(Strategy),除此以外,还大量使用了单例模式(Singleton)。