Associating a per-class instance property with a Logger

Hallo -- We have a system where we have what I'll call groups. Each group has a numeric identifier and when a new group is created a corresponding large set of heterogeneous objects are created. These objects are not shared between groups. Whenever any of these objects log anything we always want to see the numeric identifier (of the group to which they belong) in the log message. Ideally I'd create all loggers as instance variables (rather than as static) and at the point of creation somehow associate a group identifier with them. E.g. public class Foo { private final Logger logger = LoggerFactory.getLogger(Foo.class); public Foo(long groupId) { logger.setProperty("groupId", groupId); } } So far the best I can come up with is to create a helper class like so: public static class GroupId { private final String id; public GroupId(long id) { this.id = Long.toString(id); } public Object[] args(Object... o) { MDC.put("group", id); return o; } } I then make one of these a member of each class that also has a logger - and then require people to only use the Logger methods that take an Object array and use them like so: logger.error("expected [0, 1] but received [{}, {}]", groupId.args(x, y)); [ If I do this I don't have to move from static to instance variables for my loggers ] This approach means I have to update MDC every call (I thought of maintaining a lastId ThreadLocal value but it didn't look as if the cost of using this to avoid not calling put on MDC every time would really save anything). And it also means people have to use the GroupId instance even if they have no arguments for a given log message, e.g.: logger.error("limit exceeded", groupId.args()); I'm inclined to think using Markers would be better. I would create a Marker instance in every class where I also had a logger, and initialize it such that its name would return something like "groupId=" + groupId. However comments like the following on stackoverflow suggest this is an inappropriate use of Markers: http://stackoverflow.com/a/4167298/245602 At the moment though I think I'll ignore this and uses Markers unless someone can suggest a better approach? Regards, /George ________________________________ The information in this e-mail is intended only for the person or entity to which it is addressed. It may contain confidential and /or privileged material. If someone other than the intended recipient should receive this e-mail, he / she shall not be entitled to read, disseminate, disclose or duplicate it. If you receive this e-mail unintentionally, please inform us immediately by "reply" and then delete it from your system. Although this information has been compiled with great care, neither IMC Financial Markets & Asset Management nor any of its related entities shall accept any responsibility for any errors, omissions or other inaccuracies in this information or for the consequences thereof, nor shall it be bound in any way by the contents of this e-mail or its attachments. In the event of incomplete or incorrect transmission, please return the e-mail to the sender and permanently delete this message and any attachments. Messages and attachments are scanned for all known viruses. Always scan attachments before opening them.

Hi George, Comments inline. On 19.04.2012 16:15, George C. Hawkins wrote:
Hallo --
We have a system where we have what I'll call groups.
Each group has a numeric identifier and when a new group is created a corresponding large set of heterogeneous objects are created.
These objects are not shared between groups.
Whenever any of these objects log anything we always want to see the numeric identifier (of the group to which they belong) in the log message.
I take the above statement as the specification for what you wish to achieve. [cut]
logger.error("limit exceeded", groupId.args());
I'm inclined to think using Markers would be better. I would create a Marker instance in every class where I also had a logger, and initialize it such that its name would return something like "groupId=" + groupId.
However comments like the following on stackoverflow suggest this is an inappropriate use of Markers:
http://stackoverflow.com/a/4167298/245602
At the moment though I think I'll ignore this and uses Markers unless someone can suggest a better approach?
You could use markers. However, once a marker is created it lives until the app stops. To get around this problem you could create "detached" markers. See the getDetachedMarkers() method in in MarkerFactory. Instead of markers, I would suggest writing a conversion specifier [1], with conversion word %groupID and converter GroupIDConverter. Given that converters have access to all fields of the ILoggingEvent, including the parameters, GroupIDConverter could output the group id in the presence of a parameter of type GroupID. class MyObject { Logger logger = LoggerFactory.getLogger(MyObject.class); public GroupID getGroupId() { return ...; } void foo() { logger.info("Doing foo", getGroupId()); } } The trick here is to pass myObject's groupId as a parameter but have no corresponding anchor point, i.e. there is no '{}' in "Doing foo". You could factor the code so that objects with groupIds log by emitting their groupId as the last parameter (with no corresponding '{}'). HTH, [1] http://logback.qos.ch/manual/layouts.html#customConversionSpecifier
Regards,
/George
-- Ceki http://twitter.com/#!/ceki
participants (2)
-
ceki
-
George C. Hawkins