MDC And NDC

序言

  • log4j是apache实现的一个开源日志组件(Wrapped implementations)
  • logback是slf4j的原生实现(Native implementations)

slf4j是Java简单日志的门面(The Simple Logging Facade for Java),如果使用slf4j日志门面,必须要用到slf4j-api.

logback是直接实现的,所以不需要其他额外的转换以及转换带来的消耗,而slf4j要调用log4j的实现,就需要一个适配层,将log4j的实现适配到slf4j-api可调用的模式
 

MDC与NDC

他们主要思路就是在执行线程中放置上下文信息,这个上线文信息一般具有唯一性 用来追踪 跨线程,跨服务的业务流程.然后我们根据这个唯一变量来搜索日志,以展现该业务的完整流程.

MDC与NDC的线程独立的,子线程会从父线程拷贝上下文(具体时间跟ThreadLocal是一样的cuiyaonan2000@163.com)

MDC

内部是Map的key ,value的方式实现

  1. 保存信息到上下文 MDC.put(key, value);
  2. 从上下文获取设置的信息 MDC.get(key);
  3. 清楚上下文中指定的key的信息 MDC.remove(key);
  4. 清除所有 clear()
  5. 输出模板,注意是大写[%X{key}]: log4j.appender.consoleAppender.layout.ConversionPattern = %-4r [%t] %5p %c %x - %m - %X{key}%n
  6. %X :后面不带{key} 则是输出全部

NDC

内部数据结构师栈,实现方式还是MDC

  1. 开始调用 NDC.push(message);
  2. 删除栈顶消息 NDC.pop();
  3. 清除全部的消息,必须在线程退出前显示的调用,否则会导致内存溢出。 NDC.remove();
  4. 输出模板,注意是小写的[%x] log4j.appender.stdout.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ssS}] [%x] : %m%n
     

/****************************************************************************** * * package: Log4Qt * file: loggingevent.h * created: September 2007 * author: Martin Heinrich * * * Copyright 2007 Martin Heinrich * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #ifndef LOG4QT_LOG4QTEVENT_H #define LOG4QT_LOG4QTEVENT_H #include "level.h" #include <QHash> #include <QStringList> #include <QEvent> namespace Log4Qt { class Logger; class MessageContext { public: explicit MessageContext() : file(nullptr), line(-1), function(nullptr) {} explicit MessageContext(const char *fileName, int lineNumber, const char *functionName) : file(fileName), line(lineNumber), function(functionName) {} const char *file; int line; const char *function; }; /*! * \brief The class LoggingEvent is the internal representation of a * logging event. * * The class uses milliseconds since 1970-01-01T00:00:00, Coordinated * Universal Time for time values. For converstion from and to QDateTime * use DateTime. */ class LOG4QT_EXPORT LoggingEvent : public QEvent { public: static const QEvent::Type eventId; LoggingEvent(); LoggingEvent(const Logger *pLogger, Level level, const QString &rMessage); LoggingEvent(const Logger *pLogger, Level level, const QString &rMessage, const MessageContext &context, const QString &categoryName); LoggingEvent(const Logger *pLogger, Level level, const QString &rMessage, qint64 timeStamp); LoggingEvent(const Logger *pLogger, Level level, const QString &rMessage, const QString &rNdc, const QHash<QString, QString> &rProperties, const QString &rThreadName, qint64 timeStamp); LoggingEvent(const Logger *pLogger, Level level, const QString &rMessage, const QString &rNdc, const QHash<QString, QString> &rProperties, qint64 timeStamp, const MessageContext &context, const QString &categoryName); LoggingEvent(const Logger *pLogger, Level level, const QString &rMessage, const QString &rNdc, const QHash<QString, QString> &rProperties, const QString &rThreadName, qint64 timeStamp, const MessageContext &context, const QString &categoryName); virtual ~LoggingEvent(); Level level() const; // LocationInformation locationInformation() const; const Logger *logger() const; QString message() const; QHash<QString, QString> mdc() const; QString ndc() const; QHash<QString, QString> properties() const; qint64 sequenceNumber() const; QString threadName() const; qint64 timeStamp() const; QString loggerName() const; QString property(const QString &rKey) const; QStringList propertyKeys() const; void setProperty(const QString &rKey, const QString &rValue); QString toString() const; static qint64 sequenceCount(); static qint64 startTime(); int lineNumber() const; void setLineNumber(int lineNumber); QString fileName() const; void setFileName(const QString &fileName); QString functionName() const; void setMethodName(const QString &functionName); QString categoryName() const; void setCategoryName(const QString &categoryName); MessageContext context() const; void setContext(const MessageContext &context); private: void setThreadNameToCurrent(); static qint64 nextSequenceNumber(); private: Level mLevel; const Logger *mpLogger; QString mMessage; QString mNdc; QHash<QString, QString> mProperties; qint64 mSequenceNumber; QString mThreadName; qint64 mTimeStamp; MessageContext mContext; QString mCategoryName; static qint64 msSequenceCount; #ifndef QT_NO_DATASTREAM // Needs to be friend to stream objects friend LOG4QT_EXPORT QDataStream &operator<<(QDataStream &rStream, const LoggingEvent &rLoggingEvent); friend LOG4QT_EXPORT QDataStream &operator>>(QDataStream &rStream, LoggingEvent &rLoggingEvent); #endif // QT_NO_DATASTREAM }; #ifndef QT_NO_DATASTREAM /*! * \relates LoggingEvent * * Writes the given error \a rLoggingEvent to the given stream \a rStream, * and returns a reference to the stream. */ LOG4QT_EXPORT QDataStream &operator<<(QDataStream &rStream, const LoggingEvent &rLoggingEvent); /*! * \relates LoggingEvent * * Reads an error from the given stream \a rStream into the given * error \a rLoggingEvent, and returns a reference to the stream. */ LOG4QT_EXPORT QDataStream &operator>>(QDataStream &rStream, LoggingEvent &rLoggingEvent); #endif // QT_NO_DATASTREAM inline Level LoggingEvent::level() const { return mLevel; } inline const Logger *LoggingEvent::logger() const { return mpLogger; } inline QString LoggingEvent::message() const { return mMessage; } inline QHash<QString, QString> LoggingEvent::mdc() const { return mProperties; } inline QString LoggingEvent::ndc() const { return mNdc; } inline QHash<QString, QString> LoggingEvent::properties() const { return mProperties; } inline qint64 LoggingEvent::sequenceNumber() const { return mSequenceNumber; } inline QString LoggingEvent::threadName() const { return mThreadName; } inline qint64 LoggingEvent::timeStamp() const { return mTimeStamp; } inline QString LoggingEvent::property(const QString &rKey) const { return mProperties.value(rKey); } inline QStringList LoggingEvent::propertyKeys() const { return QStringList(mProperties.keys()); } inline void LoggingEvent::setProperty(const QString &rKey, const QString &rValue) { mProperties.insert(rKey, rValue); } } // namespace Log4Qt Q_DECLARE_METATYPE(Log4Qt::LoggingEvent) Q_DECLARE_TYPEINFO(Log4Qt::LoggingEvent, Q_MOVABLE_TYPE); #endif // LOG4QT_LOG4QTEVENT_H
08-16
MDC(Mapped Diagnostic Context)是一种日志诊断上下文技术,用于在日志中添加额外的上下文信息,方便在复杂的系统中进行日志追踪问题排查。 ### MDC的基本用法 可以通过`MDC.put(key, value)`方法将键值对信息放入MDC中,之后在日志输出时可以引用这些键对应的值。例如,追踪码可以是线程ID,也可以是自己生成的UUID,代码示例如下: ```java MDC.put("request_id", String.valueOf(Thread.currentThread().getId())); return UUID.randomUUID().toString().replaceAll("-","").toUpperCase().substring(0,10); ``` 通过这种方式将请求ID存入MDC中,以便在日志中区分不同的请求[^4]。 ### 在日志配置文件中使用MDC 在日志配置文件(如logback的配置文件)中,可以通过特定的占位符引用MDC中的值,从而将上下文信息包含在日志输出中。这样可以在日志中清晰地看到每个日志条目对应的上下文信息,方便后续的分析排查问题。 ### 在多线程环境中使用MDC 在多线程环境下,需要注意MDC的上下文信息不会自动在不同线程之间传递。为了确保在子线程中也能使用父线程的MDC信息,可能需要手动进行传递。例如,在创建子线程时,将父线程的MDC信息复制到子线程中。 ### MDC的清理 使用完MDC后,需要及时清理其中的信息,避免内存泄漏上下文信息混乱。可以通过`MDC.clear()`方法清理MDC中的所有信息,确保后续操作不会受到之前上下文信息的影响。 ### MDC的应用场景 - **日志跟踪**:在Spring Boot线程池场景下,借助MDC实现日志跟踪。可以创建请求追踪码,记录用户访问流水、登陆等操作信息。通过在MDC中存储请求ID等信息,在日志中可以清晰地看到每个请求的处理流程相关信息,方便排查问题。例如,在处理用户请求时,为每个请求生成一个唯一的请求ID,并将其存入MDC中,在后续的日志记录中都可以包含该请求ID,这样就可以将同一个请求的所有日志关联起来[^4]。 - **分布式系统排查**:在分布式系统中,一个请求可能会经过多个服务节点。通过在MDC中传递请求ID等关键信息,可以将不同服务节点上的日志关联起来,方便快速定位问题所在。例如,当用户请求从前端服务经过多个中间服务最终到达后端服务时,每个服务都可以从MDC中获取请求ID,并在日志中记录,这样在出现问题时,可以根据请求ID快速查找整个请求链路上的所有日志信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cuiyaonan2000

给包烟抽吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值