Issue #4830 - Add JMX to new Jetty 10 jetty-slf4j-impl.

Alternative implementation that adds JMX support for jetty-slf4j-impl.

This version modifies MBeanContainer to be aware of @MXBean annotations and *MBean and *MXBean interfaces, so it does not require a dependency on jetty-jmx nor on java.management.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2020-05-03 10:06:41 +02:00
parent 95d981b699
commit d2399205ff
16 changed files with 289 additions and 322 deletions

View File

@ -22,7 +22,7 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>

View File

@ -61,6 +61,7 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlets.PushCacheFilter;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.LoggerFactory;
public class Http2Server
{
@ -74,6 +75,8 @@ public class Http2Server
ManagementFactory.getPlatformMBeanServer());
server.addBean(mbContainer);
server.addBean(LoggerFactory.getILoggerFactory());
ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
Path docroot = Paths.get("src/main/resources/docroot");
if (!Files.exists(docroot))

View File

@ -4,9 +4,7 @@ org.eclipse.jetty.LEVEL=INFO
#com.example.LEVEL=INFO
## Configure a level for specific logger
#com.example.MyComponent.LEVEL=INFO
## Enable JMX management of Jetty Logging
# org.eclipse.jetty.logging.jmx=true
## Configure JMX Context Name
# org.eclipse.jetty.logging.jmx.contextName=JettyServer
# org.eclipse.jetty.logging.jmx.context=JettyServer
## Hide stacks traces in an arbitrary logger tree
#com.example.STACKS=false

View File

@ -30,10 +30,12 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import javax.management.DynamicMBean;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MXBean;
import javax.management.ObjectName;
import javax.management.modelmbean.ModelMBean;
@ -60,7 +62,7 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
private final MBeanServer _mbeanServer;
private final boolean _useCacheForOtherClassLoaders;
private final ConcurrentMap<Class, MetaData> _metaData = new ConcurrentHashMap<>();
private final ConcurrentMap<Class<?>, MetaData> _metaData = new ConcurrentHashMap<>();
private final ConcurrentMap<Object, Container> _beans = new ConcurrentHashMap<>();
private final ConcurrentMap<Object, ObjectName> _mbeans = new ConcurrentHashMap<>();
private String _domain = null;
@ -151,7 +153,26 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
{
if (o == null)
return null;
Object mbean = findMetaData(container, o.getClass()).newInstance(o);
if (o instanceof DynamicMBean)
return o;
Class<?> klass = o.getClass();
while (klass != Object.class)
{
MXBean mxbean = klass.getAnnotation(MXBean.class);
if (mxbean != null && mxbean.value())
return o;
String mbeanName = klass.getName() + "MBean";
String mxbeanName = klass.getName() + "MXBean";
Class<?>[] interfaces = klass.getInterfaces();
for (Class<?> type : interfaces)
{
String name = type.getName();
if (name.equals(mbeanName) || name.equals(mxbeanName))
return o;
}
klass = klass.getSuperclass();
}
Object mbean = findMetaData(container, klass).newInstance(o);
if (mbean instanceof ObjectMBean)
((ObjectMBean)mbean).setMBeanContainer(container);
if (LOG.isDebugEnabled())
@ -338,7 +359,9 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
StringBuilder buf = new StringBuilder();
String context = (mbean instanceof ObjectMBean) ? makeName(((ObjectMBean)mbean).getObjectContextBasis()) : null;
String context = (mbean instanceof ObjectMBean)
? makeName(((ObjectMBean)mbean).getObjectContextBasis())
: makeName(reflectContextBasis(mbean));
if (context == null && parentObjectName != null)
context = parentObjectName.getKeyProperty("context");
@ -347,7 +370,9 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
buf.append("type=").append(type);
String name = (mbean instanceof ObjectMBean) ? makeName(((ObjectMBean)mbean).getObjectNameBasis()) : context;
String name = (mbean instanceof ObjectMBean)
? makeName(((ObjectMBean)mbean).getObjectNameBasis())
: makeName(reflectNameBasis(mbean));
if (name != null && name.length() > 1)
buf.append(",").append("name=").append(name);
@ -394,6 +419,28 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
}
}
private String reflectContextBasis(Object mbean)
{
return reflectBasis(mbean, "jmxContext");
}
private String reflectNameBasis(Object mbean)
{
return reflectBasis(mbean, "jmxName");
}
private String reflectBasis(Object mbean, String methodName)
{
try
{
return (String)mbean.getClass().getMethod(methodName).invoke(mbean);
}
catch (Throwable x)
{
return null;
}
}
/**
* @param basis name to strip of special characters.
* @return normalized name

View File

@ -4,16 +4,35 @@
<artifactId>jetty-project</artifactId>
<version>10.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-slf4j-impl</artifactId>
<name>Jetty :: Slf4j Implementation</name>
<description>Slf4j Logging Implementation based on Jetty's older StdErrLog</description>
<url>http://www.eclipse.org/jetty</url>
<properties>
<bundle-symbolic-name>${project.groupId}.logging</bundle-symbolic-name>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>--add-modules</arg>
<arg>java.management</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
@{argLine} ${jetty.surefire.argLine} --add-reads org.eclipse.jetty.logging=java.management
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
@ -29,15 +48,17 @@
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>
</project>

View File

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

View File

@ -32,18 +32,18 @@ public class JettyLogger implements LocationAwareLogger, Logger
/**
* The Level to set if you want this logger to be "OFF"
*/
public static final int OFF = 999;
static final int OFF = 999;
/**
* The Level to set if you want this logger to show all events from all levels.
*/
public static final int ALL = -1;
static final int ALL = Level.TRACE.toInt();
private final JettyLoggerFactory factory;
private final String name;
private final String condensedName;
private final JettyAppender appender;
private int level;
private boolean hideStacks = false;
private boolean hideStacks;
public JettyLogger(JettyLoggerFactory factory, String name, JettyAppender appender)
{
@ -54,12 +54,72 @@ public class JettyLogger implements LocationAwareLogger, Logger
{
this.factory = factory;
this.name = name;
this.condensedName = JettyLoggerFactory.condensePackageString(name);
this.condensedName = condensePackageString(name);
this.appender = appender;
this.level = level;
this.hideStacks = hideStacks;
}
/**
* Condenses a classname by stripping down the package name to just the first character of each package name
* segment.Configured
*
* <pre>
* Examples:
* "org.eclipse.jetty.test.FooTest" = "oejt.FooTest"
* "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
* </pre>
*
* @param classname the fully qualified class name
* @return the condensed name
*/
private static String condensePackageString(String classname)
{
if (classname == null || classname.isEmpty())
return "";
int rawLen = classname.length();
StringBuilder dense = new StringBuilder(rawLen);
boolean foundStart = false;
boolean hasPackage = false;
int startIdx = -1;
int endIdx = -1;
for (int i = 0; i < rawLen; i++)
{
char c = classname.charAt(i);
if (!foundStart)
{
foundStart = Character.isJavaIdentifierStart(c);
if (foundStart)
{
if (startIdx >= 0)
{
dense.append(classname.charAt(startIdx));
hasPackage = true;
}
startIdx = i;
}
}
if (foundStart)
{
if (Character.isJavaIdentifierPart(c))
endIdx = i;
else
foundStart = false;
}
}
// append remaining from startIdx
if ((startIdx >= 0) && (endIdx >= startIdx))
{
if (hasPackage)
dense.append('.');
dense.append(classname, startIdx, endIdx + 1);
}
return dense.toString();
}
@Override
public void debug(String msg)
{
@ -285,7 +345,7 @@ public class JettyLogger implements LocationAwareLogger, Logger
this.level = lvlInt;
// apply setLevel to children too.
factory.walkChildLoggers(this.getName(), (logger) -> logger.setLevel(lvlInt));
factory.walkChildrenLoggers(this.getName(), (logger) -> logger.setLevel(lvlInt));
}
@Override
@ -402,7 +462,7 @@ public class JettyLogger implements LocationAwareLogger, Logger
this.hideStacks = hideStacks;
// apply setHideStacks to children too.
factory.walkChildLoggers(this.getName(), (logger) -> logger.setHideStacks(hideStacks));
factory.walkChildrenLoggers(this.getName(), (logger) -> logger.setHideStacks(hideStacks));
}
@Override
@ -639,6 +699,6 @@ public class JettyLogger implements LocationAwareLogger, Logger
@Override
public String toString()
{
return String.format("%s:%s:LEVEL=%s", JettyLogger.class.getSimpleName(), name, LevelUtils.levelToString(level));
return String.format("%s:%s:LEVEL=%s", JettyLogger.class.getSimpleName(), name, LevelUtils.intToLevel(level));
}
}

View File

@ -77,29 +77,22 @@ public class JettyLoggerConfiguration
// strip ".STACKS" suffix (if present)
if (startName.endsWith(SUFFIX_STACKS))
{
startName = startName.substring(0, startName.length() - SUFFIX_STACKS.length());
}
Boolean hideStacks = JettyLoggerFactory.walkParentLoggerNames(startName, (key) ->
Boolean hideStacks = JettyLoggerFactory.walkParentLoggerNames(startName, key ->
{
String stacksBool = properties.getProperty(key + SUFFIX_STACKS);
if (stacksBool != null)
{
return Boolean.parseBoolean(stacksBool);
}
return null;
});
if (hideStacks != null)
return hideStacks;
return DEFAULT_HIDE_STACKS;
return hideStacks != null ? hideStacks : DEFAULT_HIDE_STACKS;
}
/**
* Get the Logging Level for the provided log name. Using the FQCN first, then each package segment from longest to
* shortest.
* <p>Returns the Logging Level for the provided log name.</p>
* <p>Uses the FQCN first, then each package segment from longest to shortest.</p>
*
* @param name the name to get log for
* @return the logging level int
@ -111,42 +104,30 @@ public class JettyLoggerConfiguration
String startName = name != null ? name : "";
// strip trailing dot
// Strip trailing dot.
while (startName.endsWith("."))
{
startName = startName.substring(0, startName.length() - 1);
}
// strip ".LEVEL" suffix (if present)
// Strip ".LEVEL" suffix (if present).
if (startName.endsWith(SUFFIX_LEVEL))
{
startName = startName.substring(0, startName.length() - SUFFIX_LEVEL.length());
}
Integer level = JettyLoggerFactory.walkParentLoggerNames(startName, (key) ->
Integer level = JettyLoggerFactory.walkParentLoggerNames(startName, key ->
{
String levelStr1 = properties.getProperty(key + SUFFIX_LEVEL);
if (levelStr1 != null)
{
return LevelUtils.getLevelInt(key, levelStr1);
}
return null;
String levelStr = properties.getProperty(key + SUFFIX_LEVEL);
return LevelUtils.getLevelInt(levelStr);
});
if (level == null)
{
// try legacy root logging config
// Try legacy root logging config.
String levelStr = properties.getProperty("log" + SUFFIX_LEVEL);
if (levelStr != null)
{
level = LevelUtils.getLevelInt("log", levelStr);
}
level = LevelUtils.getLevelInt(levelStr);
}
if (level != null)
return level;
return DEFAULT_LEVEL;
return level != null ? level : DEFAULT_LEVEL;
}
public TimeZone getTimeZone(String key)
@ -154,7 +135,6 @@ public class JettyLoggerConfiguration
String zoneIdStr = properties.getProperty(key);
if (zoneIdStr == null)
return null;
return TimeZone.getTimeZone(zoneIdStr);
}
@ -207,9 +187,7 @@ public class JettyLoggerConfiguration
{
String val = properties.getProperty(key, Integer.toString(defValue));
if (val == null)
{
return defValue;
}
try
{
return Integer.parseInt(val);
@ -223,13 +201,9 @@ public class JettyLoggerConfiguration
private URL getResource(ClassLoader loader, String resourceName)
{
if (loader == null)
{
return ClassLoader.getSystemResource(resourceName);
}
else
{
return loader.getResource(resourceName);
}
}
/**
@ -260,9 +234,7 @@ public class JettyLoggerConfiguration
{
URL propsUrl = getResource(loader, resourceName);
if (propsUrl == null)
{
return null;
}
try (InputStream in = propsUrl.openStream())
{

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.logging;
import java.util.Objects;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
@ -46,6 +47,13 @@ public class JettyLoggerFactory implements ILoggerFactory, JettyLoggerFactoryMBe
rootLogger.setLevel(configuration.getLevel(Logger.ROOT_LOGGER_NAME));
}
@SuppressWarnings("unused")
public String jmxContext()
{
// Used to build the ObjectName.
return configuration.getString("org.eclipse.jetty.logging.jmx.context", null);
}
/**
* Get a {@link JettyLogger} instance, creating if not yet existing.
*
@ -55,10 +63,7 @@ public class JettyLoggerFactory implements ILoggerFactory, JettyLoggerFactoryMBe
public JettyLogger getJettyLogger(String name)
{
if (name.equals(Logger.ROOT_LOGGER_NAME))
{
return getRootLogger();
}
return loggerMap.computeIfAbsent(name, this::createLogger);
}
@ -74,118 +79,45 @@ public class JettyLoggerFactory implements ILoggerFactory, JettyLoggerFactoryMBe
return getJettyLogger(name);
}
protected void walkChildLoggers(String parentName, Consumer<JettyLogger> childConsumer)
void walkChildrenLoggers(String parentName, Consumer<JettyLogger> childConsumer)
{
String prefix = parentName;
if (parentName.length() > 0 && !prefix.endsWith("."))
{
prefix += ".";
}
for (JettyLogger logger : loggerMap.values())
{
// Skip self.
if (logger.getName().equals(parentName))
{
// skip self
continue;
}
// is child, and is not itself
// It is a child, and is not itself.
if (logger.getName().startsWith(prefix))
{
childConsumer.accept(logger);
}
}
}
public JettyLogger getRootLogger()
JettyLogger getRootLogger()
{
return rootLogger;
}
private JettyLogger createLogger(String name)
{
// or is that handled by slf4j itself?
JettyAppender appender = rootLogger.getAppender();
int level = this.configuration.getLevel(name);
boolean hideStacks = this.configuration.getHideStacks(name);
return new JettyLogger(this, name, appender, level, hideStacks);
}
/**
* Condenses a classname by stripping down the package name to just the first character of each package name
* segment.Configured
*
* <pre>
* Examples:
* "org.eclipse.jetty.test.FooTest" = "oejt.FooTest"
* "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
* </pre>
*
* @param classname the fully qualified class name
* @return the condensed name
*/
protected static String condensePackageString(String classname)
static <T> T walkParentLoggerNames(String startName, Function<String, T> nameFunction)
{
if (classname == null || classname.isEmpty())
{
return "";
}
int rawLen = classname.length();
StringBuilder dense = new StringBuilder(rawLen);
boolean foundStart = false;
boolean hasPackage = false;
int startIdx = -1;
int endIdx = -1;
for (int i = 0; i < rawLen; i++)
{
char c = classname.charAt(i);
if (!foundStart)
{
foundStart = Character.isJavaIdentifierStart(c);
if (foundStart)
{
if (startIdx >= 0)
{
dense.append(classname.charAt(startIdx));
hasPackage = true;
}
startIdx = i;
}
}
if (foundStart)
{
if (!Character.isJavaIdentifierPart(c))
{
foundStart = false;
}
else
{
endIdx = i;
}
}
}
// append remaining from startIdx
if ((startIdx >= 0) && (endIdx >= startIdx))
{
if (hasPackage)
{
dense.append('.');
}
dense.append(classname, startIdx, endIdx + 1);
}
return dense.toString();
}
public static <T> T walkParentLoggerNames(String startName, Function<String, T> nameFunction)
{
String nameSegment = startName;
if (startName == null)
return null;
// Checking with FQCN first, then each package segment from longest to shortest.
while ((nameSegment != null) && (nameSegment.length() > 0))
String nameSegment = startName;
while (nameSegment.length() > 0)
{
T ret = nameFunction.apply(nameSegment);
if (ret != null)
@ -194,22 +126,18 @@ public class JettyLoggerFactory implements ILoggerFactory, JettyLoggerFactoryMBe
// Trim and try again.
int idx = nameSegment.lastIndexOf('.');
if (idx >= 0)
{
nameSegment = nameSegment.substring(0, idx);
}
else
{
nameSegment = null;
}
break;
}
return null;
}
@Override
public String[] getLoggerNames()
{
return loggerMap.keySet().toArray(new String[0]);
TreeSet<String> names = new TreeSet<>(loggerMap.keySet());
return names.toArray(new String[0]);
}
@Override
@ -221,25 +149,23 @@ public class JettyLoggerFactory implements ILoggerFactory, JettyLoggerFactoryMBe
@Override
public String getLoggerLevel(String loggerName)
{
return walkParentLoggerNames(loggerName, (key) ->
return walkParentLoggerNames(loggerName, key ->
{
JettyLogger logger = loggerMap.get(key);
if (key != null)
{
return LevelUtils.levelToString(logger.getLevel());
}
if (logger != null)
return LevelUtils.intToLevel(logger.getLevel()).toString();
return null;
});
}
@Override
public void setLoggerLevel(String loggerName, String levelName)
public boolean setLoggerLevel(String loggerName, String levelName)
{
Integer levelInt = LevelUtils.getLevelInt(loggerName, levelName);
if (levelInt != null)
{
JettyLogger jettyLogger = getJettyLogger(loggerName);
jettyLogger.setLevel(levelInt);
}
Integer levelInt = LevelUtils.getLevelInt(levelName);
if (levelInt == null)
return false;
JettyLogger jettyLogger = getJettyLogger(loggerName);
jettyLogger.setLevel(levelInt);
return true;
}
}

View File

@ -18,14 +18,13 @@
package org.eclipse.jetty.logging;
@SuppressWarnings("unused")
public interface JettyLoggerFactoryMBean
{
int getLoggerCount();
String[] getLoggerNames();
void setLoggerLevel(String loggerName, String levelName);
boolean setLoggerLevel(String loggerName, String levelName);
String getLoggerLevel(String loggerName);
}

View File

@ -1,50 +0,0 @@
//
// ========================================================================
// 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,8 +45,6 @@ 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

@ -24,67 +24,34 @@ import org.slf4j.event.Level;
public class LevelUtils
{
public static Integer getLevelInt(String loggerName, String levelStr)
public static Integer getLevelInt(String levelStr)
{
if (levelStr == null)
try
{
if (levelStr == null)
return null;
String levelName = levelStr.trim().toUpperCase(Locale.ENGLISH);
if ("ALL".equals(levelName))
return Level.TRACE.toInt();
return Level.valueOf(levelName).toInt();
}
catch (Throwable x)
{
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)
try
{
if (level < JettyLogger.ALL)
return Level.TRACE;
return Level.intToLevel(level);
}
catch (Throwable x)
{
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

@ -0,0 +1,74 @@
//
// ========================================================================
// 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 java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.junit.jupiter.api.Test;
import org.slf4j.event.Level;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class JMXTest
{
@Test
public void testJMX() throws Exception
{
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
Properties props = new Properties();
JettyLoggerConfiguration config = new JettyLoggerConfiguration(props);
JettyLoggerFactory loggerFactory = new JettyLoggerFactory(config);
ObjectName objectName = ObjectName.getInstance("org.eclipse.jetty.logging", "type", JettyLoggerFactory.class.getSimpleName().toLowerCase(Locale.ENGLISH));
mbeanServer.registerMBean(loggerFactory, objectName);
JettyLoggerFactoryMBean mbean = JMX.newMBeanProxy(mbeanServer, objectName, JettyLoggerFactoryMBean.class);
// Only the root logger.
assertEquals(1, mbean.getLoggerCount());
JettyLogger child = loggerFactory.getJettyLogger("org.eclipse.jetty.logging");
JettyLogger parent = loggerFactory.getJettyLogger("org.eclipse.jetty");
assertEquals(3, mbean.getLoggerCount());
// Names are sorted.
List<String> expected = new ArrayList<>(Arrays.asList(JettyLogger.ROOT_LOGGER_NAME, parent.getName(), child.getName()));
expected.sort(String::compareTo);
String[] loggerNames = mbean.getLoggerNames();
assertEquals(expected, Arrays.asList(loggerNames));
// Setting the parent level should propagate to the children.
parent.setLevel(Level.DEBUG);
assertEquals(Level.intToLevel(parent.getLevel()).toString(), mbean.getLoggerLevel(child.getName()));
// Setting the level via JMX affects the logger.
assertTrue(mbean.setLoggerLevel(child.getName(), "INFO"));
assertEquals(Level.INFO.toInt(), child.getLevel());
}
}

View File

@ -526,7 +526,7 @@ public class JettyLoggerTest
assertThat("Logger.toString", log.toString(), is("JettyLogger:xxx:LEVEL=ERROR"));
log.setLevel(JettyLogger.OFF);
assertThat("Logger.toString", log.toString(), is("JettyLogger:xxx:LEVEL=OFF"));
assertThat("Logger.toString", log.toString(), is("JettyLogger:xxx:LEVEL=ERROR"));
}
@Test
@ -556,7 +556,7 @@ public class JettyLoggerTest
assertLevel(log, Level.DEBUG); // as stomped
// Restore configured
factory.walkChildLoggers(root.getName(), (logger) ->
factory.walkChildrenLoggers(root.getName(), (logger) ->
{
int configuredLevel = config.getLevel(logger.getName());
logger.setLevel(configuredLevel);
@ -609,6 +609,6 @@ public class JettyLoggerTest
private void assertLevel(JettyLogger log, Level expectedLevel)
{
assertThat("Log[" + log.getName() + "].level",
LevelUtils.levelToString(log.getLevel()), is(expectedLevel.toString()));
LevelUtils.intToLevel(log.getLevel()), is(expectedLevel));
}
}

View File

@ -1,46 +0,0 @@
//
// ========================================================================
// 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();
}
}
}