My application manages devices in a network. Those are represented in my application by objects and compositions of objects.
Each of these devices has an ID and each object related to a device knows about its ID.
When configuring logging, in addition to set the log level per the package hierarchy, I would like to be able to set it per device - that is, all instances related to one device should be eg. put in DEBUG level.
How can I set the log level by the package hierarchy and also by an instance ID?
My thoughts so far:
Currently my loggers are created the "standard" way, by the class type
public class Thermometer extends AnalogDevice {
private static final Logger logger = LoggerFactory.getLogger(Thermometer.class);
...
}
But I want to be able to choose the log level for specific devices. My current idea would be to use the ID in the logger name, like this:
public class Thermometer extends AnalogDevice {
private final Logger logger;
public Thermometer(String deviceId){
logger = LoggerFactory.getLogger(deviceId+"."+Thermometer.class);
...
}
...
}
(and the same for other classes in the hierarchy and other classes bound to this device) This would allow to configure log4j to have all messages for device "mydevice123" on DEBUG level.
log4j.logger.mydevice123=DEBUG
But this will potentially create many loggers (per device / per class):
mydevice123.com.example.dev.Thermometer.class
mydevice123.com.example.dev.AnalogDevice.class
mydevice123.com.example.dev.SomeOtherDeviceSpecific.class
...
Also I now lost the possibility to set the level on a package hierarchy. This won't work anymore.
log4j.logger.com.example.dev=DEBUG
What's a better way?
Use logback as your logging implementation.
Set your device id in the MDC (Mapped Diagnostic Context) (*)
Set up a DynamicThresholdFilter
(*) IMHO a good approach to set the device id in the MDC is using an aspect applied on every business method which will get the device id from the targeted object (exemple with spring aspects), set the id in context before the method invocation and remove it after
Related
How can we add description on the fields and operations exposed for JMX?
JBoss version : JBoss EAP 5.1.2
We have a Service bean as
#Service
#Management(MyConfigMgnt.class)
public class MyConfigService implements MyConfigLocal, MyConfigMgnt {
public void setMyValue(String MyValue){}
public String getMyValue(){}
}
These methods are declared in the MyConfigMgnt interface.
This is visible in the jboss jmx console as
and for the field it is shown as
How do we add relevant and proper information on the fields and the MBean.
Thanks
There's 2 ways of doing this.
Re-implement your service as a DynamicMBean which is slightly more complicated but allows for the definition of attribute and operation meta-data. (i.e. MyConfigMgnt extends DynamicMBean)
An easier way (but possibly not future-proof) is to use an XMBean descriptor. XMBeans are a proprietary JBoss JMX extension where meta-data is defined in an external XML resource. It would require no actual changes to the source code except the addition of the XMBean resource location which looks something like this:
#Service(objectName = XMBeanService.OBJECT_NAME, xmbean = "resource:META-INF/service-xmbean.xml")
If you have a very large number of attributes and operations, the XMBean XML descriptor can be arduous to write, but twiddle has a helper command which will generate a template specific to your existing simple MBean, so you can save the output, fill in the details and go from there.
I have a Java web application which use Spring and Hibernate and I plan to use lightadmin to provide an administration interface.
However, I found very little information about the logging part of lightadmin : if I have such an adminsitration interface, I would like that any operation made to our data (such as create, update or delete) is logged in our custom logger (it's not on a file but on a table on the database, this choice has already been made and implemented long time ago).
My need is to have a log entry containing some information (might be just the id) about the modified rows. Is there a global way to configure it?
Or can I add a logging annotation somewhere in each class which extends AdministrationConfiguration? If yes, where?
You can use the class AbstractRepositoryEventListener like it's show on the LightAdmin documentation here
Add you logger insertion by overiding onAfterSave, onAfterCreate and onAfterDelete into your own RepositoryEventListener.
After you just need to register your listener like this
public class YourAdministration extends AdministrationConfiguration<YourObject> {
public EntityMetadataConfigurationUnit configuration(EntityMetadataConfigurationUnitBuilder configurationBuilder) {
return configurationBuilder
.repositoryEventListener(YourRepositoryEventListener.class)
.build();
}
}
I would like to add a unique identifier to log statements, so I am able to add documentation (externally, e.g. a wiki) to every log statement, so a user can quickly access the message related documentation using the id. The logging framework I would like to use is SLF4J/logback.
I was not able to find documentation about related approaches except for some bits regarding auditing frameworks.
There is the Marker concept which I thought could be usable for ID injection, or I could just add the ID to the message text itself.
How would I add IDs to the logging statements "the right way"? Are there possibilities I didn't think of?
EDIT
The term unique ID just states there should be an identifier per log statement. A developer e.g. adds such an ID to a table/enum/whatever manually, which could be done wrong.
Such ID has to be stable, so documentation can be based on it. So the ID itself is not what I am wondering about.
My question is: what would be the right way of pushing the ID to the logger together with the message text? Would Markers be suited for this kind of requirement, should I embed the ID into the message text or is there some other possibility?
So, basically, would I use
logger.info(IDMarkers.DB_CONNECTION_FAILED, "no connection to the database");
or instead just
logger.info("[{}] no connection to the database", LogIDs.DB_CONNECTION_FAILED);
First approach has the advantage that showing the IDs is up to the logging system/its configuration.
Slf4j has http://www.slf4j.org/apidocs/org/slf4j/Marker.html
Unfortunately Markers are advertised for a different purpose. Still you can use them to uniquely mark logging statements.
More cumbersome solution is MDC:
MDC.put("MsgId", "EV-1234");
log.info()
MDC.remove("MsgId");
or with structural logging (requires v2.0.0):
logger.atDebug()
.addKeyValue("MsgId", "EV-1234")
.log("Temperature changed.");
Unique is only unique within some scope. Eventually, even every int or long value will be used.
So think about what "uniqueness" means to you. Then use a wrapper that will ensure your logging is handled with that id inserted.
Note that with slf4j you are dealing with an interface which will make a number of logging APIs consistent. This means you probably won't have the option to sub-class or even inject your implementation of the interface to ensure your consistent logging. Therefore you will be constrained to techniques like wrapping your logging API (preferably through the "consistent" interface).
package mypackage.log;
public class LoggerWrapper implements org.log4j.Logger {
private org.log4j.Logger logger;
public LoggerWrapper(org.log4j.Logger logger) {
this.logger = logger;
}
public String getUniqueId() {
return ...;
}
public void info(String message, Object params...) {
logger.info(String.format("[%d] %s", getUniqueId(), message), params));
}
... implement all the other methods ...
}
And this means that you will have to make your own LoggerFactory interface too
public Logger getLogger(String name) {
return new LoggerWrapper(org.sql4j.LoggerFactory(name));
}
While the code above has a few warts (not actually testing it); hopefully, it will give you an idea.
I've seen code that uses log4j, which acquires logger for a given Logger using
static public Logger getLogger(String name)
and
static public Logger getLogger(Class clazz)
with the former api passed explicitly with getSimpleName(), while the latter uses getName() on the passed Class. Is there a difference between these two? Would it affect if I configure various packages to log at different level in log4j.properties file?
Yes there is a huge difference.
I never use simpleName for Logger instance as it strips down the package name.
Apart from having problems when same class name exists in two different packages (leading to both classes getting the same logger instance), you lose the ability to control logger inheritance.
e.g. for two loggers:
com.foo.A
com.foo.B
in properties, i can just have:
log4j.logger.com.foo=DEBUG,CONSOLE
E.g. My class ShapeDemo.java resides in com.test package, and I have written code like below.
System.out.println("Name-->"+ShapeDemo.class.getName());
System.out.println("SimpleName-->"+ShapeDemo.class.getSimpleName());
This will output following
Name-->com.test.ShapeDemo
SimpleName-->ShapeDemo
Using of this.getClass().getName();
Returns : alin.iwin.flickrbrowser.GetRawData
Meanwhile
private String LOG_TAG = this.getClass().getSimpleName();
Return only : GetRawData.
I prefer using the full name (Class.getName()). When packages are organized correctly, this allows tuning log4j to handle differently log messages originating from different parts of the java packages tree.
For example, you can easily configure all classes in packages starting with "com.mycompany.infra" to use a specific appender, and log only messages of level WARN or above.
You might get confused if you have many classes with the same simpleName in different packages. Having many loggers with the same name should not be a problem otherwise - just could be confusing.
I'm configuring the logging for a Java application. What I'm aiming for is two logs: one for all messages and one for just messages above a certain level.
The app uses the java.util.logging.* classes: I'm using it as is, so I'm limited to configuration through a logging.properties file.
I don't see a way to configure two FileHandlers differently: the docs and examples I've seen set properties like:
java.util.logging.FileHandler.level = INFO
While I want two different Handlers logging at different levels to different files.
Any suggestions?
http://java.sun.com/j2se/1.4.2/docs/guide/util/logging/overview.html is helpful. You can only set one Level for any individual logger (as you can tell from the setLevel() method on the logger). However, you can take the lowest of the two common levels, and then filter programmatically.
Unfortunately, you can't do this just with the configuration file. To switch with just the configuration file you would have to switch to something like log4j, which you've said isn't an option.
So I would suggest altering the logging in code, with Filters, with something like this:
class LevelFilter implements Filter {
private Level Level;
public LevelFilter(Level level) {
this.level = level;
}
public boolean isLoggable(LogRecord record) {
return level.intValue() < record.getLevel().intValue();
}
}
And then on the second handler, do setFilter(new LevelFilter(Level.INFO)) or whatever. If you want it file configurable you could use a logging properties setting you've made up yourself, and use the normal Properties methods.
I think the configuration code for setting up the two file handlers ad the programmatic code is fairly simple once you have the design, but if you want more detail add a comment and I'll edit.
I think you should be able to just subclass a handler and then override the methods to allow output to go to multiple files depending on the level of the message. This would be done by overriding the publish() method.
Alternatively, if you have to use the system-provided FileHandler, you could do a setFilter() on it to inject your own filter into the mix and, in that filter code, send ALL messages to your other file and return true if the LogRecord level if INFO or higher, causing the FileHandler.publish() to write it to the real file.
I'm not sure this is the way you should be using filters but I can't see why it won't work.