【Logback】<logger>、<root>标签详解

article/2025/9/17 3:49:10

文章目录

  • 背景
  • 一、\<logger>使用
    • 1.1、使用示例
    • 1.1、属性配置说明 & 演示
      • 1.1.1、name
      • 1.1.2、level
      • 1.1.3、additivity
        • 1.1.3.1、效果演示:additivity=true
        • 1.1.3.1、效果演示:additivity="false"
    • 1.2 appender-ref
  • 二、\<root>使用
    • 2.1、属性
  • 三、解析
    • 3.1、\<logger>链表
      • 3.2、root是一个名为 ROOT 的特殊logger,其 parent 为 null
      • 3.3、 name属性和\<logger>继承关系
    • 3.3、level属性继承 和 优先级
    • 3.4、additivity属性
      • 3.4.1、源码分析
      • 3.4.2、演示
  • 四、日志规范
  • 五、参考资料

背景

排查一个项目的问题,发现打印了一堆重复日志…,为避免日志爆盘被投诉,写一下logback的使用和解析。 因为重复日志问题引发的,所以本文优先<logger>、<root>标签,其余的后续在写…

版本:logback-1.2.3

一、<logger>使用

如果需要定制指定模块或者类的日志信息,可以通过<logger>标签来实现,通过其name属性指定包或者类全路径即可

<logger>是有父子关系的
<root>是一个特殊的<logger> ,其name="ROOT",是所有<logger>的祖先节点,先了解,下面会讲。
父子关系是根据name属性的层级来确定的

<logger name="com.qbhj.logback.controller">,其父子关系如下(先了解结论,下面分析中会讲):

<logger name="ROOT">
└─<logger name="com">
│ └─<logger name="com.qbhj">
│ │ └─<logger name="com.qbhj.logback">
│ │ │ └─<logger name="com.qbhj.logback.controller">

1.1、使用示例

<logger name="com.qbhj" level="INFO" additivity="true"><!-- 可配置 N 个appender-ref --><appender-ref ref="CONSOLE"/><appender-ref ref="appender1"/><appender-ref ref="appender1"/>
</logger><logger name="com.qbhj.logback" additivity="true"><appender-ref ref="CONSOLE"/>
</logger><logger name="com.qbhj.logback.empty" additivity="true">
</logger>

Tips:

1、<logger>可以配置 0-N
2、<logger>属性子元素<appender-ref>2个部分构成,日志输出是<appender-ref>所引用的<appender>来执行的
3、<logger>本身是一个链表,有一个父节点和N个子节点,具体在1.3、解析中详细说明

1.1、属性配置说明 & 演示

属性名属性值是否必填说明
name包或者类路径如: com.qbhj 或 com.qbhj.Test.class
levelOFF、ERROR、WARN、INFO、DEBUG、TRACE、ALL日志级别 ,默认继承父<logger>的日志级别
additivitytrue | false是否叠加,默认true ,建议配置为false,见日志规范

1.1.1、name

指定包或者类全路径,则该<logger>的配置就对其生效,默认使用<root>的日志配置

1.1.2、level

日志级别
Tips:

<logger>日志级别level属性是有继承关系,其优先级如下(此表摘自logback官网):

Logger nameAssigned LevelEffective Level
rootDEBUGDEBUG
chapters.configurationINFOINFO
chapters.configuration.MyApp3nullINFO
chapters.configuration.FooDEBUGDEBUG

总结:
若自身没设置 level 则使用其父Logger的level
若父级都没设置,则使用<root>节点的,<root>不配置则其默认级别level为DEBUG

1.1.3、additivity

是否允许父级<logger>打印自身name指定范围内的日志。若允许,则会重复打印日志。

1.1.3.1、效果演示:additivity=true

1)、打印日志代码

package com.qbhj.logback.controller;
// import .....@Slf4j
@RestController
public class LogController {@GetMapping("/additivity")public void additivity(@RequestParam(defaultValue = "1") Integer num) {System.out.println("###################################### num: " + num);for (int i = 0; i < num; i++) {log.info("/additivity......");}}
}

2.1)、日志配置,只配置root,无<logger>

    <root level="INFO"><!-- 控制台 --><appender-ref ref="CONSOLE"/></root>

2.2)、结果,日志只打印了一遍

###################################### num: 1
20:36:06.170  INFO --- [     http-nio-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......

2.3)、说明

所有Logger都没有配置appender,所以无日志输出
root配置了appender=CONSOLE,所以控制台会打印日志

Loggerappenderadditivity(默认true打印日志是否被调用
<logger name="ROOT">CONSOLEtrue
└─<logger name="com">true
│ └─<logger name="com.qbhj">true
│ │ └─<logger name="com.qbhj.logback">true
│ │ │ └─<logger name="com.qbhj.logback.controller">true

3.1)、增加<logger>配置name=com.qbhj.logback.controller<root><logger>中都有配置控制台(CONSOLE)输出,共配置了2遍

    <logger name="com.qbhj.logback.controller" level="INFO" additivity="true"><!-- 控制台 --><appender-ref ref="CONSOLE"/></logger><root level="INFO"><!-- 控制台 --><appender-ref ref="CONSOLE"/></root>

3.2)、结果,日志打了2遍,是<logger><root>各自打一遍

###################################### num: 1
20:54:51.165  INFO --- [     http-nio-8080-exec-2] c.qbhj.logback.controller.LogController  : /additivity......
20:54:51.165  INFO --- [     http-nio-8080-exec-2] c.qbhj.logback.controller.LogController  : /additivity......

3.3)、说明

<logger name="com.qbhj.logback.controller">配置了appender=CONSOLE ,控制台打印日志
root配置了appender=CONSOLE,所以控制台会打印日志

Loggerappenderadditivity(默认true打印日志是否被调用
<logger name="ROOT">CONSOLEtrue
└─<logger name="com">true
│ └─<logger name="com.qbhj">true
│ │ └─<logger name="com.qbhj.logback">true
│ │ │ └─<logger name="com.qbhj.logback.controller">CONSOLEtrue

4.1)、再增加<logger>配置name=com.qbhj.logback logback包肯定包含 LogController类的,即<root><logger>中都有配置控制台(CONSOLE)输出,共配置了3遍

    <logger name="com.qbhj.logback.controller" level="INFO"><!-- 控制台 --><appender-ref ref="CONSOLE"/></logger><logger name="com.qbhj.logback" level="INFO"><!-- 控制台 --><appender-ref ref="CONSOLE"/></logger><root level="INFO"><!-- 控制台 --><appender-ref ref="CONSOLE"/></root>

4.2)、结果,日志打了3遍,2个<logger><root>各自打一遍

###################################### num: 1
20:58:23.914  INFO --- [     http-nio-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......
20:58:23.914  INFO --- [     http-nio-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......
20:58:23.914  INFO --- [     http-nio-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......

4.3)、说明

2个 <logger >配置了appender=CONSOLE ,控制台打印日志2遍
root配置了appender=CONSOLE,控制台会打印日志

Loggerappenderadditivity(默认true打印日志是否被调用
<logger name="ROOT">CONSOLEtrue
└─<logger name="com">true
│ └─<logger name="com.qbhj">true
│ │ └─<logger name="com.qbhj.logback">CONSOLEtrue
│ │ │ └─<logger name="com.qbhj.logback.controller">CONSOLEtrue

1.1.3.1、效果演示:additivity=“false”

1.1)、同样的配置,<logger>全部改为additivity=“false”

    <logger name="com.qbhj.logback.controller" level="INFO" additivity="false"><!-- 控制台 --><appender-ref ref="CONSOLE"/></logger><logger name="com.qbhj.logback" level="INFO" additivity="false"><!-- 控制台 --><appender-ref ref="CONSOLE"/></logger><root level="INFO"><!-- 控制台 --><appender-ref ref="CONSOLE"/></root>

1.2)、结果:日志只打印了一遍

###################################### num: 1
21:00:24.598  INFO --- [     http-nio-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......

1.3)、说明

<logger name="com.qbhj.logback.controller" level="INFO" additivity="false"> ,配置了appender=CONSOLE所以打印日志。其additivity=“false”,不调用父级,结束

Loggerappenderadditivity(默认true打印日志是否被调用
<logger name="ROOT">CONSOLEtrue
└─<logger name="com">true
│ └─<logger name="com.qbhj">true
│ │ └─<logger name="com.qbhj.logback">CONSOLEfalse
│ │ │ └─<logger name="com.qbhj.logback.controller">CONSOLEfalse

1.2 appender-ref

示例

<!-- 建议配置为false,避免日志重复输出 -->
<logger name="com.qbhj" level="INFO" additivity="false"><!-- 可配置 N 个appender-ref --><appender-ref ref="CONSOLE"/><appender-ref ref="appender1"/><appender-ref ref="appender1"/>
</logger><logger name="com.qbhj.logback" additivity="false"><appender-ref ref="CONSOLE"/>
</logger><logger name="com.qbhj.logback.empty" additivity="false">
</logger>

1、每个logger可以配置任意(含0)个<appender-ref>
2、语法:ref=appender[name],声明 <appender>name属性值

二、<root>使用

2.1、属性

属性名属性值是否必填说明
levelOFF、ERROR、WARN、INFO、DEBUG、TRACE、ALL日志级别,默认DEBUG

<root>的本质是一个名为ROOT的特殊logger,即<logger name="ROOT">
root是所有logger的根节点

<root level="DEBUG"><!-- 控制台 --><appender-ref ref="CONSOLE"/><!-- 文件 --><appender-ref ref="FILE_ALL"/><appender-ref ref="FILE_WARN"/><appender-ref ref="FILE_ERROR"/></root>

三、解析

3.1、<logger>链表

1、<logger>是一个链表,有父节点和子节点
2、除<root>父节点为null外,所有logger一定有父节点,<root>为所有logger的根节点

ch.qos.logback.classic.Logger

package ch.qos.logback.classic;
//..........public final class Logger implements org.slf4j.Logger, LocationAwareLogger, AppenderAttachable<ILoggingEvent>, Serializable {// ........../*** The name of this logger*/private String name;// The assigned levelInt of this logger. Can be null.transient private Level level;// The effective levelInt is the assigned levelInt and if null, a levelInt is// inherited form a parent.transient private int effectiveLevelInt;// root 为所有logger的祖先(根)节点/*** The parent of this category. All categories have at least one ancestor* which is the root category.*/transient private Logger parent;/*** The children of this logger. A logger may have zero or more children.*/transient private List<Logger> childrenList;// additive 默认 truetransient private boolean additive = true;final transient LoggerContext loggerContext;// ..........private boolean isRootLogger() {// only the root logger has a null parent
// 只有root的parent为nullreturn parent == null;}// .......... 
}

3.2、root是一个名为 ROOT 的特殊logger,其 parent 为 null

LoggerContext 初始化的时候就在其构造函数中创建了root了
root默认日志级别为 DEBUG

ch.qos.logback.classic.LoggerContext

public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle {/** Default setting of packaging data in stack traces */public static final boolean DEFAULT_PACKAGING_DATA = false;final Logger root;private int size;private int noAppenderWarning = 0;final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();private Map<String, Logger> loggerCache;// .............public LoggerContext() {super();this.loggerCache = new ConcurrentHashMap<String, Logger>();this.loggerContextRemoteView = new LoggerContextVO(this);// 初始化 <root>/**package org.slf4j;public interface Logger {String ROOT_LOGGER_NAME = "ROOT";*/this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
// ROOT的默认日志级别 DEBUGthis.root.setLevel(Level.DEBUG);loggerCache.put(Logger.ROOT_LOGGER_NAME, root);initEvaluatorMap();size = 1;this.frameworkPackages = new ArrayList<String>();}// .......... }

3.3、 name属性和<logger>继承关系

1、会根据name属性的值,逐级创建子logger,如com.qbhj.Test.class中 log.info(“print log…”),一共会创建3个logger

1)、com
2)、com.qbhj
3)、com.qbhj.Test

ch.qos.logback.classic.LoggerContext#getLogger(java.lang.Class<?>)

    public final Logger getLogger(final Class<?> clazz) {return getLogger(clazz.getName());}@Overridepublic final Logger getLogger(final String name) {if (name == null) {throw new IllegalArgumentException("name argument cannot be null");}
// root直接返回// if we are asking for the root logger, then let us return it without// wasting timeif (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {return root;}// 非root,先拿到root,然后逐级创建其子loggerint i = 0;Logger logger = root;// check if the desired logger exists, if it does, return it// without further ado.Logger childLogger = (Logger) loggerCache.get(name);// if we have the child, then let us return it without wasting timeif (childLogger != null) {return childLogger;}// if the desired logger does not exist, them create all the loggers// in between as well (if they don't already exist)String childName;while (true) {int h = LoggerNameUtil.getSeparatorIndexOf(name, i);if (h == -1) {childName = name;} else {childName = name.substring(0, h);}// move i left of the last pointi = h + 1;synchronized (logger) {  // 此logger为 rootchildLogger = logger.getChildByName(childName);if (childLogger == null) {
// 调用Logger创建子节点childLogger = logger.createChildByName(childName);loggerCache.put(childName, childLogger);incSize();}}logger = childLogger;if (h == -1) {return childLogger;}}}// ch.qos.logback.classic.Logger#createChildByNameLogger createChildByName(final String childName) {int i_index = LoggerNameUtil.getSeparatorIndexOf(childName, this.name.length() + 1);if (i_index != -1) {throw new IllegalArgumentException("For logger [" + this.name + "] child name [" + childName+ " passed as parameter, may not include '.' after index" + (this.name.length() + 1));}if (childrenList == null) {childrenList = new CopyOnWriteArrayList<Logger>();}Logger childLogger;childLogger = new Logger(childName, this, this.loggerContext);childrenList.add(childLogger);childLogger.effectiveLevelInt = this.effectiveLevelInt;return childLogger;}

3.3、level属性继承 和 优先级

1、若自身level为空,则继承父级Logger的level值

ch.qos.logback.classic.Logger#setLevel

    public synchronized void setLevel(Level newLevel) {if (level == newLevel) {// nothing to do;return;}// <root> 的 level 必填if (newLevel == null && isRootLogger()) {throw new IllegalArgumentException("The level of the root logger cannot be set to null");}level = newLevel;
// 若level为空,则使用其父logger的有效levelif (newLevel == null) {effectiveLevelInt = parent.effectiveLevelInt;newLevel = parent.getEffectiveLevel();} else {effectiveLevelInt = newLevel.levelInt;}if (childrenList != null) {int len = childrenList.size();for (int i = 0; i < len; i++) {Logger child = (Logger) childrenList.get(i);// tell child to handle parent levelInt changechild.handleParentLevelChange(effectiveLevelInt);}}// inform listenersloggerContext.fireOnLevelChange(this, newLevel);}

3.4、additivity属性

3.4.1、源码分析

1、调用自身的appender进行日志输出
2、若additive=false,则跳出循环,否则调用父logger的appender进行日志输出

ch.qos.logback.classic.Logger#callAppenders

// aai 就是<appender-ref>中指定的<appender> AppenderAttachableImpltransient private AppenderAttachableImpl<ILoggingEvent> aai;// additive 默认为 truetransient private boolean additive = true;// ............public void callAppenders(ILoggingEvent event) {int writes = 0;
// 1、遍历当前logger的父loggerfor (Logger l = this; l != null; l = l.parent) {
// 2、若当前logger配置了append,则遍历append输出日志信息writes += l.appendLoopOnAppenders(event);
// 3、若当前logger的属性additive=false 则跳出循环,不再调用父级if (!l.additive) {break;}}// No appenders in hierarchyif (writes == 0) {loggerContext.noAppenderDefinedWarning(this);}}private int appendLoopOnAppenders(ILoggingEvent event) {
// 若当前logger配置了append,则调用其进行日志输出if (aai != null) {return aai.appendLoopOnAppenders(event);} else {return 0;}}

在这里插入图片描述

3.4.2、演示

还是上述的例子

1)、只把name=“com.qbhj.logback.controller” 的logger,additivity属性改为"true"

    <logger name="com.qbhj.logback.controller" level="INFO" additivity="true"><!-- 控制台 --><appender-ref ref="CONSOLE"/></logger><logger name="com.qbhj.logback" level="INFO" additivity="false"><appender-ref ref="CONSOLE"/></logger><root level="INFO"><!-- 控制台 --><appender-ref ref="CONSOLE"/></root>

1.1)、结果:打印两遍

###################################### num: 1
21:02:30.800  INFO --- [     http-nio-8080-exec-2] c.qbhj.logback.controller.LogController  : /additivity......
21:02:30.800  INFO --- [     http-nio-8080-exec-2] c.qbhj.logback.controller.LogController  : /additivity......

说明:

<logger name=“com.qbhj.logback.controller” level=“INFO” additivity=“true”> ,且配置了appender=CONSOLE所以打印日志。其additivity=“true”,调用父级
<logger name="com.qbhj.logback" level="INFO" additivity="false"> ,且配置了appender=CONSOLE,所以打印日志。其additivity=“false”,跳出循环,结束

Loggerappenderadditivity(默认true打印日志是否被调用
<logger name="ROOT">CONSOLEtrue
└─<logger name="com">true
│ └─<logger name="com.qbhj">true
│ │ └─<logger name="com.qbhj.logback">CONSOLEfalse
│ │ │ └─<logger name="com.qbhj.logback.controller">CONSOLEtrue

2)、只把name=“com.qbhj.logback.controller” 的logger,additivity属性改为"false", name="com.qbhj.logback"的logger改为false

    <logger name="com.qbhj.logback.controller" level="INFO" additivity="false"><!-- 控制台 --><appender-ref ref="CONSOLE"/></logger><logger name="com.qbhj.logback" level="INFO" additivity="true"><appender-ref ref="CONSOLE"/></logger><root level="INFO"><!-- 控制台 --><appender-ref ref="CONSOLE"/></root>

2.1)、结果:打印1遍

###################################### num: 1
21:04:10.603  INFO --- [     http-nio-8080-exec-1] c.qbhj.logback.controller.LogController  : /additivity......

说明:

<logger name=“com.qbhj.logback.controller” level=“INFO” additivity=“false”> ,且配置了appender=CONSOLE所以打印日志。其additivity=“false”,跳出循环,结束

Loggerappenderadditivity(默认true打印日志是否被调用
<logger name="ROOT">CONSOLEtrue
└─<logger name="com">true
│ └─<logger name="com.qbhj">true
│ │ └─<logger name="com.qbhj.logback">CONSOLEtrue
│ │ │ └─<logger name="com.qbhj.logback.controller">CONSOLEfalse

四、日志规范

阿里开发手册(黄山版)中约定如下:
二、异常日志
(三) 日志规约

7.【强制】避免重复打印日志,浪费磁盘空间,务必在日志配置文件中设置 additivity=false
正例:<logger name=“com.taobao.dubbo.config” additivity=“false”>

五、参考资料

阿里开发手册(黄山版).pdf
https://logback.qos.ch/manual/configuration.html#rootElement


http://chatgpt.dhexx.cn/article/neJRRMgo.shtml

相关文章

python之logger

import logging import os.path import time def test_log():"""指定保存日志的文件路径&#xff0c;日志级别&#xff0c;以及调用文件将日志存入到指定的文件中:paramlogger:"""# 创建一个loggerlogger logging.getLogger()logger.setLevel(l…

Python中logger日志模块详解

1 logging模块简介 logging模块是Python内置的标准模块&#xff0c;主要用于输出运行日志&#xff0c;可以设置输出日志的等级、日志保存路径、日志文件回滚等&#xff1b;相比print&#xff0c;具备如下优点&#xff1a; 可以通过设置不同的日志等级&#xff0c;在release版…

logger:一款管理日志的Python神器

最近要新开一个项目&#xff0c;需要配个 logger 来管理日志&#xff0c;今天分享一下&#xff0c;喜欢记得点赞、关注、收藏。 【注】文末提供交流互助群 import logging ori_logger logging.getLogger(custom_logger) ori_logger.setLevel(logging.INFO) ori_logger.addHa…

Tensorboard + Logger 日志记录

在Pytorch 训练模型的时候&#xff0c;需要日志帮助开发者记录些重要信息和参数&#xff0c;以方便开发者更好的调节模型及参数&#xff0c;常见的日志非 Tensorboard不可&#xff0c;但是Pytorch 对 Tensorboard 的支持不是十分完美&#xff0c;在记录模型重要参数时 Tensorbo…

深入理解 rootLogger、logLogger、qtLogger

作者: 一去、二三里个人微信号: iwaleon微信公众号: 高效程序员 在使用 Log4Qt 时,你会发现有一系列的 logger - rootLogger()、logLogger()、qtLogger(),简直傻傻分不清楚! 为什么会有这么多 logger? 各 logger 之间有什么关系? 它们均适用于哪种场景? 参考文档对这部…

logger 报错

logger 报错&#xff1a;&#xff08;log4j 起不来&#xff0c;log message打不出来&#xff09; No appenders could be found for logger (com.vip.qa.android.base.DriverFactory). log4j:WARN Please initialize the log4j system properly. 原因&#xff1a; log4j.prop…

java的logger_java.util.logging.Logger 使用详解

概述: 第1部分 创建Logger对象 要使用J2SE的日志功能,首先要取得java.util.logging.Logger实例,这可以通过Logger类的两个静态getLogger()方法来取得: staticLogger getLogger(String name) 查找或创建一个logger。staticLogger getLogger(String name, String resourceBun…

Logger日志使用教程

Java util Logger的使用步骤 Java util Logger是java原生的日志生成工具&#xff0c;不需要另外引用类库&#xff0c;使用方便&#xff0c;学习简单&#xff0c;能够在小型应用中灵活使用。下面从实际应用角度&#xff0c;对Logger的使用步骤作出总结&#xff0c;以实现快速掌握…

Logger 日志管理

转载请注明出处&#xff1a; http://blog.csdn.net/like_program/article/details/52986553 1.Logger 是什么 在我们日常的开发中&#xff0c;肯定是少不了要和 Log 打交道&#xff0c;回想一下我们是怎么使用 Log 的&#xff1a;先定义一个静态常量 TAG&#xff0c;TAG 的值通…

【转】最详细的Log4J使用教程一、入门实例二、Log4J基本使用方法三、Spring中使用Log4J四、实战经验总结

原文地址&#xff1a;http://www.codeceo.com/article/log4j-usage.html 日志是应用软件中不可缺少的部分&#xff0c;Apache的开源项目log4j是一个功能强大的日志组件,提供方便的日志记录。在apache网站&#xff1a;jakarta.apache.org/log4j可以免费下载到Log4j最新版本的软…

Logger打印日志

1. 一个最基本的例子 使用Logging框架写Log基本上就三个步骤 引入loggerg类和logger工厂类声明logger记录日志 下面看一个例子 // 1. 引入slf4j接口的Logger和LoggerFactory import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class UserService { // 2. 声明…

rename 批量修改文件名

1.批量修改文件后缀&#xff1a; rename s/// *.nii rename s/\.nii/\.txt/ * #把.nii后缀改为.txt rename s/\.txt// * #把.txt后缀去掉 rename s/$/\.nii/ * #加上后缀.nii rename s//\.nii/ * #这样会把.nii放到文件名前面&#xff0c;导致文件被隐藏。 rename s/^/dm/ * #…

Linux基本功系列之rename命令实战

文章目录 一. rename 命令介绍二. 语法格式及常用选项三. 参考案例3.1 将当前目录下所有.cfg的文件&#xff0c;替换为.txt结尾3.2 将所有出现mufeng的部分都替换为mufeng13.3 将mufeng0开头都变成mufeng00开头3.4 rename支持正则表示式 总结 前言&#x1f680;&#x1f680;&a…

VS项目rename

Visual Studio c项目更改相关文件名字 项目里面文件夹 Rename 右键 .sln文件, 打开方式–> txt方式打开编辑找到里面project的文件夹名字, Rename. 然后到资源管理器里, 找到对应的folder, Rename. .vcxproj相关文件 Rename 相关文件Rename .sln文件, txt打开–>Ren…

rename 命令 – 批量修改文件名称

rename 命令的功能是用于批量修改文件名称。与 mv 命令一次只能修改一个文件名不同&#xff0c;rename命令能够基于正则表达式对文件名进行批量修改&#xff0c;但要求是把匹配规则准确的描述给系统。 rename 命令的参数有三项&#xff1a;其一是当前文件名中要被修改的字符&am…

linux之rename命令

用字符串替换的方式批量改变文件名 rename 命令存在两个版本用法上有所区别 C语言版本, 支持通配符 [常用通配符说明] ? 表示一个任意字符 * 表示一个或一串任意字符 [charset] 可替代charset集中的任意单个字符Perl版本, 支持正则表达式 [常用正则表达式符号说明] ^…

pandas:案例详解 rename函数 修改列名和行名

pandas&#xff1a;案例详解rename函数 修改列名和索引 rename函数简介0 构建学习数据1 修改索引两种方式2 修改列名两种方式3 是否替换原列表3 pandas 字母转换大小写3 使用axis参数常见问题问题&#xff1a;参数位置错误修改办法&#xff1a; rename函数简介 df.rename(inde…

关于Unity 2020找不到PBR graph的问题,shader graph 10 版本

2020.4以后采用了船新的Shader Graph&#xff0c;变动挺大的。 建议还是换版本或者去官网学习。 PBR是没有了&#xff0c;但是有两个空的Shader 一个是Blank Shader&#xff0c;即空shader&#xff0c;自己进去里面创建PBR(前排提醒&#xff0c;不是简单拖一个PBR nodes&#x…

Substance与PBR工作流总结

关于PBR PBR即基于物理的渲染&#xff0c;是一套尝试基于真实世界光照物理模型的渲染技术合集&#xff0c;使用了一种更符合物理学规律的方式来模拟光线&#xff0c;达到更真实的渲染效果&#xff0c;而且可以直接通过物理参数来直观地达到想要的结果&#xff0c;不用通过拙劣的…

BPR算法

目录 什么是BPR算法 BPR算法简介 显示反馈与隐式反馈 矩阵分解的不足 BPR算法 符号定义 BPR算法解决方式 BPR算法两个基本假设 BPR算法推导 贝叶斯定理 BPR推导 BPR算法流程 BPR算法代码与结果 数据 BPR算法代码 BPR结果展示 什么是BPR算法 BPR算法简介 BPR&…