首页 > 其他分享 >log4j2.xml

log4j2.xml

时间:2024-10-17 14:51:51浏览次数:11  
标签:xml final void event public 日志 log4j2 appender

log4j2.xml

如侵权,请联系,无心侵权~
如有错误,也请指正。

1、log4j2.xml使用

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
  <properties>
    <property name="LOG_HOME">./logs</property>
  </properties>
  <Appenders>
    <!--*********************控制台日志***********************-->
    <Console name="consoleAppender" target="SYSTEM_OUT">
      <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
      <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
      <!--设置日志格式及颜色-->
      <PatternLayout
        pattern="%style{%d{ISO8601}}{bright,green} %highlight{%-5level} [%style{%t}{bright,blue}] %style{%C{}}{bright,yellow}: %msg%n%style{%throwable}{red}"
        disableAnsi="false" noConsoleNoAnsi="false" charset="UTF-8"/>
    </Console>

    <!--*********************文件日志***********************-->
    <!--all级别日志-->
    <RollingFile name="allFileAppender"
      fileName="${LOG_HOME}/all.log"
      <!--日志打印路径-->
      filePattern="${LOG_HOME}/$${date:yyyy-MM}/all-%d{yyyy-MM-dd}-%i.log">
      <!--设置日志格式-->
      <PatternLayout>
        <pattern>%d %p %C{} [%t] %m%n</pattern>
      </PatternLayout>
      <Policies>
        <!-- 设置日志文件切分参数 -->
        <!--<OnStartupTriggeringPolicy/>-->
        <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新-->
        <SizeBasedTriggeringPolicy size="100MB"/>
        <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置-->
        <TimeBasedTriggeringPolicy/>
      </Policies>
      <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i-->
      <DefaultRolloverStrategy max="20"/>
    </RollingFile>

    <!--debug级别日志-->
    <RollingFile name="debugFileAppender"
      fileName="${LOG_HOME}/debug.log"
      filePattern="${LOG_HOME}/$${date:yyyy-MM}/debug-%d{yyyy-MM-dd}-%i.log">
      <Filters>
        <!--过滤掉info及更高级别日志-->
        <ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>
      </Filters>
      <!--设置日志格式-->
      <PatternLayout>
        <pattern>%d %p %C{} [%t] %m%n</pattern>
      </PatternLayout>
      <Policies>
        <!-- 设置日志文件切分参数 -->
        <!--<OnStartupTriggeringPolicy/>-->
        <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新-->
        <SizeBasedTriggeringPolicy size="100MB"/>
        <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置-->
        <TimeBasedTriggeringPolicy/>
      </Policies>
      <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i-->
      <DefaultRolloverStrategy max="20"/>
    </RollingFile>

    <!--info级别日志-->
    <RollingFile name="infoFileAppender"
      fileName="${LOG_HOME}/info.log"
      filePattern="${LOG_HOME}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz">
      <Filters>
        <!--过滤掉warn及更高级别日志-->
        <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
            </Filters>
            <!--设置日志格式-->
            <PatternLayout>
                <pattern>%d %p %C{} [%t] %m%n</pattern>
            </PatternLayout>
            <Policies>
                <!-- 设置日志文件切分参数 -->
                <!--<OnStartupTriggeringPolicy/>-->
                <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新-->
                <SizeBasedTriggeringPolicy size="100MB"/>
                <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置-->
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
            <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i-->
            <!--<DefaultRolloverStrategy max="20"/>-->
        </RollingFile>

        <!--warn级别日志-->
        <RollingFile name="warnFileAppender"
                     fileName="${LOG_HOME}/warn.log"
                     filePattern="${LOG_HOME}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log.gz">
            <Filters>
                <!--过滤掉error及更高级别日志-->
                <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
            </Filters>
            <!--设置日志格式-->
            <PatternLayout>
                <pattern>%d %p %C{} [%t] %m%n</pattern>
            </PatternLayout>
            <Policies>
                <!-- 设置日志文件切分参数 -->
                <!--<OnStartupTriggeringPolicy/>-->
                <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新-->
                <SizeBasedTriggeringPolicy size="100MB"/>
                <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置-->
                <TimeBasedTriggeringPolicy/>
            </Policies>
            <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i-->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>

        <!--error及更高级别日志-->
        <RollingFile name="errorFileAppender"
                     fileName="${LOG_HOME}/error.log"
                     filePattern="${LOG_HOME}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz">
            <!--设置日志格式-->
            <PatternLayout>
                <pattern>%d %p %C{} [%t] %m%n</pattern>
            </PatternLayout>
            <Policies>
                <!-- 设置日志文件切分参数 -->
                <!--<OnStartupTriggeringPolicy/>-->
                <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新-->
                <SizeBasedTriggeringPolicy size="100MB"/>
                <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置-->
                <TimeBasedTriggeringPolicy/>
            </Policies>
            <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i-->
            <DefaultRolloverStrategy max="20"/>
        </RollingFile>

        <!--json格式error级别日志-->
        <RollingFile name="errorJsonAppender"
                     fileName="${LOG_HOME}/error-json.log"
                     filePattern="${LOG_HOME}/error-json-%d{yyyy-MM-dd}-%i.log.gz">
            <JSONLayout compact="true" eventEol="true" locationInfo="true"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="100MB"/>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
        </RollingFile>
          <!--日志远程上报-->
	      <Scribe name="ScribeAppender">
	         <!--表示远程日志,打到远程。原理:日志先保存到kafka中,其他日志中心再消费Kafka消息。-->
	         <!--远程日志默认使用appkey作为日志名(app.properties文件中的app.name字段),也可自定义scribeCategory属性,scribeCategory优先级高于appkey-->
	        <Property name="scribeCategory">123</Property>
	        <LcLayout/>
	      </Scribe>
	  
	        <!--将ScribeAppender和remoteErrorLog两个远程日志变成异步的-->
	      <Async name="ScribeAsyncAppender" blocking="false">
	         <AppenderRef ref="ScribeAppender"/>
	      </Async>
  
    </Appenders>

    <Loggers>
        <logger name="org.springframework" level="info"/>
        <logger name="org.mybatis" level="info"/>
        <logger name="org.apache" level="info"/>
        <logger name="scribeLogger" level="info" additivity="false"> 
         <!-- 如果代码中使用scribeLogger,表示这个日志往ScribeAsyncAppender这个远程日志上打 -->
        <appender-ref ref="ScribeAsyncAppender" />
        </logger>
        <!-- 根日志设置 -->
        <Root level="INFO">
            <AppenderRef ref="allFileAppender" level="all"/>
            <AppenderRef ref="consoleAppender" level="debug"/>
            <AppenderRef ref="debugFileAppender" level="debug"/>
            <AppenderRef ref="infoFileAppender" level="info"/>
            <AppenderRef ref="warnFileAppender" level="warn"/>
            <AppenderRef ref="errorFileAppender" level="error"/>
            <AppenderRef ref="errorJsonAppender" level="error"/>
        </Root>
    </Loggers>

</Configuration>

说明:

  • additivity:非必填,默认true。该Logger是否附加给Root(此参数详细介绍看)
    子Logger 是否继承 父Logger 的 输出源(appender)的标志位。具体说,默认情况下子Logger会继承父Logger的appender,也就是说子Logger会在父Logger的appender里输出。(总之一句话,additivity=true时候,不仅会在该日志器打印一遍,而且还会在root中的所有appender中打印一遍)

以下配置了<Root>并将其打印级别设置为info,因此上面的name为"test"的<Logger>的additivity属性必须设置为false,就不会反馈到 <Root> 中;否则"apiLog"的Appender的info级别上的日志,将分别在<Logger name=“test”>和<Root>中被打印两次。

<Loggers>
    <Logger name="test" level="trace" additivity="false">
        <AppenderRef ref="apiLog"/>
    </Logger>
    <Root level="info">
        <AppenderRef ref="apiLog"/>
        <AppenderRef ref="errLog"/>
    </Root>
</Loggers>
  • 不要往监控平台打印过多的数据,例如美团的Raptor。日志中心是用来记录和展示业务日志的,什么日志都能往里放。
  • 在日志器中会有level,表示这个日志器只会打印大于等于该level的日志,而appender中也可以配置<ThresholdFilter>level=xxx</ThresholdFilter>,表示该appender只会打印大于等于xxx的日志。因此这两个是要同时满足。举例:
    配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="info">
    <appenders>
        <Console name="Console" target="SYSTEM_OUT" follow="true">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %t %-5level %logger{30}.%method - %ex %msg%n" />
            <Filters>
                <ThresholdFilter level="error" onMatch="DENY" onMismatch="ACCEPT"/>
            </Filters>
        </Console>
    <loggers>
        <root level="info">
            <appender-ref ref="Console" />
        </root>
    </loggers>
</configuration>

Java程序:

@Slf4j
public class Log4j2Test {
    @Test
    public void testLevel(){
        log.trace("trace level");
        log.debug("debug level");
        log.info("info level");
        log.warn("warn level");
        log.error("error level");
    }
}

结果:日志器root可以输出info级别及以上的,但是console appender不输出error级别及以上的,所以只输出了info warn级别的日志。因此两者取交集。

2022-12-28 19:47:34.436 main INFO  com.sankuai.mdp.fanjunfu.Log4j2Test.testLevel -  info level
2022-12-28 19:47:34.442 main WARN  com.sankuai.mdp.fanjunfu.Log4j2Test.testLevel -  warn level
  • RollingFile标签是日志打印到本地机器。

2、日志器的流程解析

2.1、几个重要的类

日志的门面类:
接口:org.slf4j的Logger(slfj4j:The Simple Logging Facade for Java)

package org.slf4j;

public interface Logger {
  // 获取日志器的名字
  public String getName();
  // 日志器是否开启了trace级别的日志打印
  public boolean isTraceEnabled();
  // 以trace级别进行打印日志,该方法多种重载
  public void trace(String msg);

  // 下面和trace含义一样,只是日志打印级别不一样
  public boolean isDebugEnabled();
	public void debug(String msg);
  
  public boolean isInfoEnabled();
  public void info(String msg);
  
  public boolean isWarnEnabled();
  public void warn(String msg);
  
  public boolean isErrorEnabled();
  public void error(String msg);
  
}

实现:org.apache.logging.slf4j.Log4jLogger

日志的实现:
org.apache.logging.log4j的Logger

public interface Logger {
  boolean isTraceEnabled();
  void trace(Message message);
  boolean isInfoEnabled();
  void info(Message message);
  boolean isDebugEnabled();
  void debug(Message message);
  boolean isWarnEnabled();
  void warn(Message message);
  boolean isErrorEnabled();
	void error(Message message);
  boolean isFatalEnabled();
	void fatal(Message message);
  // 以指定的级别进行打印
  void log(Level level, Message message);
}

org.apache.logging.log4j.spi的ExtendedLogger

package org.apache.logging.log4j.spi;

import org.apache.logging.log4j.Logger;

public interface ExtendedLogger extends Logger {
  // 日志日否开启
  boolean isEnabled(Level level, Marker marker, Message message, Throwable t);
	// 如果日志开启,打印日志
  void logIfEnabled(String fqcn, Level level, Marker marker, CharSequence message, Throwable t);

}

org.apache.logging.log4j.spi的AbstractLogger是ExtendedLogger的实现类

继承图:
在这里插入图片描述

调用图:
在这里插入图片描述

2.2、整体流程图

在这里插入图片描述
主线程或者工作线程只是调用到各种appender,如AsyncAppender/consoleAppender,会把日志数据存储到BlockingQueue队列中,BlockingQueue队列大小为1024。如果超过1024(日志打印过多导致同步日志器消费赶不上生产速度),这个阻塞队列就放不下,就会放到StatusLogger的BoundedQueue(这个作用为了日志的有序性?)并且类似于StatusConsoleListener的监听器进行打印输出(这里是同步的,大量使用了synchronized)。

在AsyncAppender的BlockingQueue之后仍然使用主线程/工作线程执行后面的逻辑DefaultErrorHandler的逻辑,而这里会有大量的同步代码块synchronized,从而导致主线程/工作线程阻塞。

这些出现大量同步的地方都是可以优化的地方,提供系统的吞吐量。

PrintStream会把信息打印到控制台Console.
在这里插入图片描述
代码简述逻辑:

  • 通过name获得日志器Logger
  • 调用日志器的callAppender()方法
  • callAppender()方法会调用这个日志器下面所有的appender-ref的appender.append()
  • 具体就看这个append()的实现:
    • 同步日志器ScribeLogger: 进行socket.connect()远程连接,然后发送数据
    • 异步步日志器AsyncScribeLogger: 存放到阻塞队列,然后开启一个子线程,将阻塞队列中的日志数据分发给异步日志器所有的appender-ref
    • 本地日志器XMDScribeLogger: 存放到阻塞队列,然后开启一个子线程,将阻塞队列中的日志数据分发给新创建的一个AppendRollingAppender(存储到本地机器)

3、部分源码

3.1、通过简单例子看源码

log4j2.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="info">
    <appenders>
        <Console name="Console" target="SYSTEM_OUT" follow="true">
            <!--<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %t %-5level %logger{30}.%method - %msg%n" />-->
            <PatternLayout pattern="%5p %d{yyyy-MM-dd HH:mm:ss} %c %L Line - %m %n" />
        </Console>

        <!--默认按天&按512M文件大小切分日志,默认最多保留30个日志文件,非阻塞模式-->
        <XMDFile name="infoAppender" fileName="info.log" sizeBasedTriggeringSize="512M"
                 rolloverMax="30">
            <Filters>
                <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </XMDFile>

        <XMDFile name="warnAppender" fileName="warn.log" sizeBasedTriggeringSize="512M"
                 rolloverMax="30">
            <Filters>
                <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
        </XMDFile>

        <XMDFile name="errorAppender" fileName="error.log" sizeBasedTriggeringSize="512M"
                 rolloverMax="30">
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout>
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} - - [%p] %t %c{1.} %XMDT (%F:%L) %msg%n</pattern>
            </PatternLayout>
        </XMDFile>

        <!--日志远程上报-->
        <Scribe name="ScribeAppender">
            <!--远程日志默认使用appkey作为日志名(app.properties文件中的app.name字段),也可自定义scribeCategory属性,scribeCategory优先级高于appkey-->
            <Property name="scribeCategory">wpt_aggroupapi_tracelog</Property>
            <LcLayout/>
        </Scribe>
        <Scribe name="remoteErrorLog">
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <LcLayout/>
        </Scribe>
        <Async name="ScribeAsyncAppender" blocking="false">
            <AppenderRef ref="ScribeAppender"/>
            <AppenderRef ref="remoteErrorLog"/>
        </Async>

        <CatAppender name="catAppender"/>

    </appenders>

    <loggers>
        <!--远程日志,详细使用说明参见 MDP 文档中日志中心部分 https://docs.sankuai.com/dp/hbar/mdp-docs/master/log/#2 -->
        <logger name="scribeLogger" level="info" additivity="false">
            <appender-ref ref="ScribeAsyncAppender" />
        </logger>
        <logger name="org.springframework" level="info" additivity="false">
            <appender-ref ref="Console" />
        </logger>
        <root level="info">
            <appender-ref ref="infoAppender"/>
            <appender-ref ref="warnAppender"/>
            <appender-ref ref="errorAppender"/>
            <appender-ref ref="Console" />
            <appender-ref ref="catAppender"/>
        </root>
    </loggers>
</configuration>

代码:

public class Test {
  private final static Logger log =  LoggerFactory.getLogger("scribeLogger");
  @Test
  public void test(){
    System.out.println("start~");
    log.error("hello {} ! ", "word");
    System.out.println("end!");
  }
}

源码:
LoggerConfig:

public class LoggerConfig extends AbstractFilterable implements LocationAware {
  
  private void processLogEvent(final LogEvent event, final LoggerConfigPredicate predicate) {
    event.setIncludeLocation(isIncludeLocation());
    if (predicate.allow(this)) {
      callAppenders(event);   // 调用所有的appender
    }
    logParent(event, predicate);
  }
  
  protected void callAppenders(final LogEvent event) {
    final AppenderControl[] controls = appenders.get();
    //noinspection ForLoopReplaceableByForEach
    for (int i = 0; i < controls.length; i++) {
      controls[i].callAppender(event);  // 调用这个日志器下面的appender-ref(appender)
    }
  }
}

AppendControl

public class AppenderControl extends AbstractFilterable {
  
  private void tryCallAppender(final LogEvent event) {
    try {
      appender.append(event);   // 无论是AsynAppender还是XMDFileAppender,该方法都是存储到他们内部的阻塞队列中BlockingQueue
    } catch (final RuntimeException error) {
      handleAppenderError(event, error);
    } catch (final Throwable throwable) {
      handleAppenderError(event, new AppenderLoggingException(throwable));
    }
  }
}

AsynAppender

public final class AsyncAppender extends AbstractAppender {
  
  // 在启动的时候,会调用该方法
  public void start() {
    final Map<String, Appender> map = config.getAppenders();
    final List<AppenderControl> appenders = new ArrayList<>();
    for (final AppenderRef appenderRef : appenderRefs) {
      final Appender appender = map.get(appenderRef.getRef());
      if (appender != null) {
        appenders.add(new AppenderControl(appender, appenderRef.getLevel(), appenderRef.getFilter()));
      } else {
        LOGGER.error("No appender named {} was configured", appenderRef);
      }
    }
    if (errorRef != null) {
      final Appender appender = map.get(errorRef);
      if (appender != null) {
        errorAppender = new AppenderControl(appender, null, null);
      } else {
        LOGGER.error("Unable to set up error Appender. No appender named {} was configured", errorRef);
      }
    }
    if (appenders.size() > 0) {
      dispatcher = new AsyncAppenderEventDispatcher(  // 这个类是一个线程继承Thread,这个类会从AsyncAppender的阻塞队列中获取数据,分发dispatch给appenders(这个是这个异步日志器下的appender(一般为同步日志器,同步日志器会通过socket.connect与远程日志器连接、发送))
        getName(), errorAppender, appenders, queue);  // 异步日志器的所有同步日志器会消费这个阻塞队列queue(该线程进行分配)
    } else if (errorRef == null) {
      throw new ConfigurationException("No appenders are available for AsyncAppender " + getName());
    }
    asyncQueueFullPolicy = AsyncQueueFullPolicyFactory.create();

    dispatcher.start();  // 启动该线程
    super.start();
  }
}

AsyncAppenderEventDispatcher

class AsyncAppenderEventDispatcher extends Log4jThread {
  @Override
  public void run() {
    LOGGER.trace("{} has started.", getName());
    dispatchAll();
    dispatchRemaining();
  }
  
  private void dispatchAll() {
    while (!stoppedRef.get()) {
      LogEvent event;
      try {
        event = queue.take();  // 
      } catch (final InterruptedException ignored) {
        // Restore the interrupted flag cleared when the exception is caught.
        interrupt();
        break;
      }
      if (event == STOP_EVENT) {
        break;
      }
      event.setEndOfBatch(queue.isEmpty());
      dispatch(event);   // 分发给这个日志的appender进行执行
    }
    LOGGER.trace("{} has stopped.", getName());
  }
  
  void dispatch(final LogEvent event) {

    // Dispatch the event to all registered appenders.
    boolean succeeded = false;
    // noinspection ForLoopReplaceableByForEach (avoid iterator instantion)
    for (int appenderIndex = 0; appenderIndex < appenders.size(); appenderIndex++) {
      final AppenderControl control = appenders.get(appenderIndex);
      try {
        control.callAppender(event);
        succeeded = true;
      } catch (final Throwable error) {
        // If no appender is successful, the error appender will get it.
        // It is okay to simply log it here.
        LOGGER.trace(
          "{} has failed to call appender {}",
          getName(), control.getAppenderName(), error);
      }
    }

    // Fallback to the error appender if none has succeeded so far.
    if (!succeeded && errorAppender != null) {
      try {
        errorAppender.callAppender(event);
      } catch (final Throwable error) {
        // If the error appender also fails, there is nothing further
        // we can do about it.
        LOGGER.trace(
          "{} has failed to call the error appender {}",
          getName(), errorAppender.getAppenderName(), error);
      }
    }

  }
}

3.2、log4j2.xml配置指导

  1. 建议日志配置文件中对所有Appender的PatternLayout都增加%ex配置,因为如果没有显式配置%ex,则异常格式化输出的默认配置是%xEx,此时会打印异常的扩展信息(jar名称和版本),可能导致业务线程Block。
  2. 不建议日志配置文件中使用AsyncAppender,建议自定义Appender实现,因为AsyncAppender是日志框架默认提供的,目前最新版本中仍然存在日志事件入队前就加载异常堆栈类的问题,可能导致业务线程Block。
  3. 不建议生产环境使用ConsoleAppender,因为输出日志到Console时有synchronized同步操作,高并发场景下非常容易导致业务线程Block。
  4. 不建议在配置文件中使用<AsyncLogger>标签,因为日志事件元素在入队前就会触发加载异常堆栈类,可能导致业务线程Block。如果希望使用Log4j2提供的异步日志AsyncLogger,建议配置Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector参数,开启异步日志。

标签:xml,final,void,event,public,日志,log4j2,appender
From: https://blog.csdn.net/m0_50149847/article/details/142963744

相关文章

  • No.17 笔记 | XXE漏洞:XML外部实体注入攻击
    1.XXE漏洞概览XXE(XMLExternalEntity)是一种允许攻击者干扰应用程序对XML输入处理的漏洞。1.1XXE漏洞比喻想象XML解析器是一个听话的机器人,而XXE就是利用这个机器人的"过分听话"来获取不应该获取的信息。1.2XXE漏洞危害危害类型描述文件读取读取服务器上的任意文件命......
  • pom.xml文件中各个标签的含义
    原文链接:pom文件中各个标签的含义–每天进步一点点(longkui.site)搭建springboot中有一个默认的pom文件,看到它里面的标签,有些不太熟悉,于是学习了下把找到的结果都记录下来。1.parent<parent><!--这是SpringBoot的父级依赖,这样当前的项目就是SpringBoot项目了......
  • CSV、XML、JSON三种形式进行存储并读取
    下面是一个完整的Python示例代码,它可以生成简单的算式(加法、减法、乘法、除法),并将生成的算式和习题长期保存到CSV、XML和JSON三种格式中。代码包括生成算式的功能,以及将数据保存和读取的功能。1.代码实现pythonimportcsvimportjsonimportxml.etree.ElementTreeas......
  • xml转YOLO的txt格式(一次必成版!)
    废话少说,直接上干货!首先,我先介绍一下代码的使用,编程语言为“Python”,共有三处需按照自己的需求修改的,我都放在代码最下面了。xml_root_path:输入你的xml格式的文件存放位置,建议全部用绝对路径txt_save_path:输入你的txt格式的文件导出后的存放位置classes_path:输入你的label......
  • golang从http请求中读取xml格式的body,并转成json
    推荐学习文档golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔记专栏文章目录以下是在Go语言中从HTT......
  • 软件构造,生成算式采用CSV、XML、JSON三种形式进行存储并读取。
    编写代码完成将生成的算式及习题长期保存下来,采用CSV、XML、JSON三种形式进行存储并读取。提交相关代码及运行截图。importrandomimportcsvimportjsonimportxml.etree.ElementTreeasETfromxml.domimportminidom#生成随机算式数据defgenerate_exercises(count......
  • 【Flink 系列二十三】hudi 消失的 HIVE_CONF_DIR,HIVE 读不到 hive-site.xml 读不到
    问题现象Unabletofindconfigfilehive-site.xmlUnabletofindconfigfilehivemetastore-site.xmlUnabletofindconfigfilemetastore-site.xml本文记录这个问题是如何导致的,并记录如何向Hive、Hudi提供hive-site.xml以便正确加载。问题分析:HiveMetaStore是......
  • 读取xml文件写到csv文件中
    #xml数据提取,拼合为csv文件importosimportcsvimportxml.dom.minidomimportglobdefto_int(str_value):try:returnint(str_value)exceptValueError:try:float_value=float(str_value)returnint(floa......
  • spring上 -基于Xml配置bean笔记
    4,Spring内容   7,快速入门 需求:通过Spring的方式[配置文件],获取JavaBean:Monster的对象,并给该的对象属性赋值,输出该对象信息.代码结构:lib目录是自己创建的,然后再引入5个jar包 源码:beans.xml<?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="......
  • C# 类型增加自定义xml序列化
    1、首先类需要增加[Serializable]标识2、类实现IXmlSerializable接口下面是重写ReadXml和WriteXml方法publicvoidReadXml(XmlReaderreader){reader.ReadStartElement(reader.LocalName);while(reader.Read()){if(reader.Name=="TimeType"&&......