Java tutorial
/* * Copyright (C) 2015-2018, JKOOL LLC. * * This library 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; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package com.jkoolcloud.tnt4j.streams.parsers; import static com.jkoolcloud.tnt4j.streams.fields.StreamFieldType.*; import static com.jkoolcloud.tnt4j.streams.utils.SyslogStreamConstants.*; import java.lang.Exception; import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.graylog2.syslog4j.impl.message.structured.StructuredSyslogMessage; import org.graylog2.syslog4j.server.SyslogServerEventIF; import org.graylog2.syslog4j.server.impl.event.SyslogServerEvent; import org.graylog2.syslog4j.server.impl.event.structured.StructuredSyslogServerEvent; import com.jkoolcloud.tnt4j.core.OpLevel; import com.jkoolcloud.tnt4j.sink.DefaultEventSinkFactory; import com.jkoolcloud.tnt4j.sink.EventSink; import com.jkoolcloud.tnt4j.streams.utils.SyslogUtils; import com.jkoolcloud.tnt4j.streams.utils.Utils; /** * Implements an activity data parser that assumes each activity data item is an Syslog server event * {@link SyslogServerEventIF}. Parser resolved event data fields are put into {@link Map} and afterwards mapped into * activity fields and properties according to defined parser configuration. * <p> * Map entries containing values as internal {@link Map}s are automatically mapped into activity properties. If only * particular inner map entries are needed, then in parser fields mapping configuration define those properties as * separate fields. * <p> * This parser resolved data map may contain such entries: * <ul> * <li>for activity fields:</li> * <ul> * <li>EventType - resolved from log event application message contained variable named * {@value com.jkoolcloud.tnt4j.logger.AppenderConstants#PARAM_OP_TYPE_LABEL}</li> * <li>EventName - resolved log event facility name, or application message contained variable named * {@value com.jkoolcloud.tnt4j.logger.AppenderConstants#PARAM_OP_NAME_LABEL}</li> * <li>Exception - resolved from log event application message contained variable named * {@value com.jkoolcloud.tnt4j.logger.AppenderConstants#PARAM_EXCEPTION_LABEL}</li> * <li>UserName - resolved from log event application message contained variable named * {@value com.jkoolcloud.tnt4j.logger.AppenderConstants#PARAM_USER_LABEL}</li> * <li>ResourceName - resolved log event application name, or application message contained variable named * {@value com.jkoolcloud.tnt4j.logger.AppenderConstants#PARAM_RESOURCE_LABEL}</li> * <li>Location - resolved log event host name/address, or application message contained variable named * {@value com.jkoolcloud.tnt4j.logger.AppenderConstants#PARAM_LOCATION_LABEL}</li> * <li>Tag - resolved set of values {host name, application name} for RFC 3164 and set of values {facility name, host * name, application name, message id} for RFC 5424, or application message contained variable named * {@value com.jkoolcloud.tnt4j.logger.AppenderConstants#PARAM_TAG_LABEL}</li> * <li>Correlator - resolved from log event application message contained variable named * {@value com.jkoolcloud.tnt4j.logger.AppenderConstants#PARAM_CORRELATOR_LABEL}</li> * <li>ProcessId - resolved log event process id</li> * <li>ThreadId - same as 'ProcessId'</li> * <li>Message - resolved log event application message</li> * <li>Severity - resolved log event level mapped to value from {@link OpLevel} enumeration</li> * <li>ApplName - resolved log event application name</li> * <li>ServerName - resolved log event host name</li> * <li>EndTime - resolved log event timestamp value in microseconds</li> * <li>ElapsedTime - calculated time difference between same host and app events in microseconds</li> * <li>MsgCharSet - resolved log event char set name</li> * </ul> * <li>for activity properties:</li> * <ul> * <li>facility - resolved log event facility name</li> * <li>level - resolved log event level</li> * <li>hostname - resolved log event host name</li> * <li>hostaddr - resolved log event host address</li> * <li>priority - resolved log event priority</li> * </ul> * <li>maps of resolved additional custom activity properties:</li> * <ul> * <li>SyslogMap - map of resolved RFC 5424 structured data: contains sub-map for every found structure, but can be * flattened to single level map (if only one structure is available) using parser property * {@code "FlattenStructuredData"}</li> * <li>SyslogVars - map of resolved application message contained (varName=varValue) variables</li> * </ul> * </ul> * <p> * This activity parser supports configuration properties from {@link AbstractSyslogParser} (and higher hierarchy * parsers). * * @version $Revision: 1 $ */ public class ActivitySyslogEventParser extends AbstractSyslogParser { private static final EventSink LOGGER = DefaultEventSinkFactory .defaultEventSink(ActivitySyslogEventParser.class); private static final String ATTR_SERVER_NAME = "server.name"; // NON-NLS private static final String ATTR_APPL_PART = "appl.part"; // NON-NLS private static final String ATTR_APPL_NAME = "appl.name"; // NON-NLS private static final String ATTR_APPL_PID = "appl.pid"; // NON-NLS /** * Constructs a new ActivitySyslogEventParser. */ public ActivitySyslogEventParser() { super(); } @Override protected EventSink logger() { return LOGGER; } /** * Returns whether this parser supports the given format of the activity data. This is used by activity streams to * determine if the parser can parse the data in the format that the stream has it. * <p> * This parser supports the following class types (and all classes extending/implementing any of these): * <ul> * <li>{@link org.graylog2.syslog4j.server.SyslogServerEventIF}</li> * </ul> * * @param data * data object whose class is to be verified * @return {@code true} if this parser can process data in the specified format, {@code false} - otherwise */ @Override protected boolean isDataClassSupportedByParser(Object data) { return SyslogServerEventIF.class.isInstance(data); } @Override public void setProperty(String name, String value) { super.setProperty(name, value); // // no any additional properties are required yet. // if (false) { // logger().log(OpLevel.DEBUG, // StreamsResources.getString(StreamsResources.RESOURCE_BUNDLE_NAME, "ActivityParser.setting"), // name, value); // } } @Override public Object getProperty(String name) { return super.getProperty(name); } @Override protected Map<String, Object> getDataMap(Object data) { if (data == null) { return null; } SyslogServerEventIF event = (SyslogServerEventIF) data; Map<String, Object> dataMap = new HashMap<>(); dataMap.put(RAW_ACTIVITY_STRING_KEY, Utils.getString(event.getRaw())); Date date = (event.getDate() == null ? new Date() : event.getDate()); String facility = SyslogUtils.getFacilityString(event.getFacility()); OpLevel level = SyslogUtils.getOpLevel(event.getLevel()); int priority = (event.getFacility() << 3) + event.getLevel(); dataMap.put(EventName.name(), facility); dataMap.put(Severity.name(), level); dataMap.put(FIELD_FACILITY, facility); dataMap.put(FIELD_HOSTNAME, event.getHost()); dataMap.put(FIELD_LEVEL, event.getLevel()); dataMap.put(FIELD_PRIORITY, priority); InetSocketAddress from = null; if (event instanceof SyslogServerEvent) { try { Field addressField = event.getClass().getField("inetAddress"); // NON-NLS addressField.setAccessible(true); from = (InetSocketAddress) addressField.get(event); } catch (Exception exc) { } } if (from == null) { dataMap.put(Location.name(), event.getHost()); } else { dataMap.put(FIELD_HOSTADDR, from.getAddress().getHostAddress()); dataMap.put(Location.name(), from.getAddress().getHostAddress()); } dataMap.put(MsgCharSet.name(), event.getCharSet()); if (event instanceof StructuredSyslogServerEvent) { processRFC5424(facility, (StructuredSyslogServerEvent) event, dataMap); } else { processRFC3164(facility, event, dataMap); } String eventKey = String.format("%s/%s", dataMap.get(Location.name()), dataMap.get(ResourceName.name())); // NON-NLS long eventTime = date.getTime(); dataMap.put(EndTime.name(), eventTime * 1000); dataMap.put(ElapsedTime.name(), getUsecSinceLastEvent(eventKey, eventTime)); return suppress(dataMap); } /** * Returns type of RAW activity data entries. * * @return type of RAW activity data entries - Syslog event */ @Override protected String getActivityDataType() { return "SYSLOG EVENT"; // NON-NLS } /** * Process Syslog message based on RFC 5424. * * @param facility * Syslog facility name * @param event * Syslog message * @param dataMap * log entry fields map to update */ protected static void processRFC3164(String facility, SyslogServerEventIF event, Map<String, Object> dataMap) { Map<String, Object> map = parseAttributes(event); String serverName = map.get(ATTR_SERVER_NAME).toString(); String appName = map.get(ATTR_APPL_NAME).toString(); Long pid = (Long) map.get(ATTR_APPL_PID); dataMap.put(Tag.name(), new String[] { serverName, appName }); SyslogUtils.assignPid(String.valueOf(pid), dataMap); dataMap.put(ResourceName.name(), appName); dataMap.put(EventName.name(), facility); // set the appropriate source dataMap.put(ApplName.name(), appName); dataMap.put(ServerName.name(), serverName); dataMap.put(Message.name(), StringUtils.trim(event.getMessage())); } /** * Process Syslog message based on RFC 5424. * * @param facility * Syslog facility name * @param sEvent * Syslog structured message event * @param dataMap * log entry fields map to update */ protected static void processRFC5424(String facility, StructuredSyslogServerEvent sEvent, Map<String, Object> dataMap) { // RFC 5424 StructuredSyslogMessage sMessage = sEvent.getStructuredMessage(); String msgId = sMessage.getMessageId(); dataMap.put(EventName.name(), facility); dataMap.put(ResourceName.name(), sEvent.getApplicationName()); dataMap.put(Tag.name(), new String[] { facility, sEvent.getHost(), sEvent.getApplicationName(), msgId }); // if (StringUtils.isNotEmpty(msgId)) { // dataMap.put(TrackingId.name(), msgId); // } SyslogUtils.assignPid( StringUtils.isEmpty(sEvent.getProcessId()) ? sMessage.getProcId() : sEvent.getProcessId(), dataMap); // set the appropriate source dataMap.put(ApplName.name(), sEvent.getApplicationName()); dataMap.put(ServerName.name(), sEvent.getHost()); dataMap.put(Message.name(), StringUtils.trim(sMessage.getMessage())); // process structured event attributes into snapshot extractStructuredData(sMessage, dataMap); // extract name=value pairs if available SyslogUtils.extractVariables(sMessage.getMessage(), dataMap); } /** * Extract Syslog structured data if available (part of RFC 5424). * * @param sMessage * Syslog structured message * @param dataMap * log entry fields map to update * @return map of structured Syslog message data */ protected static Map<String, Map<String, String>> extractStructuredData(StructuredSyslogMessage sMessage, Map<String, Object> dataMap) { Map<String, Map<String, String>> map = sMessage.getStructuredData(); if (MapUtils.isNotEmpty(map)) { // PropertySnapshot snap = new PropertySnapshot(FIELD_SYSLOG_MAP, // sEvent.getApplicationName(), // (OpLevel) dataMap.get(Severity.name())); // snap.addAll(map); dataMap.put(FIELD_SYSLOG_MAP, map); } return map; } /** * Parse Syslog header attributes into a map. Message structure: <server> <appl-part>:<message> * * @param event * Syslog event * @return Syslog attributes such as host, application, pid */ private static Map<String, Object> parseAttributes(SyslogServerEventIF event) { HashMap<String, Object> map = new HashMap<>(); String message = event.getMessage(); if (message != null && message.indexOf(COLON) > 0) { String[] tokens = message.split("[: ]"); // NON-NLS map.put(ATTR_SERVER_NAME, tokens[0]); map.put(ATTR_APPL_PART, tokens[1]); try { int first = tokens[1].indexOf(OB); int last = tokens[1].indexOf(CB); String applName = first >= 0 ? tokens[1].substring(0, first) : tokens[1]; long pid = ((last >= first && (first >= 0)) ? Long.parseLong(tokens[1].substring(first + 1, last)) : 0); map.put(ATTR_APPL_NAME, applName); map.put(ATTR_APPL_PID, pid); } catch (Throwable ex) { map.put(ATTR_APPL_NAME, tokens[1]); map.put(ATTR_APPL_PID, 0L); } } else { map.put(ATTR_SERVER_NAME, event.getHost()); map.put(ATTR_APPL_NAME, UNKNOWN); map.put(ATTR_APPL_PID, 0L); } return map; } }