/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.systemservices.impl.logsvc.marshaller; import java.io.IOException; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import org.json.simple.JSONObject; import com.emc.storageos.systemservices.impl.logsvc.LogMessage; public class JSONMarshaller extends Marshaller { private boolean firstLog = true; private static final byte COMMA = (byte) ','; private static final byte COLON = (byte) ':'; private static final byte TAB = (byte) '\t'; private static final byte LEFT_CURLY = (byte) '{'; private static final byte RIGHT_CURLY = (byte) '}'; private static final byte QUOTE = (byte) '"'; private static final byte[] CLASS = "class".getBytes(); private static final byte[] LINE = "line".getBytes(); private static final byte[] MESSAGE = "message".getBytes(); private static final byte[] NODE_ID = "node".getBytes(); private static final byte[] NODE_NAME = "node_name".getBytes(); private static final byte[] SERVICE = "service".getBytes(); private static final byte[] SEVERITY = "severity".getBytes(); private static final byte[] THREAD = "thread".getBytes(); private static final byte[] TIME = "time".getBytes(); private static final byte[] TIME_MS = "time_ms".getBytes(); private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); JSONMarshaller(OutputStream outputStream) { super(outputStream); } @Override public void head() throws IOException { outputStream.write(LEFT_BRACKET); } @Override public void tail() throws IOException { outputStream.write(RETURN); outputStream.write(RIGHT_BRACKET); outputStream.write(RETURN); } @Override public void marshall(LogMessage log) throws IOException { if (firstLog) { firstLog = false; } else { outputStream.write(COMMA); } final byte[] firstLine = log.getFirstLine(); outputStream.write(RETURN); outputStream.write(TAB); outputStream.write(LEFT_CURLY); writeEntry(CLASS, firstLine, log.getFileNameOffset(), log.getFileNameLen(), true); outputStream.write(COMMA); writeEntry(LINE, firstLine, log.getLineNumberOffset(), log.getLineNumberLen(), false); outputStream.write(COMMA); // write the log message outputStream.write(RETURN); outputStream.write(TAB); outputStream.write(TAB); outputStream.write(QUOTE); outputStream.write(MESSAGE); outputStream.write(QUOTE); outputStream.write(COLON); outputStream.write(SPACE); outputStream.write(QUOTE); escapeJSONChars(log.getLogContent()); outputStream.write(QUOTE); outputStream.write(COMMA); writeEntry(NODE_ID, log.getNodeId(), true); outputStream.write(COMMA); writeEntry(NODE_NAME, log.getNodeName(), true); outputStream.write(COMMA); writeEntry(SERVICE, log.getService(), true); outputStream.write(COMMA); writeEntry(SEVERITY, log.getLevel(), true); outputStream.write(COMMA); writeEntry(THREAD, firstLine, log.getThreadNameOffset(), log.getThreadNameLen(), true); outputStream.write(COMMA); if (log.getTimeBytesOffset() == -1 || log.getTimeBytesLen() == 0) { writeFakeTimeStr(log); } else { writeEntry(TIME, firstLine, log.getTimeBytesOffset(), log.getTimeBytesLen(), true); } outputStream.write(COMMA); writeEntry(TIME_MS, String.valueOf(log.getTime()).getBytes(), false); outputStream.write(RETURN); outputStream.write(TAB); outputStream.write(RIGHT_CURLY); } @Override public void marshall(String msg, LogMessage prevMsg) throws IOException { if (firstLog) { firstLog = false; } else { outputStream.write(COMMA); } outputStream.write(RETURN); outputStream.write(TAB); outputStream.write(LEFT_CURLY); writeEntry(MESSAGE, JSONObject.escape(msg).getBytes(), true); outputStream.write(COMMA); writeEntry(SERVICE, "internal".getBytes(), true); outputStream.write(COMMA); writeEntry(SEVERITY, "ERROR".getBytes(), true); outputStream.write(COMMA); writeEntry(TIME, prevMsg == null ? "null".getBytes() : prevMsg.getTimeBytes(), true); outputStream.write(COMMA); writeEntry(TIME_MS, prevMsg == null ? "null".getBytes() : String.valueOf(prevMsg.getTime()).getBytes(), false); outputStream.write(RETURN); outputStream.write(TAB); outputStream.write(RIGHT_CURLY); } private void writeEntry(byte[] key, byte[] value, boolean isStr) throws IOException { outputStream.write(RETURN); outputStream.write(TAB); outputStream.write(TAB); outputStream.write(QUOTE); outputStream.write(key); outputStream.write(QUOTE); outputStream.write(COLON); outputStream.write(SPACE); if (isStr) { outputStream.write(QUOTE); } outputStream.write(value); if (isStr) { outputStream.write(QUOTE); } } private void writeFakeTimeStr(LogMessage log) throws IOException { outputStream.write(RETURN); outputStream.write(TAB); outputStream.write(TAB); outputStream.write(QUOTE); outputStream.write(TIME); outputStream.write(QUOTE); outputStream.write(COLON); outputStream.write(SPACE); outputStream.write(QUOTE); outputStream.write(sdf.format(new Date(log.getTime())).getBytes()); outputStream.write(QUOTE); } private void writeEntry(byte[] key, byte[] firstLine, int valueOffset, int valueLength, boolean isStr) throws IOException { outputStream.write(RETURN); outputStream.write(TAB); outputStream.write(TAB); outputStream.write(QUOTE); outputStream.write(key); outputStream.write(QUOTE); outputStream.write(COLON); outputStream.write(SPACE); if (isStr) { outputStream.write(QUOTE); } if (valueOffset == -1 || valueLength == 0) { outputStream.write(isStr ? "null".getBytes() : "-1".getBytes()); } else { outputStream.write(firstLine, valueOffset, valueLength); } if (isStr) { outputStream.write(QUOTE); } } /** * Escape the ", / and \ char from JSON string * * @param msg * @throws IOException */ private void escapeJSONChars(byte[] msg) throws IOException { int mark = 0; for (int i = 0; i < msg.length; i++) { if (msg[i] == (byte) '"' || msg[i] == (byte) '\\' || msg[i] == (byte) '/') { outputStream.write(msg, mark, i - mark); outputStream.write((char) '\\'); mark = i; } else if (msg[i] == (byte) '\r') { outputStream.write(msg, mark, i - mark); outputStream.write((char) '\\'); outputStream.write((char) 'r'); mark = i + 1; } else if (msg[i] == (byte) '\n') { outputStream.write(msg, mark, i - mark); outputStream.write((char) '\\'); outputStream.write((char) 'n'); mark = i + 1; } else if (msg[i] == (byte) '\b') { outputStream.write(msg, mark, i - mark); outputStream.write((char) '\\'); outputStream.write((char) 'b'); mark = i + 1; } else if (msg[i] == (byte) '\f') { outputStream.write(msg, mark, i - mark); outputStream.write((char) '\\'); outputStream.write((char) 'f'); mark = i + 1; } else if (msg[i] == (byte) '\t') { outputStream.write(msg, mark, i - mark); outputStream.write((char) '\\'); outputStream.write((char) 't'); mark = i + 1; } } if (mark == msg.length) { return; } outputStream.write(msg, mark, msg.length - mark); } }