/**
* Abiquo community edition
* cloud management application for hybrid clouds
* Copyright (C) 2008-2010 - Abiquo Holdings S.L.
*
* This application is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC
* LICENSE as published by the Free Software Foundation under
* version 3 of the License
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* LESSER GENERAL PUBLIC LICENSE v.3 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package com.abiquo.api.tracer;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.stereotype.Service;
import com.abiquo.api.tracer.hierarchy.HierarchyProcessor;
import com.abiquo.commons.amqp.impl.tracer.TracerProducer;
import com.abiquo.commons.amqp.impl.tracer.domain.Trace;
import com.abiquo.tracer.ComponentType;
import com.abiquo.tracer.Enterprise;
import com.abiquo.tracer.EventType;
import com.abiquo.tracer.SeverityType;
import com.abiquo.tracer.User;
/**
* Logger to the Abiquo tracing system.
*
* @author ibarrera
*/
@Service
public class TracerLogger
{
/** The log system logger. */
private static final Logger LOGGER = LoggerFactory.getLogger(TracerLogger.class);
/** The processor factory used to get the hierarchy processors. */
@Resource(name = "hierarchyProcessorFactory")
private HierarchyProcessor hierarchyProcessor;
/** The resource bundle with the application messages. */
@Autowired
private MessageSource messages;
/** The RabbitMQ producer */
private TracerProducer producer = new TracerProducer();
/**
* Log the message to the event system.
*
* @param severity The severity of the trace.
* @param component The component that generated the trace.
* @param event The event being traced.
* @param message The message to trace.
*/
public void log(final SeverityType severity, final ComponentType component,
final EventType event, final String message, final Object... args)
{
try
{
Trace trace = getTrace(severity, component, event, message, args);
LOGGER.info(trace.toString());
processHierarchy(trace);
publishTrace(trace);
}
catch (IllegalStateException ex)
{
// Just ignore this error for the moment; it appears if the method is invoked outside
// the servlet container and the TracerFilter has not been invoked. E.g. In unit tests
}
}
/**
* Log a system message to the event system.
* <p>
* This method should only be used to log system tasks such as infrastructure check, etc.
* Actions performed by a user must be logged using the
* {@link #log(SeverityType, ComponentType, EventType, String)} method.
*
* @param severity The severity of the trace.
* @param component The component that generated the trace.
* @param event The event being traced.
* @param message The message to trace.
*/
public void systemLog(final SeverityType severity, final ComponentType component,
final EventType event, final String message, final Object... args)
{
Trace trace = getSystemTrace(severity, component, event, message, args);
LOGGER.info(trace.toString());
publishTrace(trace);
}
/**
* Log a message to the event system.
* <p>
* Sends a normal trace or a system trace depending on the user logged. If there is no logged
* user, it will send a system trace.
* <p>
* This method should be only used in methods that can be invoked either by user actions and by
* system processes (such as one time auth protected resources).
*
* @param severity The severity of the trace.
* @param component The component that generated the trace.
* @param event The event being traced.
* @param message The message to trace.
*/
public void logFromContext(final SeverityType severity, final ComponentType component,
final EventType event, final String message, final Object... args)
{
TracerContext tracerContext = TracerContextHolder.getContext();
if (tracerContext.getUserId() != null)
{
log(severity, component, event, message, args);
}
else
{
systemLog(severity, component, event, message, args);
}
}
/**
* Log a system error message to the event system.
* <p>
* This method should only be used to log system tasks such as infrastructure check, etc.
* Actions performed by a user must be logged using the
* {@link #log(SeverityType, ComponentType, EventType, String)} method.
*
* @param severity The severity of the trace.
* @param component The component that generated the trace.
* @param event The event being traced.
* @param message The message to trace.
* @param ex The error.
*/
public void systemError(final SeverityType severity, final ComponentType component,
final EventType event, final Throwable error, final String message, final Object... args)
{
Trace trace = getSystemTrace(severity, component, event, message, args);
LOGGER.error(trace.toString(), error);
publishTrace(trace);
}
/**
* Process the hierarchy to extract resource data.
*
* @param trace The trace with the hierarchy to process.
*/
private void processHierarchy(final Trace trace)
{
Map<String, String> hierarchyData = new HashMap<String, String>();
hierarchyProcessor.process(trace.getHierarchy(), hierarchyData);
trace.setHierarchyData(hierarchyData);
}
/**
* Publish the trace to the tracing broker.
*
* @param trace The trace to publish.
*/
private synchronized void publishTrace(final Trace trace)
{
try
{
producer.openChannel();
producer.publish(trace);
producer.closeChannel();
}
catch (IOException e)
{
LOGGER.error("Could not publish the trace.", e);
}
}
private Trace getTrace(final SeverityType severity, final ComponentType component,
final EventType event, final String message, final Object... args)
{
TracerContext tracerContext = TracerContextHolder.getContext();
Trace trace = new Trace();
trace.setSeverity(severity.name());
trace.setComponent(component.name());
trace.setEvent(event.name());
trace.setHierarchy(tracerContext.getHierarchy());
trace.setEnterpriseId(tracerContext.getEnterpriseId());
trace.setEnterpriseName(tracerContext.getEnterpriseName());
trace.setUserId(tracerContext.getUserId());
trace.setUsername(tracerContext.getUsername());
trace.setMessage(getMessage(message, args));
return trace;
}
private Trace getSystemTrace(final SeverityType severity, final ComponentType component,
final EventType event, final String message, final Object... args)
{
Trace trace = new Trace();
trace.setSeverity(severity.name());
trace.setComponent(component.name());
trace.setEvent(event.name());
trace.setEnterpriseName(Enterprise.SYSTEM_ENTERPRISE.getName());
trace.setUsername(User.SYSTEM_USER.getName());
trace.setMessage(getMessage(message, args));
return trace;
}
private String getMessage(final String code, final Object... args)
{
if (messages == null)
{
// Maybe null in unit tests that manually instantiate this class.
// Just return the code to avoid making tests fail.
return code;
}
try
{
return messages.getMessage(code, args, Locale.getDefault());
}
catch (NoSuchMessageException ex)
{
// TODO If no message was found, return the code as the message
// When all tracer messages are migrated to the resource bundle file
// this exception should be propagated instead of returning a valid value
return code;
}
}
}