log(三)——MDCAdapter之实现LogbackMDCAdapter

来源:互联网 发布:淘宝卖家需要哪些软件 编辑:程序博客网 时间:2024/05/21 22:24

1.put

/**   * Put a context value (the <code>val</code> parameter) as identified with the   * <code>key</code> parameter into the current thread's context map. Note that   * contrary to log4j, the <code>val</code> parameter can be null.   * <p/>   * <p/>   * If the current thread does not have a context map it is created as a side   * effect of this call.   *   * @throws IllegalArgumentException in case the "key" parameter is null   */  public void put(String key, String val) throws IllegalArgumentException {    if (key == null) {      throw new IllegalArgumentException("key cannot be null");    }    Map<String, String> oldMap = copyOnInheritThreadLocal.get();    Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);    if (wasLastOpReadOrNull(lastOp) || oldMap == null) {      Map<String, String> newMap = duplicateAndInsertNewMap(oldMap);      newMap.put(key, val);    } else {      oldMap.put(key, val);    }  }
这个put的逻辑很简单,首先把通过getAndSetLastOperation把操作改成WRITE_OPERATION,同时判断上一次的操作,如果是null,又或者copyOnInheritThreadLocal中的内容是null——可以理解为还没有初始化,就初始化一个map。如果已有内容,直接向里面设置内容。

这里要说的是上次的操作如果是READ_OPERATION,当前操作是put或者remove,即紧跟着读操作的一次写操作,也需要通过duplicateAndInsertNewMap来拷贝一个map的副本。

2.duplicateAndInsertNewMap

如果上次的操作如果是READ_OPERATION,当前操作是put或者remove,即紧跟着读操作的一次写操作,也需要通过duplicateAndInsertNewMap来拷贝一个map的副本。

先来一段注释

// We wish to avoid unnecessarily copying of the map. To ensure  // efficient/timely copying, we have a variable keeping track of the last  // operation. A copy is necessary on 'put' or 'remove' but only if the last  // operation was a 'get'. Get operations never necessitate a copy nor  // successive 'put/remove' operations, only a get followed by a 'put/remove'  // requires copying the map.  // See http://jira.qos.ch/browse/LBCLASSIC-254 for the original discussion.
这里说了,为了避免不必要的map的copy操作,只有在get操作进行之后做put/remove操作的时候,去对map进行copy。jira上的issue已经过期无法访问了,通过百度快照看到了:

Michael Franz added a comment - 07/Mar/11 12:14 PM Hello Ceki, I've looked over the whole application code and actually there is only one very rarely called get-operation on the MDC. So your approach would also work in my scenario. But as a software developer I would feel a bit uncomfortable with your strategy: "copy on write after read" sounds quite uncommon against "copy on write" and "lazy copy". With your approach you are forcing developers to keep an eye on the order of put- and get-operations, especially if an application performs existence checks before writing on the MDC. As long as only puts are performed (like in my case) your approach is sufficient. I don't know if there are real world applications that often call gets. Nevertheless this is just a feeling.

I've looked over the whole application code and actually there is only one very rarely called get-operation on the MDC. So your approach would also work in my scenario.But as a software developer I would feel a bit uncomfortable with your strategy: "copy on write after read" sounds quite uncommon against "copy on write" and "lazy copy". With your approach you are forcing developers to keep an eye on the order of put- and get-operations, especially if an application performs existence checks before writing on the MDC. As long as only puts are performed (like in my case) your approach is sufficient. I don't know if there are real world applications that often call gets. Nevertheless this is just a feeling.

这里觉得copy on write依然浪费性能,所以只在get操作后做copy on write。

这里本人理解是,getPropertyMap操作会得到copyOnInheritThreadLocal的内容,别人有可能获取这个内容的引用然后修改这个内容,所以再写的时候copy一份,但是这期间利用这个拷贝打出来的日志信息不就错了么?!

所以,为什么在get操作后做copy on write???

copy on write容器是来解决并发读的问题的,但是这个也没有并发读的异常,因为是ThreadLocal操作,所以对于这个map只有当前线程才能访问,所以自然线程安全啊。

private Map<String, String> duplicateAndInsertNewMap(Map<String, String> oldMap) {    Map<String, String> newMap = Collections.synchronizedMap(new HashMap<String, String>());    if (oldMap != null) {        // we don't want the parent thread modifying oldMap while we are        // iterating over it        synchronized (oldMap) {          newMap.putAll(oldMap);        }    }    copyOnInheritThreadLocal.set(newMap);    return newMap;  }
然后这个方法中的同步块也很奇怪,为什么父线程会能修改这个map,其他线程能修改这个map的地方只有通过getPropertyMap方法来获得copyOnInheritThreadLocal内容的引用,然后修改这个map的内容,只有这一处,但是一般使用MDC应该不会用getPropertyMap方法获取内容然后进行操作吧,不知道为什么把这个方法公有化,而不是包一个ImmutableMap再对外暴露。

3.getPropertyMap

/**   * Get the current thread's MDC as a map. This method is intended to be used   * internally.   */  public Map<String, String> getPropertyMap() {    lastOperation.set(READ_OPERATION);    return copyOnInheritThreadLocal.get();  }

返回当前线程的context map的直接引用!是直接引用,不是拷贝副本!

注意这个方法的注释,上面说了intended to be used internally,也就是说是为了内部使用的。这个方法很危险,会对外直接暴露copyOnInheritThreadLocal的内容,然后通过获得的map引用修改context内容。

4.getCopyOfContextMap

/**   * Return a copy of the current thread's context map. Returned value may be   * null.   */  public Map getCopyOfContextMap() {    lastOperation.set(READ_OPERATION);    Map<String, String> hashMap = copyOnInheritThreadLocal.get();    if (hashMap == null) {      return null;    } else {      return new HashMap<String, String>(hashMap);    }  }
返回当前线程的context map的一个副本,对这个map的修改不会影响原来copyOnInheritThreadLocal中的内容。

0 0
原创粉丝点击