// // Copyright 2010 Cinch Logic Pty Ltd. // // http://www.chililog.com // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package org.chililog.server.engine.parsers; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Hashtable; import java.util.regex.Pattern; import org.apache.commons.lang.NotImplementedException; import org.apache.commons.lang.StringUtils; import org.chililog.server.common.ChiliLogException; import org.chililog.server.common.Log4JLogger; import org.chililog.server.data.MongoJsonParser; import org.chililog.server.data.RepositoryEntryBO; import org.chililog.server.data.RepositoryConfigBO; import org.chililog.server.data.RepositoryParserConfigBO; import org.chililog.server.data.RepositoryEntryBO.Severity; import org.chililog.server.engine.Strings; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; /** * <p> * Parser to extract field values from JSON log entries. For example, * </p> * <code> * { "field1": 1, "field2": "abc", "field3": 123 } * </code> * <p> * Field definitions are not required because it is defined in the JSON format. * </p> * * @author vibul * */ public class JsonEntryParser extends EntryParser { private static Log4JLogger _logger = Log4JLogger.getLogger(JsonEntryParser.class); private Pattern _datePattern = null; private String _dateFormat = null; private Pattern _longNumberPattern = null; /** * <p> * Regular expression pattern to use in order to identify a date within a string. For example, if * "^([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z)$", then "2000-01-01T01:01:01Z" will be treated as a * Date. * </p> * <p> * If not set, date parsing will not be performed. * </p> */ public static final String DATE_PATTERN_PROPERTY_NAME = "date_pattern"; /** * <p> * Date format to use with {@link SimpleDateFormat} when parsing for a date format. For example, * "yyyy-MM-dd'T'HH:mm:ssZ". Parsing is only performed on a {@link String} if it passes the string length and * pattern checks. * </p> * <p> * If not set, date parsing will not be performed. * </p> */ public static final String DATE_FORMAT_PROPERTY_NAME = "date_format"; /** * <p> * Regular expression pattern to use in order to identify a long within a string. For example, if "^([0-9]+L)$", * then "88888L" will be treated as a Long. * </p> * <p> * If not set, long number parsing on a string will not be performed. * </p> */ public static final String LONG_NUMBER_PATTERN_PROPERTY_NAME = "long_suffix"; /** * <p> * Basic constructor * </p> * * @param repoInfo * Repository meta data * @param repoParserInfo * Parser information that we need * @throws ChiliLogException */ public JsonEntryParser(RepositoryConfigBO repoInfo, RepositoryParserConfigBO repoParserInfo) throws ChiliLogException { super(repoInfo, repoParserInfo); try { Hashtable<String, String> properties = repoParserInfo.getProperties(); String s = properties.get(DATE_PATTERN_PROPERTY_NAME); if (!StringUtils.isBlank(s)) { _datePattern = Pattern.compile(s); } _dateFormat = properties.get(DATE_FORMAT_PROPERTY_NAME); s = properties.get(LONG_NUMBER_PATTERN_PROPERTY_NAME); if (!StringUtils.isBlank(s)) { _longNumberPattern = Pattern.compile(s); } } catch (Exception ex) { if (ex instanceof ChiliLogException) { throw (ChiliLogException) ex; } else { throw new ChiliLogException(Strings.PARSER_INITIALIZATION_ERROR, repoParserInfo.getName(), repoInfo.getName(), ex.getMessage()); } } return; } /** * Parse a string for fields. All exceptions are caught and logged. If <code>null</code> is returned, this indicates * that the entry should be skipped. * * @param timestamp * Time when this log entry was created at the source on the host. * @param source * Name of the input device or application that created this text entry * @param host * IP address of the input device or application that created this text entry * @param severity * Classifies the importance of the entry. Can be the severity code (0-7) or text. * @param preparsedFields * Pre-parsed fields in JSON format. * @param message * The text for this entry to parse * @return <code>RepositoryEntryBO</code> ready for saving to mongoDB. If the entry cannot be parsed, then null is * returned */ @Override public RepositoryEntryBO parse(String timestamp, String source, String host, String severity, String preparsedFields, String message) { try { this.setLastParseError(null); checkParseArguments(timestamp, source, host, severity, message); MongoJsonParser parser = new MongoJsonParser(message, _datePattern, _dateFormat, _longNumberPattern); DBObject fieldsDBObject = new BasicDBObject(); try { fieldsDBObject = (DBObject) parser.parse(); } catch (Exception ex) { switch (this.getRepoParserInfo().getParseFieldErrorHandling()) { case SkipField: case SkipEntry: throw new ChiliLogException(ex, Strings.PARSER_JSON_ERROR_SKIP_ENTRY, this.getRepoName(), ex.getMessage(), message); case SkipFieldIgnoreError: break;// Do nothing default: throw new NotImplementedException("ParseFieldErrorHandling type " + this.getRepoParserInfo().getParseFieldErrorHandling().toString()); } } Severity sev = Severity.parse(severity); ArrayList<String> keywords = parseKeywords(source, host, sev, message); return new RepositoryEntryBO(parseTimestamp(timestamp), source, host, sev, keywords, message, fieldsDBObject); } catch (Exception ex) { this.setLastParseError(ex); _logger.error(ex, "Error parsing JSON entry: " + message); return null; } } }