方法级别的java日志输出控制(二)集群

方法级别的java日志输出控制(一)这篇文章中主要讨论了通过properties配置文件以及AOP技术批量控制方法级别的日志输出。

用properties配置文件的好处是不用更改程序即可控制日志的输出,然而大型的应用通常是分布式的,会有很多的服务器,需要更改全部服务器上的配置文件,然后再重启应用。这将会是一件非常麻烦的事情。事实上在大型集群应用中有更好的方法实现他。zookeeper的特性决定着它有一个应用场景就是集群配置中心。本文不介绍zookeeper原理以及搭建,将直接使用zookeeper实现实时更改配置。本文表面上是做方法级别的日志输出控制在集群中的实现,但实际上完全是一个zookeeper集群配置中心的简单实现。

先看ZkHelper工具类:

package com.lf.testLog4j.Util;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

/**
 * Created by lufei3 on 2015/7/29.
 */
public class ZkHelper {
    private static final Logger logger = LogManager.getLogger(ZkHelper.class);
    Stat stat = null;
    private static ZooKeeper zk;
    static private ZkHelper instance;
    //本机搭建的zk伪集群
    public static String hostports = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";
    //存放所有的log方法配置键值对,系统系统时会首先从zk上拿到所有的节点数据放在map里,只有当节点发生改变的时候
    // 才会更新map,这样避免每取一个节点就去zk里面读数据,从而节省了io时间。
    public static Map<String, String> nodeList;

    //等待zk连接的方法,若没有连接时会报错
    public static void waitUntilConnected(ZooKeeper zooKeeper, CountDownLatch connectedLatch) {
        if (ZooKeeper.States.CONNECTING == zooKeeper.getState()) {
            try {
                connectedLatch.await();
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
        }
    }

    //watcher类,该watcher可以watch到zk连接以及每个节点的变化
    static class ConnectedWatcher implements Watcher {

        private CountDownLatch connectedLatch;

        ConnectedWatcher(CountDownLatch connectedLatch) {
            this.connectedLatch = connectedLatch;
        }

        @Override
        public void process(WatchedEvent event) {
            if (event.getState() == Event.KeeperState.SyncConnected) {
                connectedLatch.countDown();
            }
            logger.info("process : " + event.getType() + " " + event.getPath());
            //当节点发生变化时更新map
            nodeListMapRefresh(String.valueOf(event.getType()), event.getPath());
        }
    }

    public static void nodeListMapRefresh(String eventType, String path) {
        if (eventType.equals("NodeDeleted")) {
            nodeList.remove(path.substring(path.lastIndexOf("/") + 1));
        } else if (eventType.equals("NodeCreated") || eventType.equals("NodeDataChanged")) {
            nodeList.put(path.substring(path.lastIndexOf("/") + 1), getNode(path));
        }
    }

    //单例
    public static ZkHelper Instance() {
        if (instance == null) {
            instance = new ZkHelper(hostports, 1000);
        }
        return instance;
    }

    //初始化连接并装载节点列表
    public boolean Init(String hostports, int times) {
        try {
            CountDownLatch connectedLatch = new CountDownLatch(1);
            Watcher watcher = new ConnectedWatcher(connectedLatch);
            zk = new ZooKeeper(hostports, times, watcher);
            waitUntilConnected(zk, connectedLatch);
            nodeList = getNodeList("/root/log");
        } catch (Exception e) {
            System.out.println(e);
            return false;
        }
        return true;
    }

    //构造方法,构造时完成初始化
    ZkHelper(String hostports, int times) {
        Init(hostports, times);
    }

    //获取节点信息
    public static String getNode(String keys) {
        String re = "";
        String ppath = keys;
        Stat stat = new Stat();
        try {
            byte[] b = zk.getData(ppath, false, stat);    //获取节点的信息及存储的数据
            re = new String(b);
        } catch (Exception e) {
            System.out.println(e);
        }
        return re;
    }

    //创建节点或更新节点数据
    public void create(String key, String value) {
        try {
            stat = null;
            stat = zk.exists("/root", false);
            if (stat == null) {
                zk.create("/root", "rootData".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
            stat = null;
            stat = zk.exists("/root/log", false);
            if (stat == null) {
                zk.create("/root/log", "logData".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
            stat = null;
            stat = zk.exists("/root/log/" + key, true);
            if (stat == null) {
                zk.create("/root/log/" + key, value.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            } else {
                zk.setData("/root/log/" + key, value.getBytes(), -1);
            }

        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //删除节点
    public void delete(String key) {
        stat = null;
        try {
            stat = zk.exists("/root/log/" + key, true);
            if (stat != null) {
                zk.delete("/root/log/" + key, -1);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }

    //获取节点列表
    public Map getNodeList(String path) {
        Map<String, String> map = new HashMap<String, String>();
        List<String> nodeList = null;
        try {
            nodeList = zk.getChildren(path, true);
            for (String s : nodeList) {
                byte[] bytes = zk.getData(path + "/" + s, true, null);
                map.put(s, new String(bytes));
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return map;
    }

}

  

LogConfig类:

package com.lf.zookeeperConfigCenter;

import com.lf.testLog4j.Util.ZkHelper;

public class LogConfig {
public static void main(String args[]) {
ZkHelper conf = ZkHelper.Instance();
// conf.delete("TestLogAOP.test2");
// conf.delete("TestLogAOP.test");
conf.create("TestLogAOP.test2", "{\"level\":\"TRACE\",\"on\":1}");
String str = conf.getNode("/root/log/TestLogAOP.test2");
conf.create("TestLogAOP.test", "{\"level\":\"TRACE\",\"on\":0}");
}
}

  

  这个类主要完成了zk的节点创建两个节点的路径和值分别为

/root/log/TestLogAOP.test,{"level":"TRACE","on":0}

/root/log/TestLogAOP.test2,{"level":"TRACE","on":1}与上篇的properties文件配置一致。完善的配置中心应当有一个图形界面。

LogActiveZK类:

package com.lf.testLog4j.aop;

import com.google.gson.Gson;
import com.lf.testLog4j.Util.ZkHelper;
import com.lf.testLog4j.common.CommonLogUtil;
import com.lf.testLog4j.domain.ConfigLog;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import java.util.Map;

/**
 * Created by lufei3 on 2015/7/29.
 */
public class LogActiveZK {
    private static final Logger logger = LogManager.getLogger(LogActive.class);
    public void before(JoinPoint jp){
        ZkHelper zkHelper = ZkHelper.Instance();
        Map map = zkHelper.nodeList;
        Gson gson = new Gson();
        ConfigLog configLog = null;
        String cName = jp.getThis().toString();
        Object[] args = jp.getArgs();  //获得参数列表
        String className = cName.substring(cName.lastIndexOf(".")+1,cName.lastIndexOf("@"));
        String methodName = jp.getSignature().getName();   //获得方法名
        String key = className + "." + methodName;
        String configValue = (String) map.get(key);
        try {
            configLog = gson.fromJson(configValue,ConfigLog.class);
        } catch (Exception e) {
            logger.error("Gson Format Exception!! logLevel:{}",configValue);
            e.printStackTrace();
            return;
        }
        if(configLog == null) {
            return;
        }
        String logLevel = configLog.getLevel();
        int offset = configLog.getOn();
        if(StringUtils.isBlank(logLevel)){
            logger.warn("method:{} log not config", key);
            return;
        }
        if(CommonLogUtil.isInfoEnable(logLevel, offset)) {
            logger.info("====Method:{};", key);
            if(args.length <=0){
                logger.info("===={}方法没有参数", methodName);
            } else{
                for(int i=0; i<args.length; i++){
                    logger.info("====参数 {}:{} ", (i + 1), args[i]);
                }
            }
        }
    }
    public void after(){
        logger.info("调用完毕!!");
    }
}

  Spring配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
       default-autowire="byName">
    <bean id="LogActiveZK" class="com.lf.testLog4j.aop.LogActiveZK"></bean> <!--将日志类注入到bean中。-->
    <bean id="testLogAOP" class="com.lf.testLog4j.service.TestLogAOP"></bean>

    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <aop:config>
        <!--拦截service层的所有方法-->
        <aop:pointcut id="service" expression="execution(* com.lf.testLog4j.service.*.*(..))"/>
        <aop:aspect id="log" ref="LogActiveZK">
            <aop:before pointcut-ref="service" method="before"/>
            <aop:after pointcut-ref="service" method="after"/>
        </aop:aspect>
    </aop:config>
</beans>

  TestLogAOP类:

package com.lf.testLog4j.service;

import org.springframework.stereotype.Component;

/**
 * Created by lufei3 on 2015/7/14.
 */
@Component
public class TestLogAOP {
    public void test(){
        System.out.println("测试类的test方法被调用");
    }
    public void test2() {
        System.out.println("测试2的方法被调用!");
    }
}

  测试:

package com.lf.testLog4j;

import com.lf.testLog4j.service.TestLogAOP;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by lufei3 on 2015/7/14.
 */

public class LogAOPMain {
    public static void main(String... args) {
        ApplicationContext act = new ClassPathXmlApplicationContext("spring/spring-config.xml");
        TestLogAOP testLogAOP = (TestLogAOP) act.getBean("testLogAOP");
        testLogAOP.test();
        testLogAOP.test2();
    }
}

结果: 

 

可见成功用zk作为log配置中心并通过Spring AOP拦截了方法级别的log输出。

时间: 2024-10-24 11:48:30

方法级别的java日志输出控制(二)集群的相关文章

Java 日志体系(二)jcl 和 slf4j

Java 日志体系(二)jcl 和 slf4j <java 日志体系(一)统一日志>:https://www.cnblogs.com/binarylei/p/9828166.html <Java 日志体系(二)jcl 和 slf4j>:https://www.cnblogs.com/binarylei/p/10781582.html 前面介绍了 jdk 自带的 logging.log4j1.log4j2.logback 等实际的日志框架.对于开发者而言,每种日志都有不同的写法.如果

【web】 亿级Web系统搭建——单机到分布式集群

当一个Web系统从日访问量10万逐步增长到1000万,甚至超过1亿的过程中,Web系统承受的压力会越来越大,在这个过程中,我们会遇到很多的问题.为了解决这些性能压力带来问题,我们需要在Web系统架构层面搭建多个层次的缓存机制.在不同的压力阶段,我们会遇到不同的问题,通过搭建不同的服务和架构来解决. Web负载均衡 Web负载均衡(Load Balancing),简单地说就是给我们的服务器集群分配“工作任务”,而采用恰当的分配方式,对于保护处于后端的Web服务器来说,非常重要. 负载均衡的策略有很

很不错的文章---【问底】徐汉彬:亿级Web系统搭建——单机到分布式集群

原文:很不错的文章---[问底]徐汉彬:亿级Web系统搭建--单机到分布式集群 [导读]徐汉彬曾在阿里巴巴和腾讯从事4年多的技术研发工作,负责过日请求量过亿的Web系统升级与重构,目前在小满科技创业,从事SaaS服务技术建设. 大规模流量的网站架构,从来都是慢慢“成长”而来.而这个过程中,会遇到很多问题,在不断解决问题的过程中,Web系统变得越来越大.并且,新的挑战又往往出现在旧的解决方案之上.希望这篇文章能够为技术人员提供一定的参考和帮助. 以下为原文 当一个Web系统从日访问量10万逐步增长

Centos7下ELK+Redis日志分析平台的集群环境部署记录

之前的文档介绍了ELK的架构基础知识,下面简单记录下ELK结合Redis搭建日志分析平台的集群环境部署过程,大致的架构如下: + Elasticsearch是一个分布式搜索分析引擎,稳定.可水平扩展.易于管理是它的主要设计初衷 + Logstash是一个灵活的数据收集.加工和传输的管道软件 + Kibana是一个数据可视化平台,可以通过将数据转化为酷炫而强大的图像而实现与数据的交互将三者的收集加工,存储分析和可视转化整合在一起就形成了ELK. 基本流程:1)Logstash-Shipper获取日

MySQL 8.0.11 innodb cluster 运维管理手册之二--集群搭建

MySQL 8.0.11 innodb cluster 高可用集群部署运维管理手册之二 集群建设 作者 方连超 基础环境 系统:centos 7.5Mysql:8.0.11 二进制包Mysqlshell: 8.0.11 rpm 包Mysql router: 8.0.11 二进制包 架构: 192.168.181.101 myrouter1 Keepalived.MySQL-shell.MySQL-Router.MySQL-client 192.168.181.102 myrouter2 Keep

输出redis cluster集群所有节点指定的参数的配置

需要:实现类似redis-trib.rb call 命令的功能,输出redis cluster集群所有节点指定的参数的配置 redis-trib.rb的输出 [[email protected] ~]$ redis-trib.rb call 5.5.5.101:29001 config get *timeout* /usr/local/ruby2.5.1/lib/ruby/gems/2.5.0/gems/redis-3.3.0/lib/redis/client.rb:459: warning:

亿级Web系统搭建——单机到分布式集群

原文转自:http://kb.cnblogs.com/page/509402/ 当一个Web系统从日访问量10万逐步增长到1000万,甚至超过1亿的过程中,Web系统承受的压力会越来越大,在这个过程中,我们会遇到很多的问题.为了解决这些性能压力带来问题,我们需要在Web系统架构层面搭建多个层次的缓存机制.在不同的压力阶段,我们会遇到不同的问题,通过搭建不同的服务和架构来解决. Web负载均衡 Web负载均衡(Load Balancing),简单地说就是给我们的服务器集群分配“工作任务”,而采用恰

(转)亿级Web系统搭建——单机到分布式集群

当一个Web系统从日访问量10万逐步增长到1000万,甚至超过1亿的过程中,Web系统承受的压力会越来越大,在这个过程中,我们会遇到很多的问题.为了解决这些性能压力带来问题,我们需要在Web系统架构层面搭建多个层次的缓存机制.在不同的压力阶段,我们会遇到不同的问题,通过搭建不同的服务和架构来解决. Web负载均衡  Web负载均衡(Load Balancing),简单地说就是给我们的服务器集群分配“工作任务”,而采用恰当的分配方式,对于保护处于后端的Web服务器来说,非常重要. 负载均衡的策略有

[转]亿级Web系统搭建——单机到分布式集群

当一个Web系统从日访问量10万逐步增长到1000万,甚至超过1亿的过程中,Web系统承受的压力会越来越大,在这个过程中,我 们会遇到很多的问题.为了解决这些性能压力带来问题,我们需要在Web系统架构层面搭建多个层次的缓存机制.在不同的压力阶段,我们会遇到不同的问题,通 过搭建不同的服务和架构来解决. Web负载均衡 Web负载均衡(Load Balancing),简单地说就是给我们的服务器集群分配“工作任务”,而采用恰当的分配方式,对于保护处于后端的Web服务器来说,非常重要. 负载均衡的策略