-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue #5872 - JMX DynamicMBean for jetty-slf4j-impl #5876
Changes from all commits
37e7361
87ab8bd
5f9ea78
81c2c23
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,15 +19,28 @@ | |
import java.util.concurrent.ConcurrentMap; | ||
import java.util.function.Consumer; | ||
import java.util.function.Function; | ||
import javax.management.Attribute; | ||
import javax.management.AttributeList; | ||
import javax.management.AttributeNotFoundException; | ||
import javax.management.DynamicMBean; | ||
import javax.management.MBeanAttributeInfo; | ||
import javax.management.MBeanConstructorInfo; | ||
import javax.management.MBeanException; | ||
import javax.management.MBeanInfo; | ||
import javax.management.MBeanNotificationInfo; | ||
import javax.management.MBeanOperationInfo; | ||
import javax.management.MBeanParameterInfo; | ||
import javax.management.ReflectionException; | ||
|
||
import org.slf4j.ILoggerFactory; | ||
import org.slf4j.Logger; | ||
|
||
public class JettyLoggerFactory implements ILoggerFactory, JettyLoggerFactoryMBean | ||
public class JettyLoggerFactory implements ILoggerFactory, DynamicMBean | ||
{ | ||
private final JettyLoggerConfiguration configuration; | ||
private final JettyLogger rootLogger; | ||
private final ConcurrentMap<String, JettyLogger> loggerMap; | ||
private MBeanInfo mBeanInfo; | ||
|
||
public JettyLoggerFactory(JettyLoggerConfiguration config) | ||
{ | ||
|
@@ -129,20 +142,17 @@ static <T> T walkParentLoggerNames(String startName, Function<String, T> nameFun | |
return nameFunction.apply(Logger.ROOT_LOGGER_NAME); | ||
} | ||
|
||
@Override | ||
public String[] getLoggerNames() | ||
{ | ||
TreeSet<String> names = new TreeSet<>(loggerMap.keySet()); | ||
return names.toArray(new String[0]); | ||
} | ||
|
||
@Override | ||
public int getLoggerCount() | ||
{ | ||
return loggerMap.size(); | ||
} | ||
|
||
@Override | ||
public String getLoggerLevel(String loggerName) | ||
{ | ||
return walkParentLoggerNames(loggerName, key -> | ||
|
@@ -154,7 +164,6 @@ public String getLoggerLevel(String loggerName) | |
}); | ||
} | ||
|
||
@Override | ||
public boolean setLoggerLevel(String loggerName, String levelName) | ||
{ | ||
JettyLevel level = JettyLoggerConfiguration.toJettyLevel(loggerName, levelName); | ||
|
@@ -166,4 +175,161 @@ public boolean setLoggerLevel(String loggerName, String levelName) | |
jettyLogger.setLevel(level); | ||
return true; | ||
} | ||
|
||
@Override | ||
public Object getAttribute(String name) throws AttributeNotFoundException | ||
{ | ||
Objects.requireNonNull(name, "Attribute Name"); | ||
|
||
switch (name) | ||
{ | ||
case "LoggerNames": | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm afraid There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name here and the name in MBeanInfo must match, case included. Example: MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = ObjectName.getInstance("java.lang", "type", "OperatingSystem");
System.out.printf("OSVer (normal): %s%n", mbeanServer.getAttribute(objectName, "Version"));
System.out.printf("OSVer (lowercase): %s%n", mbeanServer.getAttribute(objectName, "version")); results in ...
|
||
return getLoggerNames(); | ||
case "LoggerCount": | ||
return getLoggerCount(); | ||
default: | ||
throw new AttributeNotFoundException("Cannot find " + name + " attribute in " + this.getClass().getName()); | ||
} | ||
} | ||
|
||
@Override | ||
public void setAttribute(Attribute attribute) throws AttributeNotFoundException | ||
{ | ||
Objects.requireNonNull(attribute, "attribute"); | ||
String name = attribute.getName(); | ||
// No attributes are writable | ||
throw new AttributeNotFoundException("Cannot set attribute " + name + " because it is read-only"); | ||
} | ||
|
||
@Override | ||
public AttributeList getAttributes(String[] attributeNames) | ||
{ | ||
Objects.requireNonNull(attributeNames, "attributeNames[]"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd tolerate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is invalid per spec. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Where do you see this? It's not in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Which is nice and all, but pointless. MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = ObjectName.getInstance("java.lang", "type", "OperatingSystem");
AttributeList attributeList = mbeanServer.getAttributes(objectName, null);
for (Attribute attr : attributeList.asList())
{
System.out.printf("Attribute[%s] = %s%n", attr.getName(), attr.getValue());
} results in ...
|
||
|
||
AttributeList ret = new AttributeList(); | ||
if (attributeNames.length == 0) | ||
return ret; | ||
|
||
for (String name : attributeNames) | ||
{ | ||
try | ||
{ | ||
Object value = getAttribute(name); | ||
ret.add(new Attribute(name, value)); | ||
} | ||
catch (Exception e) | ||
{ | ||
// nothing much we can do, this method has no throwables, and we cannot use logging here. | ||
e.printStackTrace(); | ||
} | ||
} | ||
return ret; | ||
} | ||
|
||
@Override | ||
public AttributeList setAttributes(AttributeList attributes) | ||
{ | ||
Objects.requireNonNull(attributes, "attributes"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd tolerate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is supposed to error out, as null is invalid. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
AttributeList ret = new AttributeList(); | ||
|
||
if (attributes.isEmpty()) | ||
return ret; | ||
|
||
for (Attribute attr : attributes.asList()) | ||
{ | ||
try | ||
{ | ||
setAttribute(attr); | ||
String name = attr.getName(); | ||
Object value = getAttribute(name); | ||
ret.add(new Attribute(name, value)); | ||
} | ||
catch (Exception e) | ||
{ | ||
// nothing much we can do, this method has no throwables, and we cannot use logging here. | ||
e.printStackTrace(); | ||
} | ||
} | ||
return ret; | ||
} | ||
|
||
@Override | ||
public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException | ||
{ | ||
Objects.requireNonNull(actionName, "Action Name"); | ||
|
||
switch (actionName) | ||
{ | ||
case "setLoggerLevel": | ||
{ | ||
String loggerName = (String)params[0]; | ||
String level = (String)params[1]; | ||
return setLoggerLevel(loggerName, level); | ||
} | ||
case "getLoggerLevel": | ||
{ | ||
String loggerName = (String)params[0]; | ||
return getLoggerLevel(loggerName); | ||
} | ||
default: | ||
throw new ReflectionException( | ||
new NoSuchMethodException(actionName), | ||
"Cannot find the operation " + actionName + " in " + this.getClass().getName()); | ||
} | ||
} | ||
|
||
@Override | ||
public MBeanInfo getMBeanInfo() | ||
{ | ||
if (mBeanInfo == null) | ||
{ | ||
MBeanAttributeInfo[] attrs = new MBeanAttributeInfo[2]; | ||
|
||
attrs[0] = new MBeanAttributeInfo( | ||
"LoggerCount", | ||
"java.lang.Integer", | ||
"Count of Registered Loggers by Name.", | ||
true, | ||
false, | ||
false); | ||
attrs[1] = new MBeanAttributeInfo( | ||
"LoggerNames", | ||
"java.lang.String[]", | ||
"List of Registered Loggers by Name.", | ||
true, | ||
false, | ||
false); | ||
|
||
MBeanOperationInfo[] operations = new MBeanOperationInfo[]{ | ||
new MBeanOperationInfo( | ||
"setLoggerLevel", | ||
"Set the logging level at the named logger", | ||
new MBeanParameterInfo[]{ | ||
new MBeanParameterInfo("loggerName", "java.lang.String", "The name of the logger"), | ||
new MBeanParameterInfo("level", "java.lang.String", "The name of the level [DEBUG, INFO, WARN, ERROR]") | ||
}, | ||
"boolean", | ||
MBeanOperationInfo.ACTION | ||
), | ||
new MBeanOperationInfo( | ||
"getLoggerLevel", | ||
"Get the logging level at the named logger", | ||
new MBeanParameterInfo[]{ | ||
new MBeanParameterInfo("loggerName", "java.lang.String", "The name of the logger") | ||
}, | ||
"java.lang.String", | ||
MBeanOperationInfo.INFO | ||
) | ||
}; | ||
|
||
mBeanInfo = new MBeanInfo(this.getClass().getName(), | ||
"Jetty Slf4J Logger Factory", | ||
attrs, | ||
new MBeanConstructorInfo[0], | ||
operations, | ||
new MBeanNotificationInfo[0]); | ||
} | ||
return mBeanInfo; | ||
} | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would make this
final
and build it in the constructor (calling a method).This would solve the problem of
getMBeanInfo()
possibly creating it twice when accessed concurrently.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we create this MBeanInfo always? even for those users that don't actually use JMX?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point!