Skip to content

Instantly share code, notes, and snippets.

@zacscott
Last active December 21, 2015 05:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zacscott/6254420 to your computer and use it in GitHub Desktop.
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.
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} &amp
* {@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.
*/
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