Extend Java.logging for another log level? - java

I was wondering, if it is possible to extend the standard java logger (java.util.logging.Logger;) for another logger level.
The goal is, that there should show up "ERROR" instead of "SEVERE" in the log files.
Is that possible?
Or do I have to use a different logger instead (e.g. Log4j)?
Thanks a lot!

If you just want to print something different than the standard you could set your own formatter, see http://tutorials.jenkov.com/java-logging/formatters.html
If you want to add an additional log level you can do so by subclassing java.util.logging.Level:
public class MyErrorLevel extends java.util.logging.Level {
public MyErrorLevel() {
super("ERROR", 1000);
}
}

Ok, Andreas Vogler's version works. Create this class:
public class MyErrorLevel extends java.util.logging.Level
{
public MyErrorLevel()
{
super("ERROR", 1000);
}
}
To use it in your running program code, you have to do it that way:
logger.log(new MyErrorLevel(),"My Error number one");
If you need more than one errorLevel you can do it that way:
public class MyErrorLevel extends Level
{
public static MyErrorLevel ERROR = new MyErrorLevel ("ERROR", 950);
public static MyErrorLevel SERIOUS_ERROR = new MyErrorLevel("SERIOUS_ERROR", 980);
//...and so on...
private MyErrorLevel(String name, int value)
{
super (name, value);
}
}
In your program code, you can use it like this:
logger.log(MyErrorLevel.ERROR, "my other error");
logger.log(MyErrorLevel.SERIOUS_ERROR, "my significant Error");
Now, if you don't want to specify your own classname (MyErrorLevel.SERIOUS_ERROR) everytime and instead you want to use 'standard-methods' (e. g. like the already existing method logger.info("my information")) you may think about extending the logger itself with new methods. This should (as far as my understanding goes) basically work like that:
public class MyLogger extends Logger
{
public MyLogger(String name, String resourceBundleName)
{
super(name, resourceBundleName);
}
public void error(String msg)
{
super.log(MyErrorLevel.ERROR, msg);
}
public void error(String msg)
{
super.log(MyErrorLevel.SERIOUS_ERROR, msg);
}
}
Now you should be able to call these methods in your code like that:
myLogger.error("my error")
myLogger.seriousError("my serious error")
But I wasnt able to do it:
I couldn't initialize my own logger with:
MyLogger myLogger = MyLogger.getLogger("MyModifiedLogger");
This doesn't compile because of type mismatch (Cannont convert from logger to MyLogger).
I also tried:
MyLogger myLogger = (MyLogger)Logger.getLogger("MyModifiedLogger");
This results in an error message while running:
java.util.logging.Logger cannot be cast to utility.MyLogger
So somehow my extension failed. Any ideas what I am missing?

Related

Modify functionality of Parent class without rewrite in java

Let's say I have an abstract class, called Logger:
public abstract class AbstractLogger {
public enum Levels {
DEBUG, INFO, WARNING, ERROR
}
public void debug(String message) {
Levels level = Levels.DEBUG;
log(level, message);
}
public void info(String message) {
Levels level = Levels.INFO;
log(level, message);
}
public void warning(String message) {
Levels level = Levels.WARNING;
log(level, message); }
public void error(String message) {
Levels level = Levels.ERROR;
log(level, message); }
public void log(Levels level, String message) {}
}
And I also have classes that inherit this class, such as FileAppenderLogger:
public class FileAppenderLogger extends AbstractLogger {
private final Path logPath;
public FileAppender(Path logPath) {
this.logPath = logPath;
createLogFile();
}
private void createLogFile() {
try {
File logFile = new File(logPath.toString());
if (logFile.createNewFile()) {
System.out.println("File created: " + logFile.getName());
} else {
System.out.println("File already exists.");
}
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
}
#Override
public void log(Levels level, String message) {
try {
FileWriter myWriter = new FileWriter(this.logPath.toString());
myWriter.write(message+"\n");
myWriter.close();
System.out.println("Successfully wrote to the file.");
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
}
#Override
public void debug(String message) {
super.info(message);
}
#Override
public void info(String message) {
super.info(message);
}
#Override
public void warning(String message) {
super.warning(message);
}
#Override
public void error(String message) {
super.error(message);
}
}
Now, let's say I need to extend Logger to support new Log level, such as "FATAL", and also extend its children, such as FileAppenderLogger to support it, without modify any of those classes, only extend them.
what could be the best practice for that (if I still want to preserve non generic methods such as ".info(String s)" or ".debug(String s))?
What design pattern may I use here?
I'm open for changes regard this problem.
Thank you!
Simply add it to AbstractLogger:
public abstract class AbstractLogger {
public enum Levels {
DEBUG, INFO, WARNING, ERROR, /* added */ FATAL,
}
public void fatal(String message) {
log(Levels.FATAL, message);
}
}
Given that the types that extend AbstractLogger all already implement the log method, then 'things will just work' - possibly some of the implementations cannot deal with the fact that a new log level has now appeared. Assuming they were appropriately programmed, they'll throw. Your FileAppenderLogger class, for example, would just continue to work without requiring any change or even recompilation.
The key design pattern to make this work is that all those non-generic methods such as .error(x) are light wrappers that all send the data to a single method that does the real work - log. But, you already do that.
NB: Reinventing the wheel is a bad idea. Logging frameworks already exist, use an existing one instead.
NB2: Idiomatic java dictates you call your enum types the singular - it should be Level, not Levels. The type name describes, well, a type name. It's called String, not Strings, because an instance of java.lang.String represents one string. The class itself represents all strings, but that doesn't mean it should be called Strings. Similarly, an instance of the Levels enum represents a single level. Hence, it should be named Level, not Levels.
Instead of using enum for level, you can make class LogLevel and make classes that extend it, for example LogLevelError, LogLevelFatal, then in log method: this.logLevel.log(message);. Of course, it look strange, but this is the way I see to add new log levels. Also, as said by #rzwitserloot :"NB: Reinventing the wheel is a bad idea. Logging frameworks already exist, use an existing one instead". They are much faster, optimized, and 'time-tested'.
You can't add more values to your enum, that's not possible in java. I would suggest to either use a String for levels, or declare your own Level class, so you can add more levels.
public class Level {
private final String levelName;
//getter, etc.
}
To extend the functionality of your AbstractLogger, without modifying it, you can wrap it in another class and declare the additional methods, fatal() in this case.
public class ExtendedLogger extends AbstractLogger {
private final AbstractLogger abstractLogger;
public ExtendedLogger(AbstractLogger abstractLogger) {
this.abstractLogger = abstractLogger;
}
#Override
public void debug(String message) {
abstractLogger.debug(message);
}
//info, warning and rest of methods
#Override
public void log(Levels level, String message) {
abstractLogger.log(level, message);
}
public void fatal(String message) {
//implement
}
}
First: logger libraries are numerous, and the first reform was the introduction of java.util.Logger to unify things a bit. Still not the dead of the other logging libraries.
Then came - especially for libraries - the underestimated System.Logger: a Logger façade that can be discover logging implementations. This allows publishing a library, use Logging, but leave the actual logging library choice to the library user.
So in that context meddling in class hierarchies and enum constants is counter-productive to say the least.
What you can do is a specific configuration, say for some packages, implement a specific file handler (FileHandler, Handler), and reserve ERROR for your own FATALISH when using *Exception classes or such.
Though seemingly simple, using FileWriter in the Logger child was not intended to be done as such. You should leave it to configuring the usage to your own Handler class.
Unfortunately the solution does not exist. You'll better write a prototype to test your specific configuration.

Get the same instance of a service provider class that provides more than one service with ServiceLoader

I have a problem and I don't know which way to got about it
Say I have two Service Provider Interfaces (SPI)
public interface View{
display();
}
public interface Logger{
log(String s);
}
And a Service provider that provides both services i.e
public class LogView implements View, Logger{
...
}
The problem is that, when I try to get an instance of the log service via ServiceLoader.load(Logger.class) it's different from the instance created with ServiceLoader.load(View.class). Is there a way to go about it such that I can get the same object instance from both calls?
The idea is that after loading the view as a GUI, I want to be able to log on that same instance of the GUI and not another. As it stand now I'm stuck with two separate instance so the log does show up.
A solution has been added together with Java modules, but the caveat is that it only works with explicit modules, i.e. with service declarations in a module-info, rather than a file in META-INF/services.
Then, you can declare a public static T provider() method, to be used instead of a public default constructor, whereas the return type T must be assignable to the service type. When such a method has been declared, the declaring type doesn’t need to be an implementation of T itself, but it’s not an error if it is.
So given the class declarations
package somemodule;
public interface Logger{
void log(String s);
}
package somemodule;
public interface View{
void display();
}
package somemodule;
public class LogView implements View, Logger {
static final LogView INSTANCE = new LogView();
private LogView() {}
public static LogView provider() {
return INSTANCE;
}
#Override
public void display() {
System.out.println("display");
}
#Override
public void log(String s) {
System.out.println("log "+s);
}
}
and a module declaration like
module SomeModule {
uses somemodule.Logger;
uses somemodule.View;
provides somemodule.Logger with somemodule.LogView;
provides somemodule.View with somemodule.LogView;
}
The following code prints true:
View v = ServiceLoader.load(View.class).findFirst().orElseThrow();
Logger l = ServiceLoader.load(Logger.class).findFirst().orElseThrow();
System.out.println(v == l);

Shadowing variable used in a default method of an interface in Java 8

Today I was thinking about a nice way to write less code for a common functionality that is required for different objects.
Inheritance can do the job but then the classes won't be able to inherit from anyone else, so I chose Interfaces.
So I have my interface with the functionality I will need for some objects:
public interface Test {
String message = "Hello from Interface!";
default void printMessage() {
System.out.println(message);
}
}
And then I can use it in any object without having to override/write any code more than just simply calling the method when needed:
public class TestingTest implements Test {
public String message = "Hello from Class!";
public TestingTest() {
printMessage();
}
public static void main(String[] args) {
new TestingTest();
}
}
It works like a charm! But... Then I thought, what if I want some of those objects to specify a different message without being required (optional), well first thing I thought was to shadow the interface variable, but it doesn't work, the default method keeps using the variable from the interface instead of the class variable (which shadowed it).
A solution of course would be to overload the printMessage method in the interface so it recieves the message as a parameter for when the user requires to specify the message, but is there any more elegant way? Something like simply just declaring a new message in the class?
The String message in the interface is static (AFAIK). So that scheme does not work.
You might do something (ugly) as:
default void printMessage(String... messages) {
if (messages.length == 0) {
messages = new String[] { "arrgg" };
}
System.out.println(messages[0]);
}
Fields have no inheritance, so the value can only stem from an overridable method like
public String message() { return "..."; }
What you want is a functionality in n classes that should also be modifiable, if needed.
To be honest, your example is a little bit abstract and thus my answer will be abstract, too.
public interface Test {
void printMessage();
default void printMessage(String message) {
System.out.println(message);
}
}
public class TestingTest {
private final test;
public TestingTest(Test test) {
this.test = test;
}
public void someMethod() {
test.printMessage("Hello from class");
}
}
Additionally, you would have a class that implements the interface and offers the message. This way you could group your objects, change the message, make more complex logging and you would actually see the dependency from outside.
In my opinion, you are misusing the interface. An interface offers public methods to call it from outside, but you want to use them inside like they were private functionalities for the class.
Just use objects instead.

java pircbot cannot over ride final method from pircbot

Hello I have a question about pircbot. I'm trying to have a send message method but i have an error that i am confused with.
here is my code
import org.jibble.pircbot.PircBot;
public class sendMessage extends PircBot {
public sendMessage() {
this.setName("user");
}
public static void main(String[] args) throws Exception {
sendMessage bot = new sendMessage();
bot.setVerbose(true);
bot.connect("irc.twitch.tv", 6667, "oauth:code");
bot.joinChannel("#channel");
public void sendMessage(String target, String message) {
sendMessage(target, "hello");
}
}
it says that cannot over ride final method from pircbot. I dont understand whats wrong. can someone help me understand why im getting this?
A method declared final means that it cannot be overridden. Thus no matter how much you extend the PircBot, you can't change that method which has been provided. See this tutorial for more details on final.
Instead of trying to extend the class, consider using composition instead.
Also it looks like you just want to use the PircBot, so why not just use it as provided?
PircBot bot = new PircBot();
bot.setName("user");
bot.setVerbose(true);
bot.connect("irc.twitch.tv", 6667, "oauth:code");
bot.joinChannel("#channel");
bot.sendMessage(target, message);
sendMessage() in class PircBot is indeed final.
It has the following signature:
public final void sendMessage(String target, String message)
You did not need to override it at all. You can use it in your code.
bot.sendMessage(target, "hello");
You still need to provide a target String which is defined as follows
The name of the channel or user nick to send to.
This method "sendMessage": is trying to Override one of the PircBot Super Class, and is final. You cannot Override a final method. Have a look here Class PircBot Doc.
Also, you should consider changing the name of you class "sendMessage" for another one, to don't make confusion with the constructor and the method (equals to the Super class).
Also, the name of your class should be in a pattern of good practice, with the first letter in uppercase.
Here a example:
public class MyBot extends PircBot {// changed here
public MyBot() { // changed here
this.setName("user");
}
public static void main(String[] args) throws Exception {
MyBot bot = new MyBot(); // changed here
bot.setVerbose(true);
bot.connect("irc.twitch.tv", 6667, "oauth:code");
bot.joinChannel("#channel");
//changed here
public void sendMyMessage(String target) {
sendMessage(target, "hello");
}
}

SLF4J Log Level as an argument [duplicate]

This question already has answers here:
Setting log level of message at runtime in slf4j
(18 answers)
Closed 7 years ago.
We are looking to use SLF4J, but one thing we found was that you can't specify the level as an argument, i.e
Logger.log(Level.INFO, "messsage");
You have to do this
logger.info("message");
this prevents being able to pass everything through a method, so you can tack other properties to all log messages in a class.
public class Test
{
public Test(SomeObj obj)
{
log(Level.INFO, "message");
}
public void anotherMethod()
{
log(Level.DEBUG, "another message");
}
private void log(Level level, String message)
{
logger.log(level, message + obj.someString());
}
}
Is there a way to achieve this using SLF4j ?
Write a wrapper around the slf4j call and create your own enum for the six log levels. Then in your wrapper, use a switch to call the correct slf4j call.
void myLog(Level level, String message)
{
switch (level)
{
case FATAL:
log.fatal(message);
break;
case ERROR:
log.error(message);
break;
....
}
}
The answer is No. Refer to this discussion.
Your usecase screams for the delegation pattern. Basically you wedge your own implementation of Logger between your code and SLF4J and "extend" the relevant methods:
class MyLogger implements Logger {
Logger realLogger;
Object userData;
MyLogger(Class clazz, Object userData){
this.realLogger = LoggerFactory.getLogger(clazz);
}
public void debug(String msg) {
realLogger.debug(msg + userData.someString());
}
// many more methods, perhaps per java.lang.reflect.Proxy
}
This is use in the business code like this:
public class Test
{
Logger log;
public Test(SomeObj obj)
{
log = new MyLogger(Test.class, obj);
log.logInfo("message");
}
public void anotherMethod()
{
logDebug("another message");
}
}
For optimal reusability of the MyLogger class SomeObj should either use Object.toString() or it should implement an interface which MyLogger can use to get the message addendum.
Well, technically SLF4J doesn't offer you a logger.log(Level, message) method. But I found a way around that. [edit: uses introspection]
Using the below code snippet you can get the native logger that slf4j found and wrapped for you at runtime. If you'll recall, slf4j is simply a wrapper around an slf4j implementation from another provider (either, jdkLogging, Log4J, JCL, etc...). So here:
public Object getNativeLogger( org.slf4j.Logger logger ) {
Object result = null;
if ( logger.getClass().getName().equals("org.slf4j.impl.Log4jLoggerAdapter")) {
try {
Field f = Log4jLoggerAdapter.class.getDeclaredField("logger");
f.setAccessible( true );
result = (org.apache.log4j.Logger)f.get(logger);
}
catch( Exception e ) {
System.out.println("Unable to access native log4j logger");
}
}
else if ( logger.getClass().getName().equals("org.slf4j.impl.JDK14LoggerAdapter")) {
try {
Field f = Jdk14Logger.class.getDeclaredField("logger");
f.setAccessible( true );
result = (Jdk14Logger)f.get(logger);
}
catch( Exception e ) {
System.out.println("Unable to access native log4j logger");
}
}
else if (..... other native loggers slf4j supports)....
}
return result;
}
Then you can use it like this:
Object l = getNativeLogger(mySlf4jLogger);
if ( l instanceof org.apache.log4j.Logger ) {
org.apache.log4j.Logger logger = (org.apache.log4j.Logger) l;
logger.log( CUSTOMLog4JLevel, message);
}
else if( .... other implementations that you care about ...)...
So while it's not technically within slf4j, it is possible to do it using slf4j as your primary logging interface.

Categories

Resources