org.red5.logging.W3CAppender Maven / Gradle / Ivy
/*
* RED5 Open Source Media Server - https://github.com/Red5/
*
* Copyright 2006-2016 by respective authors (see below). All rights reserved.
*
* 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.red5.logging;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.red5.server.api.Red5;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.status.ErrorStatus;
/**
* Logback appender for the Extended W3C format.
*
* @see "http://www.w3.org/TR/WD-logfile.html"
* @author Paul Gregoire ([email protected])
*/
public class W3CAppender extends FileAppender {
/**
* #Software: Red5 0.7.1 #Version: 1.0 #Date: 1998-11-19 22:48:39 #Fields: date time c-ip cs-username s-ip cs-method
*/
//whether or not the header has been written
private static boolean headerWritten;
//events that are to be logged
private static String events;
//linked list to preserve order
private static List eventsList = new ArrayList<>();
//fields that are to be logged
private static String fields;
//linked list to preserve order
private static LinkedList fieldList = new LinkedList<>();
public W3CAppender() {
setPrudent(true);
}
public void setEvents(String events) {
W3CAppender.events = events;
//make a list out of the event names
String[] arr = events.split(";");
for (String s : arr) {
eventsList.add(s);
}
}
public String getEvents() {
return events;
}
public void setFields(String fields) {
W3CAppender.fields = fields;
//make a list out of the field names
String[] arr = fields.split(";");
for (String s : arr) {
fieldList.add(s);
}
}
public String getFields() {
return fields;
}
@Override
public synchronized void doAppend(LoggingEvent event) {
//get the log message
String message = event.getFormattedMessage();
//look for w3c prefix
if (!message.startsWith("W3C")) {
return;
}
// http://logback.qos.ch/apidocs/ch/qos/logback/classic/spi/LoggingEvent.html
StringBuilder sbuf = new StringBuilder(128);
//see if header has been written
if (!headerWritten) {
//build the header
StringBuilder sb = new StringBuilder("#Software: ");
sb.append(Red5.VERSION);
sb.append("\n#Version: 1.0");
sb.append("\n#Date: ");
sb.append(new Date());
sb.append("\n#Fields: ");
for (String field : fields.split(";")) {
sb.append(field);
sb.append(' ');
}
sb.append('\n');
sbuf.append(sb.toString());
headerWritten = true;
sb = null;
}
//break the message into pieces
String[] arr = message.split(" ");
//create a map
Map elements = new HashMap<>(arr.length);
int i = 0;
for (String s : arr) {
if ((i = s.indexOf(':')) != -1) {
String key = s.substring(0, i);
String value = s.substring(i + 1);
elements.put(key, value);
}
}
//Events Categories
//connect-pending session
//connect session
//disconnect session
//publish stream
//unpublish stream
//play stream
//pause stream
//unpause stream
//seek stream
//stop stream
//record stream
//recordstop stream
//server-start server
//server-stop server
//vhost-start vhost
//vhost-stop vhost
//app-start application
//app-stop application
//filter based on event type - asterik allows all events
if (!events.equals("*")) {
if (!eventsList.contains(elements.get("x-event"))) {
elements.clear();
elements = null;
sbuf = null;
return;
}
}
//x-category event category
//x-event type of event
//date date at which the event occurred
//time time at which the event occurred
//tz time zone information
//x-ctx event dependant context information
//s-ip ip address[es] of the server
//x-pid server process id
//x-cpu-load cpu load
//x-mem-load memory load (as reported in getServerStats)
//x-adaptor adaptor name
//x-vhost vhost name
//x-app application name
//x-appinst application instance name
//x-duration duration of an event/session
//x-status status code
//c-ip client ip address
//c-proto connection protocol - rtmp or rtmpt
//s-uri uri of the fms application
//cs-uri-stem stem of s-uri
//cs-uri-query query portion of s-uri
//c-referrer uri of the referrer
//c-user-agent user agent
//c-client-id client id
//cs-bytes bytes transferred from client to server
//sc-bytes bytes transferred from server to client
//c-connect-type type of connection received by the server
//x-sname stream name
//x-sname-query query portion of stream uri
//x-suri-query same as x-sname-query
//x-suri-stem cs-uri-stem + x-sname + x-file-ext
//x-suri x-suri-stem + x-suri-query
//x-file-name full file path of recorded stream
//x-file-ext stream type (flv or mp3)
//x-file-size stream size in bytes
//x-file-length stream length in seconds
//x-spos stream position
//cs-stream-bytes stream bytes transferred from client to server
//sc-stream-bytes stream bytes transferred from server to client
//x-service-name name of the service providing the connection
//x-sc-qos-bytes bytes transferred from server to client for quality of service
//x-comment comments
//we may need date and/or time
Calendar cal = GregorianCalendar.getInstance();
cal.clear();
cal.setTimeInMillis(event.getTimeStamp());
//loop through the field names and grab the values from the map
//fields without a value get a tab character as a place holder if
//their value is not available to the server
for (String field : fieldList) {
String value = elements.get(field);
if (value == null) {
if ("date".equals(field)) {
sbuf.append(cal.get(Calendar.MONTH) + 1);
sbuf.append('/');
sbuf.append(cal.get(Calendar.DAY_OF_MONTH));
sbuf.append('/');
sbuf.append(cal.get(Calendar.YEAR));
} else if ("time".equals(field)) {
sbuf.append(cal.get(Calendar.HOUR_OF_DAY));
sbuf.append(':');
int min = cal.get(Calendar.MINUTE);
if (min < 10) {
sbuf.append('0');
sbuf.append(min);
} else {
sbuf.append(min);
}
} else if ("s-ip".equals(field)) {
//where should we grab the server ip from?
sbuf.append("127.0.0.1");
} else if ("x-pid".equals(field)) {
//should we pass thread name?
sbuf.append(event.getThreadName());
} else {
sbuf.append('\t');
}
} else {
sbuf.append(value);
}
//space padded
sbuf.append(' ');
}
sbuf.append('\n');
try {
//switch out the message
event.setMessage(sbuf.toString());
//write it
writeOut(event);
} catch (IOException ioe) {
addStatus(new ErrorStatus("IO failure in appender", this, ioe));
}
}
}