Java tutorial
/*************************************************************************************************** * Educational Online Test Delivery System * Copyright (c) 2017 Regents of the University of California * * Distributed under the AIR Open Source License, Version 1.0 * See accompanying file AIR-License-1_0.txt or at * http://www.smarterapp.org/documents/American_Institutes_for_Research_Open_Source_Software_License.pdf * * SmarterApp Open Source Assessment Software Project: http://smarterapp.org * Developed by Fairway Technologies, Inc. (http://fairwaytech.com) * for the Smarter Balanced Assessment Consortium (http://smarterbalanced.org) **************************************************************************************************/ package org.opentestsystem.delivery.logging; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; import org.slf4j.MarkerFactory; import java.util.HashMap; import java.util.Map; import java.util.UUID; import static java.lang.String.format; import static java.util.Collections.unmodifiableMap; /** * Centralized event logger. */ public abstract class EventLoggerBase implements EventLogger { private static final Logger logger = LoggerFactory.getLogger(EventLogger.class); private static final Marker marker = MarkerFactory.getMarker("event-logger"); private final ObjectWriter writer; private final Map<String, Long> eventTimes = new HashMap<>(); private final Map<String, Object> extraFields = new HashMap<>(); public EventLoggerBase(final ObjectMapper objectMapper) { this.writer = objectMapper.writer(); extraFields.put(BaseEventData.HTTP_REQUEST_UUID.name(), UUID.randomUUID()); } @Override public void putField(final String key, final Object value) { extraFields.put(key, value); } @Override public Object getField(final String key) { return extraFields.get(key); } /** * Log event occurrences to centralized logging. * <p> * These events are intended to be sent to a logstash server and searchable using elastic search. * * @param app name of the app writing the log * @param logEvent name of the event occurring * @param checkpoint an optional checkpoint name * @param message an optional free-format message string to add to the log entry * @param data optional searchable event data to add to the recorded event */ public void trace(final String app, final String logEvent, final String checkpoint, final String message, final Map<EventData, Object> data) { log(formatMessage(logEvent, message), getFieldMap(app, logEvent, checkpoint, data)); } /** * Concrete classes are required to supply the application name they are logging from. * @return name of the application that is logging */ protected abstract String getApp(); public void trace(final EventInfo eventInfo) { trace(getApp(), eventInfo.event(), eventInfo.checkpoint().or(""), eventInfo.message().or(""), eventInfo.data()); } public void error(final EventInfo eventInfo, final Exception e) { error(getApp(), eventInfo.event(), eventInfo.checkpoint().or(""), eventInfo.message().or(""), eventInfo.data(), e); } /** * Log event errors to centralized logging. * <p> * These events are intended to be sent to a logstash server and searchable using elastic search. * * @param app name of the app writing the log * @param logEvent name of the event occurring * @param checkpoint an optional checkpoint name * @param message an optional free-format message string to add to the log entry * @param data optional searchable event data to add to the recorded event */ public void error(final String app, final String logEvent, final String checkpoint, String message, final Map<EventData, Object> data, final Exception e) { error(formatMessage(logEvent, message), getFieldMap(app, logEvent, checkpoint, data), e); } private void log(final String event, final Map<String, Object> fields) { try { logger.trace(marker, format("EVENT:%s JSON:({\"event_data\": %s})", event, writer.writeValueAsString(fields))); } catch (Exception e) { logger.warn(marker, format("exception occurred while logging event: %s", event), e); } } private void error(final String event, final Map<String, Object> fields, final Exception e) { try { logger.error(marker, format("EVENT:%s JSON:({\"event_data\": %s})", event, writer.writeValueAsString(fields)), e); } catch (Exception inner) { logger.error(marker, format("exception occurred while error event: %s", event), inner); } } private void addTimerMetrics(final String event, final String checkpoint, Map<EventData, Object> fields) { Long eventTime = eventTimes.get(event); if (Checkpoint.ENTER.name().equals(checkpoint)) { // entry point - (re)set eventTime and emit nothing eventTimes.put(event, System.currentTimeMillis()); } else { if (Checkpoint.EXIT.name().equals(checkpoint)) { // exit point - remove timestamp. falls through to default to emit metric. eventTimes.remove(event); } // non entry/exit or missing position marker - emit elapsed metric if (null != eventTime) { fields.put(BaseEventData.ELAPSED_TIME, System.currentTimeMillis() - eventTime); } } } private Map<String, Object> getFieldMap(final String app, final String event, String checkpoint, final Map<EventData, Object> data) { Map<EventData, Object> fields = new HashMap<>(); if (StringUtils.isNotBlank(app)) { fields.put(BaseEventData.APP, app); } if (StringUtils.isNotBlank(checkpoint)) { fields.put(BaseEventData.CHECKPOINT, checkpoint.toLowerCase()); } if (null != data) { fields.putAll(data); } addTimerMetrics(event, checkpoint, fields); // Write all data into outputMap, lowercasing key names. Map<String, Object> outputMap = new HashMap<>(); for (Map.Entry<EventData, Object> entry : fields.entrySet()) { outputMap.put(entry.getKey().name().toLowerCase(), entry.getValue()); } for (Map.Entry<String, Object> entry : extraFields.entrySet()) { outputMap.put(entry.getKey().toLowerCase(), entry.getValue()); } return unmodifiableMap(outputMap); } private static String formatMessage(final String logEvent, final String message) { String result = logEvent.toLowerCase(); if (StringUtils.isNotBlank(message)) { result = result + " - " + message; } return result; } }