I have deployed springboot application in PCF . I want to log the message based on the environment variable .What should I do so that the run time log level change will work
If you are using logback
api to configure logging in the project then you can use the AutoScan feature of logback
api.
As per documentation
logback-classic will scan for changes in its configuration file and automatically reconfigure itself when the configuration file changes. In order to instruct logback-classic to scan for changes in its configuration file and to automatically re-configure itself set the scan attribute of the element to true.
<configuration scan="true">
...
</configuration>
Scan frequency: "By default, the configuration file will be scanned for changes once every minute
". See the logback
API documentation for more details.
You can create a jsp and using it directly like
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:
<input name="logNameFilter" type="text" size="50" value="<%=(logNameFilter == null ? "":logNameFilter)%>"
class="filterText"/>
<input name="logNameFilterType" type="submit" value="<%=beginsWithFilter%>" class="filterButton"/>
<input name="logNameFilterType" type="submit" value="<%=containsFilter%>" class="filterButton"/>
<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>
<%
}
}
%>
</td>
</tr>
<%
}
%>
</table>
<h2>
Revision: 1.0<br/>
Page Load Time (Millis): <%=(System.currentTimeMillis() - beginPageLoadTime)%>
</h2>
</div>
</body>
</html>
You can also add a settings page in the web service to update the log level. This can then be done using ajax. The following example includes login and csrf token:
First, add some form to specify the new log level. Can be improved for example by using a select
element.
<form>
<input type="text" id="logClassName" name="logClassName"/>
<input type="text" id="logLevel" name="logLevel" />
<button onclick="submitLogLevelChange(); return false;">Submit</button>
</form>
Then, the request is sent:
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
}
});
}
The following function injects the csrf token to the POST request:
function setHeader(xhr) {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
xhr.setRequestHeader(header, token);
}
If you use Log4j 2 for logging you can easily configuration it to set the log level to use based on an environment variable or system property. If you do it this way you won't need to modify the file just because the environment changed.
<?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>