Last active
December 21, 2015 05:09
-
-
Save zacscott/6254420 to your computer and use it in GitHub Desktop.
A simple utility which produces java.util.logging.Logger's using a consistent, logical naming scheme using class annotations.
MIT licensed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.zeddev.util; | |
import java.lang.annotation.Annotation; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.util.logging.Level; | |
import java.util.logging.Logger; | |
import static net.zeddev.util.Assertions.*; | |
/** | |
* <p> | |
* A simple utility which produces {@code java.util.logging.Logger}s using a | |
* consistent, logical naming scheme using class annotations. Logger names | |
* are indicated for a class using the {@link LogName} and {@link LogParent} | |
* annotations. For example: | |
* </p> | |
* | |
* <pre> | |
* {@literal @}LogParent(ParentClass.class) // lets assume name is ParentsParent.Parent | |
* {@literal @}LogName("MyLogger") // the MyClass logger will then be | |
* // ParentsParent.Parent.MyLogger | |
* public class MyClass { | |
* | |
* private static final Logger = AnnotatedLogger.getLogger(MyClass.class); | |
* // the logger is just a java.util.logging.Logger and can be used | |
* // exactly as you normally would. | |
* | |
* } | |
* </pre> | |
* | |
* <p> | |
* This provides a uniform way to provide a logger for a given class with a | |
* descriptive name. Names can be provided in a logical way, better than using | |
* the fully qualified class name). Such as the following: | |
* </p> | |
* | |
* <ul> | |
* <li>Layer.TiledLayer.Tile - instead of {@code some.package.Tile}.</li> | |
* <li>Vehicle.Car.SportsCar - instead of | |
* {@code some.package.cars.SportsCar}.</li> | |
* <li>etc.</li> | |
* </ul> | |
* | |
* <p> | |
* <b>NOTE:</b> This file is released as a stand-alone utility. The original | |
* file and the associated unit test can be found on GitHub Gist - | |
* <a href="https://gist.github.com/zscott92/6254420">here</a>. | |
* </p> | |
* | |
* @author Zachary Scott <zscott.dev@gmail.com> | |
*/ | |
public enum AnnotatedLogger { | |
INSTANCE; | |
private final Logger logger = Logger.getLogger("AnnotatedLogger"); | |
/** | |
* <p> | |
* Returns the {@code Logger} for the given class. | |
* Processes logger annotations (i.e. {@link LogName} & | |
* {@link LogParent}) and returns the appropriate | |
* {@code java.util.logging.Logger}. Usually called using | |
* {@link #getLogger(java.lang.Class)} from a static context, like so: | |
* </p> | |
* | |
* <pre> | |
* private static final Logger = AnnotatedLogger.getLogger(MyClass.class); | |
* // returned logger can then be used as any | |
* // {@code java.util.logging.Logger} | |
* </pre> | |
* | |
* @param clazz The class to get the logger for (must not be {@code null}). | |
* @return The logger instance for the given class (will not be | |
* {@code null}). | |
*/ | |
public Logger getLoggerFor(Class clazz) { | |
requireNotNull(clazz); | |
Logger classLogger = Logger.getLogger( | |
classLoggerName(clazz) | |
); | |
ensureNotNull(classLogger); | |
return classLogger; | |
} | |
/** | |
* <p> | |
* Convenience static method for {@link #getLogger(java.lang.Class)}. | |
* </p> | |
* | |
* @param clazz See {@link #getLoggerFor(java.lang.Class)}. | |
* @return See {@link #getLoggerFor(java.lang.Class)}. | |
*/ | |
public static Logger getLogger(Class clazz) { | |
requireNotNull(clazz); | |
return INSTANCE.getLoggerFor(clazz); | |
} | |
// returns the name of the given class | |
private String classLoggerName(Class clazz) { | |
requireNotNull(clazz); | |
StringBuilder resolved = new StringBuilder(); | |
// get the parent logger name | |
String parent = logParent(clazz); | |
if (!parent.equals("")) { | |
resolved.append(parent); | |
resolved.append("."); | |
} | |
resolved.append(logName(clazz)); | |
return resolved.toString(); | |
} | |
// returns the name for the logger for the given class | |
private String logName(Class clazz) { | |
String name = clazz.getSimpleName(); // class name is default | |
Annotation nameAnnotation = clazz.getAnnotation( | |
LogName.class | |
); | |
if (nameAnnotation != null) { | |
Class nameClazz = nameAnnotation.getClass(); | |
try { | |
Method valueMethod = nameClazz.getMethod("value"); | |
name = (String) valueMethod.invoke(nameAnnotation); | |
} catch (SecurityException | | |
IllegalArgumentException | | |
NoSuchMethodException | | |
IllegalAccessException | | |
InvocationTargetException ex) { | |
logger.log(Level.WARNING, | |
"Failed to get value in @LogName for {0}.", | |
new Object[] { clazz.getName() } | |
); | |
} | |
} | |
ensureNotNull(name); | |
return name; | |
} | |
// returns the parent for the logger for the given class | |
private String logParent(Class clazz) { | |
StringBuilder parent = new StringBuilder(); | |
Annotation parentAnnotation = clazz.getAnnotation( | |
LogParent.class | |
); | |
if (parentAnnotation != null) { | |
try { | |
Method valueMethod = | |
parentAnnotation.getClass().getMethod("value"); | |
Class parentClazz = (Class) valueMethod.invoke( | |
parentAnnotation | |
); | |
// recursively add parent class name | |
String parentsParent = classLoggerName(parentClazz); | |
if (!parentsParent.equals("")) | |
parent.append(parentsParent); | |
} catch (NoSuchMethodException | | |
SecurityException | | |
IllegalAccessException | | |
IllegalArgumentException | | |
InvocationTargetException ex) { | |
logger.log(Level.WARNING, | |
"Failed to get value in @LogParent for {0}.", | |
new Object[] { clazz.getName() } | |
); | |
} | |
} | |
return parent.toString(); | |
} | |
/******** ANNOTATION DEFINITIONS ********/ | |
/** | |
* <p> | |
* Indicates the name of the logger for the annotated class. | |
* </p> | |
*/ | |
@Target(ElementType.TYPE) | |
@Retention(RetentionPolicy.RUNTIME) | |
public static @interface LogName { | |
String value(); | |
} | |
/** | |
* <p> | |
* Indicates the parent class of the logger. Should be the logical parent | |
* class of the class which owns the logger. For example: | |
* </p> | |
* | |
* <pre> | |
* {@literal @}LogParent(Car.class) // assume called Vehicle.Car | |
* {@literal @}LogName("SportsCar") // log will then be named Vehicle.Car.SportsCar | |
* </pre> | |
*/ | |
@Target(ElementType.TYPE) | |
@Retention(RetentionPolicy.RUNTIME) | |
public static @interface LogParent { | |
Class value(); | |
} | |
} | |
/* Copyright (C) 2013 Zachary Scott <zscott.dev@gmail.com> | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
* IN THE SOFTWARE. | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.zeddev.util; | |
import java.util.logging.Logger; | |
import net.zeddev.util.AnnotatedLogger.LogName; | |
import net.zeddev.util.AnnotatedLogger.LogParent; | |
import org.junit.Test; | |
import static org.junit.Assert.*; | |
@LogName("parent1") | |
class ParentClass1 { | |
} | |
@LogParent(ParentClass1.class) | |
@LogName("parent2") | |
class ParentClass2 { | |
} | |
/** | |
* <p> | |
* Unit test for {@link AnnotatedLogger}. | |
* </p> | |
* | |
* <p> | |
* <b>NOTE:</b> The {@link AnnotatedLogger} class is released as a stand-alone | |
* utility and the original file can be found on GitHub Gist - | |
* <a href="https://gist.github.com/zscott92/6254420">here</a>. | |
* </p> | |
* | |
* @author Zachary Scott <zscott.dev@gmail.com> | |
*/ | |
@LogParent(ParentClass2.class) | |
@LogName("test") | |
public class AnnotatedLoggerTest { | |
public AnnotatedLoggerTest() { | |
} | |
/** | |
* Test of {@link AnnotatedLogger#getLogger(java.lang.Class)} and | |
* {@link AnnotatedLogger#getLoggerFor(java.lang.Class)} methods. | |
*/ | |
@Test | |
public void testGetLogger() { | |
Logger logger; | |
// test without parent logger | |
logger = AnnotatedLogger.getLogger(ParentClass1.class); | |
assertNotNull(logger); | |
assertEquals(logger.getName(), "parent1"); | |
// test with parent logger | |
logger = AnnotatedLogger.getLogger(AnnotatedLoggerTest.class); | |
assertNotNull(logger); | |
assertEquals(logger.getName(), "parent1.parent2.test"); | |
} | |
} | |
/* Copyright (C) 2013 Zachary Scott <zscott.dev@gmail.com> | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
* IN THE SOFTWARE. | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment