• 方法级别的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输出。

    本文地址:http://www.cnblogs.com/danseqianbi/p/4688869.html

  • 相关阅读:
    输入一个字符串,输出该字符串中字符的所有组合 C#【总结】
    Asp.net的身份验证【转载】
    C#面试基础问题【转载】
    Testing and Checking Refined
    Exploratory Testing 3.0 探索式测试
    博客搬家
    Seven Kinds of Testers - 七种类型的测试
    使用Spring的Property文件存储测试数据 - 初始化
    Protractor 怎样运行
    Protractor-引入Cucumber
  • 原文地址:https://www.cnblogs.com/danseqianbi/p/4688869.html
Copyright © 2020-2023  润新知