source

스프링 부트 응용 프로그램을 재시작하지 않고 런타임에서 로그 수준을 변경하는 방법

ittop 2023. 4. 2. 11:55
반응형

스프링 부트 응용 프로그램을 재시작하지 않고 런타임에서 로그 수준을 변경하는 방법

PCF에 springboot 어플리케이션을 도입했습니다.환경변수에 따라 메시지를 기록합니다.응용 프로그램을 재시작하지 않고 런타임 로그 수준 변경이 작동하려면 어떻게 해야 합니까?

Spring Boot 1.5+의 로그레벨 변경은 http-endpoint를 사용하여 실행할 수 있습니다.

더하다

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

사용할 수 있는 것보다

curl -X "POST" "http://localhost:8080/loggers/de.springbootbuch" \
     -H "Content-Type: application/json; charset=utf-8" \
     -d $'{
  "configuredLevel": "WARN"
}'  

여기서 /loggers/ 이외의 모든 것은 로거의 이름입니다.

이것을 PCF로 실행하면, 한층 더 향상됩니다.이는 백엔드에서 직접 지원됩니다.

스프링 부트 2.1.5+의 경우:

먼저 액추에이터 플러그인이 필요합니다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

가 있습니다.loggers

management.endpoints.web.exposure.include=health,info,loggers

마지막으로 Rest Endpoints를 사용하여 로거에 대한 정보를 가져오고 로깅 수준을 설정할 수 있습니다.

curl -X "GET" "http://localhost:8080/actuator/loggers"

「 」을 합니다.Root할 수

curl -X "POST" "http://localhost:8080/actuator/loggers/ROOT" -H "Content-Type: application/json; charset=utf-8"   -d $'{ "configuredLevel": "INFO" }'

이것은 @Michael Simons answer의 확장입니다.이 방법을 사용하면 이를 위한 UI를 사용할 수 있습니다.

이 방법은 조금 더 길지만 훨씬 더 많이 해결된다.Spring Boot Admin Server라는 을 사용합니다.

  1. 먼저 몇 가지 종속성을 포함해야 합니다.

    <!--Dependency for registering your app as a Spring Boot Admin Server-->
    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-server</artifactId>
        <version>1.3.3</version>
    </dependency>
    
    <!--Provide a nice looking ui-->
    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-server-ui</artifactId>
        <version>1.3.3</version>
    </dependency>
    
    <!--Dependency for registering your app as a Spring Boot Admin Client-->
    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-client</artifactId>
        <version>1.3.0</version>
    </dependency>
    <dependency>
            <groupId>org.jolokia</groupId>
            <artifactId>jolokia-core</artifactId>
    </dependency>
    
  2. "Spring Boot Admin Server"라는 .@EnableAdminServer.

    @SpringBootApplication
    @EnableAdminServer
    public class Application {
       public static void main(String[] args) {
          // ... your code as before ...
       }
    }
    
  3. 고객님의 고객명application.properties다음을 추가합니다.

    Spring Boot Admin Server에 앱을 등록합니다.여전히 앱입니다.

    spring.boot.admin.url=http://localhost:8031
    

    Spring Boot Admin Server에 클라이언트 검색 위치 지시

    // For versions 2.*.*
    spring.boot.admin.client.url=http://localhost:8031
    // For versions 1.*.*
    spring.boot.admin.client.service-url=http://localhost:8031
    spring.boot.admin.client.management-url=http://localhost:8031
    spring.boot.admin.client.health-url=http://localhost:8031/health
    
  4. 고객님의 고객명logback.xml 행만 .<jmxConfigurator/>JMX 경유 로그백을 설정할 수 있습니다.자세한 내용은 이쪽

... 그럼 넌 끝이야.이제 실행 시 임의의 로거의 디버깅레벨을 변경할 수 있게 되었습니다

사용하시는 Admin 이 「Spring Boot Admin Server」의 URL 를 참조해 주세요.네, 네, 네, 네, 네, 네, 네, 네, 기기를요요요요요요 。http:/localhost:8031

ii. 등록된 어플리케이션(클라이언트)의 리스트가 홈페이지에 표시됩니다.

Details다른 페이지로 이동할 수 있도록 등록된 클라이언트에 대해 설명합니다.

iv를 합니다.Logging탭에는 어플리케이션에 등록된 모든 로거가 나열됩니다.

v. 로그 수준을 변경할 수 있으며 런타임에 로그 수준이 변경됩니다.여기 당신이 기대하는 것의 일부분이 있습니다.

런타임에 로깅 수준 변경

「 」를 사용하고 logbackapi를 사용하여 프로젝트 로그인을 구성할 수 있습니다.그러면 AutoScan 기능을 사용하여logbackapi. api와 . 매뉴얼에 따라

logback-display는 컨피규레이션파일 변경을 스캔하여 컨피규레이션파일이 변경되면 자동으로 재설정합니다.logback-classic에 컨피규레이션파일 변경을 스캔하여 자동으로 재설정하도록 지시하려면 요소의 스캔 속성을 true로 설정합니다.

<configuration scan="true"> 
  ... 
</configuration> 

빈도 " 캔캔빈 " " : "By default, the configuration file will be scanned for changes once every minute "를하십시오.logback 자세한 내용은 API 매뉴얼을 참조하십시오.

Spring Boot 1.5.x 이후 로거 엔드포인트를 사용하여 원하는 로깅 수준을 POST할 수 있습니다.

컨트롤러에서 다음 코드를 사용하여 API를 호출하여 로그 수준을 변경할 수 있습니다.

@PostMapping("/changeloglevel")
public void changeloglevel(@RequestParam String loglevel)
{
    LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
    loggerContext.getLogger("package where springboot main class resides").setLevel(Level.toLevel(loglevel));   
}

로그 레벨은 ERROR, INFO, WARN 등입니다.

기본 로깅 공급자는 logback입니다.실행 시 로깅 수준을 변경할 수 있도록 시스템을 설정하려면 다음 절차를 수행해야 합니다.

첫첫 의 첫 src/main/resources" " " 라는 .logback-spring.xml으로 JMX를합니다.

<configuration>
  <include resource="org/springframework/boot/logging/logback/base.xml"/>
  <jmxConfigurator />    
</configuration>

브릿지에 합니다.「 Jolokia JMX-over-HTTP 」 。org.jolokia:jolokia-core.

으로, 「 」를 할 수 되었습니다./jolokiaspring boot 어플리케이션의 엔드 포인트.프로토콜은 여기에 기재되어 있습니다.그것은 예쁘지 않다.시작하기 전에 몇 가지 예를 들어보겠습니다.GET「 」 「 」 、 「 」 、 「 」

ROOT 로거 수준 표시:

/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/getLoggerLevel/ROOT

ROOT 로거 레벨을 디버깅으로 변경합니다.

/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/setLoggerLevel/ROOT/debug

는 spring-boot-timeaces를 하고 있습니다./jolokia되어 있습니다.sensitive=true스프링 시큐리티

세 가지 방법이 있습니다.

  1. 스프링 액추에이터 - /loggers 엔드포인트 활용
  2. 스프링 부트 관리자
  3. LogBack 자동 스캔

자세한 것은, Amy DeGregorio블로그를 봐 주세요.

IntelliJ 사용자의 경우:저도 비슷한 시나리오를 썼고, 저만의 Intelij 플러그인을 쓰게 되었습니다.그것은 지금까지 저에게 있어서 가장 간단한 해결책이었습니다.기본적으로 액추에이터 로그 엔드포인트 위의 래퍼입니다.

  1. 위의 설명에 따라 Spring Boot Actuator에서 로그 엔드포인트를 활성화합니다.
  2. IntelIj 플러그인 로그 부트 설치
  3. Spring Boot App에 접속하면 바로 사용할 수 있습니다.

https://plugins.jetbrains.com/plugin/17101-logboot LogBoot 플러그인에서 확인하세요.

jsp를 생성하여 다음과 같이 직접 사용할 수 있습니다.https://gist.github.com/iamkristian/943918/043ac51bd80321a0873d93277979c8a9a20a9a48#file-log4jadmin-jsp

<%@ page language="java" contentType="text/html;charset=UTF-8" %>
<%@ page import="org.apache.log4j.Level" %>
<%@ page import="org.apache.log4j.LogManager" %>
<%@ page import="org.apache.log4j.Logger" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Enumeration" %>
<%@ page import="java.util.Set" %>
<%@ page import="java.util.Arrays" %>
<% long beginPageLoadTime = System.currentTimeMillis();%>

<html>
<head>
    <title>Log4J Administration</title>
    <style type="text/css">
        <!--
        #content {
            margin: 0px;
            padding: 0px;
            text-align: center;
            background-color: #ccc;
            border: 1px solid #000;
            width: 100%;
        }
        body {
            position: relative;
            margin: 10px;
            padding: 0px;
            color: #333;
        }
        h1 {
            margin-top: 20px;
            font: 1.5em Verdana, Arial, Helvetica sans-serif;
        }
        h2 {
            margin-top: 10px;
            font: 0.75em Verdana, Arial, Helvetica sans-serif;
            text-align: left;
        }
        a, a:link, a:visited, a:active {
            color: red;
            text-decoration: none;
            text-transform: uppercase;
        }
        table {
            width: 100%;
            background-color: #000;
            padding: 3px;
            border: 0px;
        }
        th {
            font-size: 0.75em;
            background-color: #ccc;
            color: #000;
            padding-left: 5px;
            text-align: center;
            border: 1px solid #ccc;
            white-space: nowrap;
        }
        td {
            font-size: 0.75em;
            background-color: #fff;
            white-space: nowrap;
        }
        td.center {
            font-size: 0.75em;
            background-color: #fff;
            text-align: center;
            white-space: nowrap;
        }
        .filterForm {
            font-size: 0.9em;
            background-color: #000;
            color: #fff;
            padding-left: 5px;
            text-align: left;
            border: 1px solid #000;
            white-space: nowrap;
        }
        .filterText {
            font-size: 0.75em;
            background-color: #fff;
            color: #000;
            text-align: left;
            border: 1px solid #ccc;
            white-space: nowrap;
        }
        .filterButton {
            font-size: 0.75em;
            background-color: #000;
            color: #fff;
            padding-left: 5px;
            padding-right: 5px;
            text-align: center;
            border: 1px solid #ccc;
            width: 100px;
            white-space: nowrap;
        }
        -->
    </style>
</head>
<body onLoad="javascript:document.logFilterForm.logNameFilter.focus();">
<%
    String containsFilter = "Contains";
    String beginsWithFilter = "Begins With";
    String[] logLevels = {"debug", "info", "warn", "error", "fatal", "off"};
    String targetOperation = (String) request.getParameter("operation");
    String targetLogger = (String) request.getParameter("logger");
    String targetLogLevel = (String) request.getParameter("newLogLevel");
    String logNameFilter = (String) request.getParameter("logNameFilter");
    String logNameFilterType = (String) request.getParameter("logNameFilterType");
%>
<div id="content">
<h1>Log4J Administration</h1>
<div class="filterForm">
    <form action="log4jAdmin.jsp" name="logFilterForm">Filter Loggers:&nbsp;&nbsp;
        <input name="logNameFilter" type="text" size="50" value="<%=(logNameFilter == null ? "":logNameFilter)%>"
               class="filterText"/>
        <input name="logNameFilterType" type="submit" value="<%=beginsWithFilter%>" class="filterButton"/>&nbsp;
        <input name="logNameFilterType" type="submit" value="<%=containsFilter%>" class="filterButton"/>&nbsp;
        <input name="logNameClear" type="button" value="Clear" class="filterButton"
               onmousedown='javascript:document.logFilterForm.logNameFilter.value="";'/>
        <input name="logNameReset" type="reset" value="Reset" class="filterButton"/>
        <param name="operation" value="changeLogLevel"/>
    </form>
</div>
<table cellspacing="1">
    <tr>
        <th width="25%">Logger</th>
        <th width="25%">Parent Logger</th>
        <th width="15%">Effective Level</th>
        <th width="35%">Change Log Level To</th>
    </tr>
    <%
        Enumeration loggers = LogManager.getCurrentLoggers();
        HashMap loggersMap = new HashMap(128);
        Logger rootLogger = LogManager.getRootLogger();
        if (!loggersMap.containsKey(rootLogger.getName())) {
            loggersMap.put(rootLogger.getName(), rootLogger);
        }
        while (loggers.hasMoreElements()) {
            Logger logger = (Logger) loggers.nextElement();
            if (logNameFilter == null || logNameFilter.trim().length() == 0) {
                loggersMap.put(logger.getName(), logger);
            } else if (containsFilter.equals(logNameFilterType)) {
                if (logger.getName().toUpperCase().indexOf(logNameFilter.toUpperCase()) >= 0) {
                    loggersMap.put(logger.getName(), logger);
                }
            } else {
// Either was no filter in IF, contains filter in ELSE IF, or begins with in ELSE
                if (logger.getName().startsWith(logNameFilter)) {
                    loggersMap.put(logger.getName(), logger);
                }
            }
        }
        Set loggerKeys = loggersMap.keySet();
        String[] keys = new String[loggerKeys.size()];
        keys = (String[]) loggerKeys.toArray(keys);
        Arrays.sort(keys, String.CASE_INSENSITIVE_ORDER);
        for (int i = 0; i < keys.length; i++) {
            Logger logger = (Logger) loggersMap.get(keys[i]);
// MUST CHANGE THE LOG LEVEL ON LOGGER BEFORE GENERATING THE LINKS AND THE
// CURRENT LOG LEVEL OR DISABLED LINK WON'T MATCH THE NEWLY CHANGED VALUES
            if ("changeLogLevel".equals(targetOperation) && targetLogger.equals(logger.getName())) {
                Logger selectedLogger = (Logger) loggersMap.get(targetLogger);
                selectedLogger.setLevel(Level.toLevel(targetLogLevel));
            }
            String loggerName = null;
            String loggerEffectiveLevel = null;
            String loggerParent = null;
            if (logger != null) {
                loggerName = logger.getName();
                loggerEffectiveLevel = String.valueOf(logger.getEffectiveLevel());
                loggerParent = (logger.getParent() == null ? null : logger.getParent().getName());
            }
    %>
    <tr>
        <td><%=loggerName%>
        </td>
        <td><%=loggerParent%>
        </td>
        <td><%=loggerEffectiveLevel%>
        </td>
        <td class="center">
            <%
                for (int cnt = 0; cnt < logLevels.length; cnt++) {
                    String url = "log4jAdmin.jsp?operation=changeLogLevel&logger=" + loggerName + "&newLogLevel=" + logLevels[cnt] + "&logNameFilter=" + (logNameFilter != null ? logNameFilter : "") + "&logNameFilterType=" + (logNameFilterType != null ? logNameFilterType : "");
                    if (logger.getLevel() == Level.toLevel(logLevels[cnt]) || logger.getEffectiveLevel() == Level.toLevel(logLevels[cnt])) {
            %>
            [<%=logLevels[cnt].toUpperCase()%>]
            <%
            } else {
            %>
            <a href='<%=url%>'>[<%=logLevels[cnt]%>]</a>&nbsp;
            <%
                    }
                }
            %>
        </td>
    </tr>
    <%
        }
    %>
</table>
<h2>
    Revision: 1.0<br/>
    Page Load Time (Millis): <%=(System.currentTimeMillis() - beginPageLoadTime)%>
</h2>
</div>
</body>
</html>

웹 서비스에 설정 페이지를 추가하여 로그 수준을 업데이트할 수도 있습니다.그리고 나서 이것은 아약스를 사용하여 수행될 수 있다.다음으로 로그인 토큰과 CSRF 토큰의 예를 나타냅니다.

먼저 몇 가지 양식을 추가하여 새 로그 수준을 지정합니다.예를 들어 다음과 같은 방법으로 개선할 수 있습니다.select요소.

<form>
    <input type="text" id="logClassName" name="logClassName"/>
    <input type="text" id="logLevel" name="logLevel" />
    <button onclick="submitLogLevelChange(); return false;">Submit</button>
</form>

다음으로 요구가 송신됩니다.

function submitLogLevelChange() {
    var className = document.getElementById('logClassName').value;
    var logLevel = document.getElementById("logLevel").value;
    $.ajax({
        // Set up security, see below.
        beforeSend: setHeader,
        type: 'POST',
        // specify the logger to be modified
        url: "/loggers/" + className,
        // specify the new log level
        data: '{"configuredLevel":"' + logLevel + '"}',
        contentType: 'application/json',
        processData: false,
        }).done(function(data, textStatus, jqXHR) {
            if (jqXHR.status === 200) {
                // Happy
            } else if (jqXHR.status === 401) {
                // Logged out or not enough user rights
            } else {
                //Some other problem
            }
        })
        .fail(function(jqXHR, textStatus ) {
            if (jqXHR.status === 200) {
                // Actually was successful, FireFox has some issues...
            } else {
                // Failure
            }
        });
    }

다음 함수는 POST 요구에 csrf 토큰을 주입합니다.

function setHeader(xhr) {
  var token = $("meta[name='_csrf']").attr("content");
  var header = $("meta[name='_csrf_header']").attr("content");
  xhr.setRequestHeader(header, token);
}

Log4j 2를 로깅에 사용하는 경우 환경 변수 또는 시스템 속성에 따라 사용할 로그 수준을 설정하도록 쉽게 구성할 수 있습니다.이렇게 하면 환경이 변경되었다고 해서 파일을 수정할 필요가 없습니다.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="ERROR" monitorInterval="300">
<properties>
  <property name="LOG_DIR">${sys:user.dir}/logs/</property>
  <property name="log_env">${sys:env:-lab}</property>
  <property name="flow_lab">${sys:flow_match:-ACCEPT}</property>
  <property name="flow_prod">NEUTRAL</property>
  <property name="level_lab">DEBUG</property>
  <property name="level_prod">INFO</property>
</properties>
<MarkerFilter marker="FLOW" onMatch="${flow_${log_env}}" onMismatch="NEUTRAL"/>
<Appenders>
    <Console name="Console" target="SYSTEM_OUT">
        <PatternLayout pattern="%d{ABSOLUTE} %-5level # %class.%method %m%n" />
    </Console>

    <RollingFile name="log4j" fileName="${LOG_DIR}/log4j.txt" filePattern="${LOG_DIR}/archive/log4j.txt.%d{yyyyMMdd_HHmmss}-%i">
        <PatternLayout>
            <MarkerPatternSelector defaultPattern="%d [%t] %-5p %X{requestId, sessionId, loginId, userId, ipAddress, corpAcctNumber} %C{1.}.%M:%L - %m%n">
                <PatternMatch key="FLOW" pattern="%d [%t] %-5p %X{requestId, sessionId, loginId, userId, ipAddress, corpAcctNumber} -------- %C{1.}.%M:%L %msg --------%n"/>
            </MarkerPatternSelector>
        </PatternLayout>
        <Policies>
            <SizeBasedTriggeringPolicy size="30 MB"/>
        </Policies>
        <!-- A max of 20 will allow 20 files per second with the date pattern specified on the RollingFile declaration.
             Hopefully that is a ridiculous value -->
        <DefaultRolloverStrategy min="1" max="20">
            <Delete basePath="${LOG_DIR}/archive">
                <!-- Nested conditions: the inner condition is only evaluated on files for which the outer conditions are true. -->
                <IfFileName glob="log4j.txt.*">
                    <!-- Only allow 1 GB of files to accumulate -->
                    <IfAccumulatedFileSize exceeds="1 GB"/>
                </IfFileName>
            </Delete>
        </DefaultRolloverStrategy>
    </RollingFile>
</Appenders>
<Loggers>
    <Logger name="com.mycorp.package1" level="${level_${log_env}}" additivity="false">
        <AppenderRef ref="log4j"/>
    </Logger>
    <Logger name="com.mycorp.package2" level="info" additivity="false">
        <AppenderRef ref="log4j"/>
    </Logger>
    <Root level="${level_${log_env}}">
        <AppenderRef ref="log4j" />
    </Root>
</Loggers>

언급URL : https://stackoverflow.com/questions/33844580/how-do-i-change-log-level-in-runtime-without-restarting-spring-boot-application

반응형