Issue #4830 - Enabling JMX on jetty-slf4j-impl

Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
Joakim Erdfelt 2020-04-30 14:51:42 -05:00
parent fa94cc2f6c
commit 67bd321bfc
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
10 changed files with 305 additions and 107 deletions

View File

@ -25,5 +25,7 @@ module org.eclipse.jetty.logging
requires transitive org.slf4j;
requires static java.management;
provides SLF4JServiceProvider with JettyLoggingServiceProvider;
}

View File

@ -247,7 +247,7 @@ public class JettyLogger implements LocationAwareLogger, Logger
{
long timestamp = System.currentTimeMillis();
String threadName = Thread.currentThread().getName();
getAppender().emit(this, intToLevel(levelInt), timestamp, threadName, throwable, message, argArray);
getAppender().emit(this, LevelUtils.intToLevel(levelInt), timestamp, threadName, throwable, message, argArray);
}
}
@ -636,43 +636,9 @@ public class JettyLogger implements LocationAwareLogger, Logger
getAppender().emit(this, level, timestamp, threadName, throwable, msg);
}
public static Level intToLevel(int level)
{
if (level >= JettyLogger.OFF)
return Level.ERROR;
if (level >= Level.ERROR.toInt())
return Level.ERROR;
if (level >= Level.WARN.toInt())
return Level.WARN;
if (level >= Level.INFO.toInt())
return Level.INFO;
if (level >= Level.DEBUG.toInt())
return Level.DEBUG;
if (level >= Level.TRACE.toInt())
return Level.TRACE;
return Level.TRACE; // everything else
}
public static String levelToString(int level)
{
if (level >= JettyLogger.OFF)
return "OFF";
if (level >= Level.ERROR.toInt())
return "ERROR";
if (level >= Level.WARN.toInt())
return "WARN";
if (level >= Level.INFO.toInt())
return "INFO";
if (level >= Level.DEBUG.toInt())
return "DEBUG";
if (level >= Level.TRACE.toInt())
return "TRACE";
return "OFF"; // everything else
}
@Override
public String toString()
{
return String.format("%s:%s:LEVEL=%s", JettyLogger.class.getSimpleName(), name, levelToString(level));
return String.format("%s:%s:LEVEL=%s", JettyLogger.class.getSimpleName(), name, LevelUtils.levelToString(level));
}
}

View File

@ -26,7 +26,6 @@ import java.security.PrivilegedAction;
import java.util.Locale;
import java.util.Properties;
import java.util.TimeZone;
import java.util.function.Function;
import org.slf4j.event.Level;
@ -82,7 +81,7 @@ public class JettyLoggerConfiguration
startName = startName.substring(0, startName.length() - SUFFIX_STACKS.length());
}
Boolean hideStacks = walkParentLoggerNames(startName, (key) ->
Boolean hideStacks = JettyLoggerFactory.walkParentLoggerNames(startName, (key) ->
{
String stacksBool = properties.getProperty(key + SUFFIX_STACKS);
if (stacksBool != null)
@ -124,12 +123,12 @@ public class JettyLoggerConfiguration
startName = startName.substring(0, startName.length() - SUFFIX_LEVEL.length());
}
Integer level = walkParentLoggerNames(startName, (key) ->
Integer level = JettyLoggerFactory.walkParentLoggerNames(startName, (key) ->
{
String levelStr = properties.getProperty(key + SUFFIX_LEVEL);
if (levelStr != null)
String levelStr1 = properties.getProperty(key + SUFFIX_LEVEL);
if (levelStr1 != null)
{
return getLevelInt(key, levelStr);
return LevelUtils.getLevelInt(key, levelStr1);
}
return null;
});
@ -140,7 +139,7 @@ public class JettyLoggerConfiguration
String levelStr = properties.getProperty("log" + SUFFIX_LEVEL);
if (levelStr != null)
{
level = getLevelInt("log", levelStr);
level = LevelUtils.getLevelInt("log", levelStr);
}
}
@ -193,6 +192,11 @@ public class JettyLoggerConfiguration
});
}
public String getString(String key, String defValue)
{
return properties.getProperty(key, defValue);
}
public boolean getBoolean(String key, boolean defValue)
{
String val = properties.getProperty(key, Boolean.toString(defValue));
@ -216,36 +220,6 @@ public class JettyLoggerConfiguration
}
}
private Integer getLevelInt(String levelSegment, String levelStr)
{
if (levelStr == null)
{
return null;
}
String levelName = levelStr.trim().toUpperCase(Locale.ENGLISH);
switch (levelName)
{
case "ALL":
return JettyLogger.ALL;
case "TRACE":
return Level.TRACE.toInt();
case "DEBUG":
return Level.DEBUG.toInt();
case "INFO":
return Level.INFO.toInt();
case "WARN":
return Level.WARN.toInt();
case "ERROR":
return Level.ERROR.toInt();
case "OFF":
return JettyLogger.OFF;
default:
System.err.println("Unknown JettyLogger/Slf4J Level [" + levelSegment + "]=[" + levelName + "], expecting only [ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF] as values.");
return null;
}
}
private URL getResource(ClassLoader loader, String resourceName)
{
if (loader == null)
@ -303,30 +277,4 @@ public class JettyLoggerConfiguration
}
return null;
}
private <T> T walkParentLoggerNames(String startName, Function<String, T> nameFunction)
{
String nameSegment = startName;
// Checking with FQCN first, then each package segment from longest to shortest.
while ((nameSegment != null) && (nameSegment.length() > 0))
{
T ret = nameFunction.apply(nameSegment);
if (ret != null)
return ret;
// Trim and try again.
int idx = nameSegment.lastIndexOf('.');
if (idx >= 0)
{
nameSegment = nameSegment.substring(0, idx);
}
else
{
nameSegment = null;
}
}
return null;
}
}

View File

@ -22,16 +22,16 @@ import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
import java.util.function.Function;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
public class JettyLoggerFactory implements ILoggerFactory
public class JettyLoggerFactory implements ILoggerFactory, JettyLoggerFactoryMBean
{
private static final String ROOT_LOGGER_NAME = "";
private final JettyLoggerConfiguration configuration;
private final JettyLogger rootLogger;
private ConcurrentMap<String, JettyLogger> loggerMap;
private final ConcurrentMap<String, JettyLogger> loggerMap;
public JettyLoggerFactory(JettyLoggerConfiguration config)
{
@ -41,9 +41,9 @@ public class JettyLoggerFactory implements ILoggerFactory
StdErrAppender appender = new StdErrAppender(configuration);
rootLogger = new JettyLogger(this, ROOT_LOGGER_NAME, appender);
loggerMap.put(ROOT_LOGGER_NAME, rootLogger);
rootLogger.setLevel(configuration.getLevel(ROOT_LOGGER_NAME));
rootLogger = new JettyLogger(this, Logger.ROOT_LOGGER_NAME, appender);
loggerMap.put(Logger.ROOT_LOGGER_NAME, rootLogger);
rootLogger.setLevel(configuration.getLevel(Logger.ROOT_LOGGER_NAME));
}
/**
@ -54,7 +54,7 @@ public class JettyLoggerFactory implements ILoggerFactory
*/
public JettyLogger getJettyLogger(String name)
{
if (name.equals(ROOT_LOGGER_NAME))
if (name.equals(Logger.ROOT_LOGGER_NAME))
{
return getRootLogger();
}
@ -179,4 +179,67 @@ public class JettyLoggerFactory implements ILoggerFactory
return dense.toString();
}
public static <T> T walkParentLoggerNames(String startName, Function<String, T> nameFunction)
{
String nameSegment = startName;
// Checking with FQCN first, then each package segment from longest to shortest.
while ((nameSegment != null) && (nameSegment.length() > 0))
{
T ret = nameFunction.apply(nameSegment);
if (ret != null)
return ret;
// Trim and try again.
int idx = nameSegment.lastIndexOf('.');
if (idx >= 0)
{
nameSegment = nameSegment.substring(0, idx);
}
else
{
nameSegment = null;
}
}
return null;
}
@Override
public String[] getLoggerNames()
{
return loggerMap.keySet().toArray(new String[0]);
}
@Override
public int getLoggerCount()
{
return loggerMap.size();
}
@Override
public String getLoggerLevel(String loggerName)
{
return walkParentLoggerNames(loggerName, (key) ->
{
JettyLogger logger = loggerMap.get(key);
if (key != null)
{
return LevelUtils.levelToString(logger.getLevel());
}
return null;
});
}
@Override
public void setLoggerLevel(String loggerName, String levelName)
{
Integer levelInt = LevelUtils.getLevelInt(loggerName, levelName);
if (levelInt != null)
{
JettyLogger jettyLogger = getJettyLogger(loggerName);
jettyLogger.setLevel(levelInt);
}
}
}

View File

@ -0,0 +1,31 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.logging;
@SuppressWarnings("unused")
public interface JettyLoggerFactoryMBean
{
int getLoggerCount();
String[] getLoggerNames();
void setLoggerLevel(String loggerName, String levelName);
String getLoggerLevel(String loggerName);
}

View File

@ -0,0 +1,50 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.logging;
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
public class JettyLoggingJmx
{
public static void initialize(JettyLoggerConfiguration config, JettyLoggerFactory loggerFactory)
{
if (!config.getBoolean("org.eclipse.jetty.logging.jmx", false))
{
loggerFactory.getJettyLogger(JettyLoggingJmx.class.getName()).debug("JMX not enabled");
return;
}
try
{
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
String contextName = config.getString("org.eclipse.jetty.logging.jmx.contextName", "default");
ObjectName objName = new ObjectName(JettyLoggerFactory.class.getName() + ":name=" + contextName);
mbs.registerMBean(loggerFactory, objName);
}
catch (Throwable cause)
{
JettyLogger logger = loggerFactory.getJettyLogger(JettyLoggingJmx.class.getName());
logger.warn("java.management not available.");
logger.debug("java.management is not available", cause);
}
}
}

View File

@ -45,6 +45,8 @@ public class JettyLoggingServiceProvider implements SLF4JServiceProvider
loggerFactory = new JettyLoggerFactory(config);
markerFactory = new BasicMarkerFactory();
mdcAdapter = new NOPMDCAdapter(); // TODO: Provide Jetty Implementation?
JettyLoggingJmx.initialize(config, loggerFactory);
}
public JettyLoggerFactory getJettyLoggerFactory()

View File

@ -0,0 +1,90 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.logging;
import java.util.Locale;
import org.slf4j.event.Level;
public class LevelUtils
{
public static Integer getLevelInt(String loggerName, String levelStr)
{
if (levelStr == null)
{
return null;
}
String levelName = levelStr.trim().toUpperCase(Locale.ENGLISH);
switch (levelName)
{
case "ALL":
return JettyLogger.ALL;
case "TRACE":
return Level.TRACE.toInt();
case "DEBUG":
return Level.DEBUG.toInt();
case "INFO":
return Level.INFO.toInt();
case "WARN":
return Level.WARN.toInt();
case "ERROR":
return Level.ERROR.toInt();
case "OFF":
return JettyLogger.OFF;
default:
System.err.println("Unknown JettyLogger/Slf4J Level [" + loggerName + "]=[" + levelName + "], expecting only [ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF] as values.");
return null;
}
}
public static Level intToLevel(int level)
{
if (level >= JettyLogger.OFF)
return Level.ERROR;
if (level >= Level.ERROR.toInt())
return Level.ERROR;
if (level >= Level.WARN.toInt())
return Level.WARN;
if (level >= Level.INFO.toInt())
return Level.INFO;
if (level >= Level.DEBUG.toInt())
return Level.DEBUG;
if (level >= Level.TRACE.toInt())
return Level.TRACE;
return Level.TRACE; // everything else
}
public static String levelToString(int level)
{
if (level >= JettyLogger.OFF)
return "OFF";
if (level >= Level.ERROR.toInt())
return "ERROR";
if (level >= Level.WARN.toInt())
return "WARN";
if (level >= Level.INFO.toInt())
return "INFO";
if (level >= Level.DEBUG.toInt())
return "DEBUG";
if (level >= Level.TRACE.toInt())
return "TRACE";
return "OFF"; // everything else
}
}

View File

@ -609,6 +609,6 @@ public class JettyLoggerTest
private void assertLevel(JettyLogger log, Level expectedLevel)
{
assertThat("Log[" + log.getName() + "].level",
JettyLogger.levelToString(log.getLevel()), is(expectedLevel.toString()));
LevelUtils.levelToString(log.getLevel()), is(expectedLevel.toString()));
}
}

View File

@ -0,0 +1,46 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.logging;
import java.util.Properties;
public class JmxExperiment
{
public static void main(String[] args)
{
try
{
Properties props = new Properties();
props.setProperty("org.eclipse.jetty.logging.jmx", "true");
props.setProperty("org.eclipse.jetty.logging.jmx.contextName", "Main");
props.setProperty("org.eclipse.jetty.logging.LEVEL", "DEBUG");
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
JettyLoggerFactory loggerFactory = new JettyLoggerFactory(config);
JettyLoggingJmx.initialize(config, loggerFactory);
loggerFactory.getJettyLogger(JmxExperiment.class.getName()).info("Waiting for key-press");
System.in.read();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}