All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gravitee.reporter.file.FileReporter Maven / Gradle / Ivy

There is a newer version: 3.2.2
Show newest version
/**
 * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
 *
 * 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 io.gravitee.reporter.file;

import io.gravitee.common.service.AbstractService;
import io.gravitee.reporter.api.Reportable;
import io.gravitee.reporter.api.Reporter;
import io.gravitee.reporter.api.http.Metrics;
import io.gravitee.reporter.file.config.Config;
import org.apache.commons.lang3.time.FastDateFormat;
import org.eclipse.jetty.util.RolloverFileOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.TimeZone;

/**
 * Write an access log to a file by using the following line format:
 *
 * 
 *     [TIMESTAMP] (LOCAL_IP) REMOTE_IP API KEY METHOD PATH STATUS LENGTH TOTAL_RESPONSE_TIME
 * 
* * This class is not thread safe, the record method should only be called by a single thread * * @author David BRASSELY (brasseld at gmail.com) */ @SuppressWarnings("rawtypes") public class FileReporter extends AbstractService implements Reporter { @Autowired private Config config; private static final Logger LOGGER = LoggerFactory.getLogger(FileReporter.class); private static final String RFC_3339_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; private static final FastDateFormat dateFormatter = FastDateFormat.getInstance(RFC_3339_DATE_FORMAT); private static final String NO_STRING_DATA_VALUE = "-"; private static final String NO_INTEGER_DATA_VALUE = "-1"; // buffer reused between calls to the report method private final StringBuilder accessLogBuffer = new StringBuilder(256); private final StringBuffer dateFormatBuffer = new StringBuffer(); private final char[] stringBuilderConverterBuffer = new char[2048]; private transient OutputStream _out; private transient Writer _writer; private void write(StringBuilder accessLog) throws IOException { synchronized (this) { if (_writer == null) { return; } /* * OMG What's going on ? * Why aren't you doing a _writer.write(accessLog.toString()) or _writer.write(accessLog) ? * Because it's doing too many memory allocations ! * accessLog.toString() will create a copy of the the StringBuilder content into a String * then _writer.write will make another copy of the content of the String * Here we're doing only one copy AND we reuse the buffer. */ int length = accessLog.length(); int chunkLength = stringBuilderConverterBuffer.length; for (int srcBegin = 0; srcBegin < length; srcBegin += chunkLength) { final int srcEnd = Math.min(srcBegin + chunkLength, length); accessLog.getChars(srcBegin, srcEnd, stringBuilderConverterBuffer, 0); _writer.write(stringBuilderConverterBuffer, 0, srcEnd - srcBegin); } _writer.flush(); } } private StringBuilder format(Metrics metrics) { StringBuilder buf = accessLogBuffer; StringBuffer dateBuffer = dateFormatBuffer; buf.setLength(0); dateBuffer.setLength(0); // Append request timestamp buf.append('['); dateFormatter.format(metrics.timestamp().toEpochMilli(), dateBuffer); buf.append(dateBuffer); buf.append("] "); // Append request id buf.append(metrics.getRequestId()); buf.append(" "); // Append transaction id buf.append(metrics.getTransactionId()); buf.append(" "); // Append local IP buf.append('('); buf.append(metrics.getLocalAddress()); buf.append(") "); // Append remote IP buf.append(metrics.getRemoteAddress()); buf.append(' '); // Append Api name String apiName = metrics.getApi(); if (apiName == null) { apiName = NO_STRING_DATA_VALUE; } buf.append(apiName); buf.append(' '); // Append key String apiKey = metrics.getApiKey(); if (apiKey == null) { apiKey = NO_STRING_DATA_VALUE; } buf.append(apiKey); buf.append(' '); // Append request method and URI buf.append(metrics.getHttpMethod()); buf.append(' '); buf.append(metrics.getPath()); buf.append(' '); // Append response status int status = metrics.getStatus(); if (status <= 0) { status = 404; } buf.append((char) ('0' + ((status / 100) % 10))); buf.append((char) ('0' + ((status / 10) % 10))); buf.append((char) ('0' + (status % 10))); buf.append(' '); // Append response length long responseLength = metrics.getResponseContentLength(); if (responseLength >= 0) { if (responseLength > 99999) { buf.append(responseLength); } else { if (responseLength > 9999) buf.append((char) ('0' + ((responseLength / 10000) % 10))); if (responseLength > 999) buf.append((char) ('0' + ((responseLength / 1000) % 10))); if (responseLength > 99) buf.append((char) ('0' + ((responseLength / 100) % 10))); if (responseLength > 9) buf.append((char) ('0' + ((responseLength / 10) % 10))); buf.append((char) ('0' + (responseLength) % 10)); } } else { buf.append(NO_INTEGER_DATA_VALUE); } buf.append(' '); // Append total response time buf.append(metrics.getProxyResponseTimeMs()); buf.append(' '); // Append proxy latency buf.append(metrics.getProxyLatencyMs()); buf.append(System.lineSeparator()); return buf; } @Override public synchronized void doStart() throws Exception { String filename = config.getFilename(); if (filename != null) { _out = new RolloverFileOutputStream( filename, config.isAppend(), config.getRetainDays(), TimeZone.getDefault(), config.getDateFormat(), config.getBackupFormat() ); LOGGER.info("Opened rollover access log file " + filename); } synchronized (this) { _writer = new OutputStreamWriter(_out); } } @Override public synchronized void doStop() throws Exception { synchronized (this) { try { if (_writer != null) _writer.flush(); } catch (IOException ioe) { LOGGER.error("", ioe); } if (_out != null) try { _out.close(); } catch (IOException ioe) { LOGGER.error("", ioe); } _out = null; _writer = null; } } @Override public void report(Reportable reportable) { try { write(format((Metrics) reportable)); } catch (IOException ioe) { LOGGER.error("", ioe); } } @Override public boolean canHandle(Reportable reportable) { return (reportable instanceof Metrics); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy